From ed56f041f8aaa464b9288ac4982c9d917a9fd908 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 5 Jan 2021 11:37:05 +0200 Subject: [PATCH] UI optimizations --- ui-ngx/angular.json | 26 +- ui-ngx/extra-webpack.config.js | 10 + .../event/event-content-dialog.component.ts | 52 ++-- .../alarm/alarm-schedule.component.html | 2 +- .../profile/alarm/alarm-schedule.component.ts | 20 +- ...-action-pretty-resources-tabs.component.ts | 32 ++- .../widget/widget-component.service.ts | 27 ++- .../rulechain/rulechain-routing.module.ts | 20 +- .../pages/widget/widget-editor.component.ts | 77 +++--- .../node-script-test-dialog.component.ts | 14 +- .../shared/components/js-func.component.ts | 10 +- .../components/json-content.component.ts | 10 +- .../json-form/react/json-form-ace-editor.tsx | 24 +- .../json-form/react/json-form-css.tsx | 7 +- .../json-form/react/json-form-html.tsx | 7 +- .../json-form/react/json-form-javascript.tsx | 6 +- .../json-form/react/json-form-json.tsx | 6 +- .../shared/components/nav-tree.component.ts | 229 +++++++++--------- .../time/timezone-select.component.ts | 87 +++---- .../src/app/shared/models/beautify.models.ts | 79 ++++++ ui-ngx/src/app/shared/models/device.models.ts | 2 +- .../src/app/shared/models/time/time.models.ts | 63 +++++ ui-ngx/tsconfig.json | 12 + 23 files changed, 535 insertions(+), 287 deletions(-) create mode 100644 ui-ngx/src/app/shared/models/beautify.models.ts diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json index a9a2cac52f..3cb140c837 100644 --- a/ui-ngx/angular.json +++ b/ui-ngx/angular.json @@ -86,25 +86,9 @@ ] }, "scripts": [ - "node_modules/jquery/dist/jquery.min.js", - "node_modules/jquery.terminal/js/jquery.terminal.min.js", - "node_modules/flot/lib/jquery.colorhelpers.js", - "node_modules/flot/src/jquery.flot.js", - "node_modules/flot/src/plugins/jquery.flot.time.js", - "node_modules/flot/src/plugins/jquery.flot.selection.js", - "node_modules/flot/src/plugins/jquery.flot.pie.js", - "node_modules/flot/src/plugins/jquery.flot.crosshair.js", - "node_modules/flot/src/plugins/jquery.flot.stack.js", - "node_modules/flot/src/plugins/jquery.flot.symbol.js", - "node_modules/flot.curvedlines/curvedLines.js", "node_modules/tinycolor2/dist/tinycolor-min.js", - "node_modules/tooltipster/dist/js/tooltipster.bundle.min.js", - "node_modules/split.js/dist/split.js", - "node_modules/js-beautify/js/lib/beautify.js", - "node_modules/js-beautify/js/lib/beautify-css.js", - "node_modules/js-beautify/js/lib/beautify-html.js", - "node_modules/systemjs/dist/system.js", - "node_modules/jstree/dist/jstree.min.js" + "node_modules/split.js/dist/split.min.js", + "node_modules/systemjs/dist/system.js" ], "customWebpackConfig": { "path": "./extra-webpack.config.js" @@ -130,7 +114,11 @@ "tinycolor2", "json-schema-defaults", "leaflet-providers", - "lodash" + "lodash", + "jquery", + "jquery.terminal", + "tooltipster", + "jstree" ] }, "configurations": { diff --git a/ui-ngx/extra-webpack.config.js b/ui-ngx/extra-webpack.config.js index f834952fb3..355d85551e 100644 --- a/ui-ngx/extra-webpack.config.js +++ b/ui-ngx/extra-webpack.config.js @@ -34,6 +34,13 @@ module.exports = (config, options) => { SUPPORTED_LANGS: JSON.stringify(langs), }) ); + config.plugins.push( + new webpack.ProvidePlugin( + { + $: "jquery" + } + ) + ); config.plugins.push( new CompressionPlugin({ filename: "[path][base].gz[query]", @@ -44,6 +51,9 @@ module.exports = (config, options) => { deleteOriginalAssets: false, }) ); + config.plugins.push( + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) + ); if (config.mode === 'production') { const index = config.plugins.findIndex(p => p instanceof AngularCompilerPlugin.AngularCompilerPlugin); diff --git a/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts b/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts index 9ca56d53ef..b8232e7109 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts @@ -24,6 +24,9 @@ import { DialogComponent } from '@shared/components/dialog.component'; import { Router } from '@angular/router'; import { ContentType, contentTypesMap } from '@shared/models/constants'; import { getAce } from '@shared/models/ace/ace.models'; +import { Observable } from 'rxjs/internal/Observable'; +import { beautifyJs } from '@shared/models/beautify.models'; +import { of } from 'rxjs'; export interface EventContentDialogData { content: string; @@ -64,33 +67,42 @@ export class EventContentDialogComponent extends DialogComponent = null; if (this.contentType) { mode = contentTypesMap.get(this.contentType).code; if (this.contentType === ContentType.JSON && content) { - content = js_beautify(content, {indent_size: 4}); + content$ = beautifyJs(content, {indent_size: 4}); } } - let editorOptions: Partial = { - mode: `ace/mode/${mode}`, - theme: 'ace/theme/github', - showGutter: false, - showPrintMargin: false, - readOnly: true - }; + if (!content$) { + content$ = of(content); + } + + content$.subscribe( + (processedContent) => { + let editorOptions: Partial = { + mode: `ace/mode/${mode}`, + theme: 'ace/theme/github', + showGutter: false, + showPrintMargin: false, + readOnly: true + }; - const advancedOptions = { - enableSnippets: false, - enableBasicAutocompletion: false, - enableLiveAutocompletion: false - }; + const advancedOptions = { + enableSnippets: false, + enableBasicAutocompletion: false, + enableLiveAutocompletion: false + }; - editorOptions = {...editorOptions, ...advancedOptions}; - getAce().subscribe( - (ace) => { - const editor = ace.edit(editorElement, editorOptions); - editor.session.setUseWrapMode(false); - editor.setValue(content, -1); - this.updateEditorSize(editorElement, content, editor); + editorOptions = {...editorOptions, ...advancedOptions}; + getAce().subscribe( + (ace) => { + const editor = ace.edit(editorElement, editorOptions); + editor.session.setUseWrapMode(false); + editor.setValue(processedContent, -1); + this.updateEditorSize(editorElement, processedContent, editor); + } + ); } ); } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html index ea4ad4097a..e6da924699 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html @@ -29,7 +29,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts index 65009dbd0b..7ee5102aa6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts @@ -32,11 +32,15 @@ import { AlarmSchedule, AlarmScheduleType, AlarmScheduleTypeTranslationMap, - dayOfWeekTranslations, getAlarmScheduleRangeText, timeOfDayToUTCTimestamp, utcTimestampToTimeOfDay + dayOfWeekTranslations, + getAlarmScheduleRangeText, + timeOfDayToUTCTimestamp, + utcTimestampToTimeOfDay } from '@shared/models/device.models'; import { isDefined, isDefinedAndNotNull } from '@core/utils'; -import * as _moment from 'moment-timezone'; import { MatCheckboxChange } from '@angular/material/checkbox'; +import { getDefaultTimezone } from '@shared/models/time/time.models'; +import { share } from 'rxjs/operators'; @Component({ selector: 'tb-alarm-schedule', @@ -58,7 +62,9 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, alarmScheduleForm: FormGroup; - defaultTimezone = _moment.tz.guess(); + defaultTimezone$ = getDefaultTimezone().pipe( + share() + ); alarmScheduleTypes = Object.keys(AlarmScheduleType); alarmScheduleType = AlarmScheduleType; @@ -93,9 +99,11 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems) }); this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { - this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: this.defaultTimezone}, {emitEvent: false}); - this.updateValidators(type, true); - this.alarmScheduleForm.updateValueAndValidity(); + getDefaultTimezone().subscribe((defaultTimezone) => { + this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); + this.updateValidators(type, true); + this.alarmScheduleForm.updateValueAndValidity(); + }); }); this.alarmScheduleForm.valueChanges.subscribe(() => { this.updateModel(); diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts index 16c924b711..0463bd2ba7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts @@ -34,13 +34,13 @@ import { AppState } from '@core/core.state'; import { CustomActionDescriptor } from '@shared/models/widget.models'; import { Ace } from 'ace-builds'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; -import { css_beautify, html_beautify } from 'js-beautify'; import { ResizeObserver } from '@juggle/resize-observer'; import { CustomPrettyActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; import { Observable } from 'rxjs/internal/Observable'; import { forkJoin, from } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { getAce } from '@shared/models/ace/ace.models'; +import { beautifyCss, beautifyHtml } from '@shared/models/beautify.models'; @Component({ selector: 'tb-custom-action-pretty-resources-tabs', @@ -140,21 +140,27 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl } public beautifyCss(): void { - const res = css_beautify(this.action.customCss, {indent_size: 4}); - if (this.action.customCss !== res) { - this.action.customCss = res; - this.cssEditor.setValue(this.action.customCss ? this.action.customCss : '', -1); - this.notifyActionUpdated(); - } + beautifyCss(this.action.customCss, {indent_size: 4}).subscribe( + (res) => { + if (this.action.customCss !== res) { + this.action.customCss = res; + this.cssEditor.setValue(this.action.customCss ? this.action.customCss : '', -1); + this.notifyActionUpdated(); + } + } + ); } public beautifyHtml(): void { - const res = html_beautify(this.action.customHtml, {indent_size: 4, wrap_line_length: 60}); - if (this.action.customHtml !== res) { - this.action.customHtml = res; - this.htmlEditor.setValue(this.action.customHtml ? this.action.customHtml : '', -1); - this.notifyActionUpdated(); - } + beautifyHtml(this.action.customHtml, {indent_size: 4, wrap_line_length: 60}).subscribe( + (res) => { + if (this.action.customHtml !== res) { + this.action.customHtml = res; + this.htmlEditor.setValue(this.action.customHtml ? this.action.customHtml : '', -1); + this.notifyActionUpdated(); + } + } + ); } private initAceEditors(): Observable { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index 01ebb4b39c..ff78ae2e3f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -111,11 +111,32 @@ export class WidgetComponentService { const initSubject = new ReplaySubject(); this.init$ = initSubject.asObservable(); - (window as any).tinycolor = tinycolor; - (window as any).cssjs = cssjs; - (window as any).moment = moment; + const w = (this.window as any); + + w.tinycolor = tinycolor; + w.cssjs = cssjs; + w.moment = moment; + w.$ = $; + w.jQuery = $; const widgetModulesTasks: Observable[] = []; + widgetModulesTasks.push(from(import('jquery.terminal'))); + + widgetModulesTasks.push(from(import('flot/src/jquery.flot.js')).pipe( + mergeMap(() => { + const flotJsPluginsTasks: Observable[] = []; + flotJsPluginsTasks.push(from(import('flot/lib/jquery.colorhelpers.js'))); + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.time.js'))); + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.selection.js'))); + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.pie.js'))); + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.crosshair.js'))); + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.stack.js'))); + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.symbol.js'))); + flotJsPluginsTasks.push(from(import('flot.curvedlines/curvedLines.js'))); + return forkJoin(flotJsPluginsTasks); + }) + )); + widgetModulesTasks.push(from(import('@home/components/widget/lib/flot-widget')).pipe( tap((mod) => { (window as any).TbFlot = mod.TbFlot; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts index 0431be2e14..e0c667a889 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts @@ -29,7 +29,7 @@ import { import { EntitiesTableComponent } from '../../components/entity/entities-table.component'; import { Authority } from '@shared/models/authority.enum'; import { RuleChainsTableConfigResolver } from '@modules/home/pages/rulechain/rulechains-table-config.resolver'; -import { Observable } from 'rxjs'; +import { from, Observable } from 'rxjs'; import { BreadCrumbConfig, BreadCrumbLabelFunction } from '@shared/components/breadcrumb'; import { ResolvedRuleChainMetaData, RuleChain } from '@shared/models/rule-chain.models'; import { RuleChainService } from '@core/http/rule-chain.service'; @@ -75,6 +75,17 @@ export class RuleNodeComponentsResolver implements Resolve { + + constructor() { + } + + resolve(route: ActivatedRouteSnapshot): Observable { + return from(import('tooltipster')); + } +} + @Injectable() export class RuleChainImportGuard implements CanActivate { @@ -144,7 +155,8 @@ const routes: Routes = [ resolve: { ruleChain: RuleChainResolver, ruleChainMetaData: ResolvedRuleChainMetaDataResolver, - ruleNodeComponents: RuleNodeComponentsResolver + ruleNodeComponents: RuleNodeComponentsResolver, + tooltipster: TooltipsterResolver } }, { @@ -162,7 +174,8 @@ const routes: Routes = [ import: true }, resolve: { - ruleNodeComponents: RuleNodeComponentsResolver + ruleNodeComponents: RuleNodeComponentsResolver, + tooltipster: TooltipsterResolver } } ] @@ -178,6 +191,7 @@ const routes: Routes = [ RuleChainResolver, ResolvedRuleChainMetaDataResolver, RuleNodeComponentsResolver, + TooltipsterResolver, RuleChainImportGuard ] }) diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts index 46338a2dfe..136e2133f6 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts @@ -34,7 +34,6 @@ import { TranslateService } from '@ngx-translate/core'; import { getCurrentIsLoading } from '@app/core/interceptors/load.selectors'; import { Ace } from 'ace-builds'; import { getAce, Range } from '@shared/models/ace/ace.models'; -import { css_beautify, html_beautify } from 'js-beautify'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { WINDOW } from '@core/services/window.service'; import { WindowMessage } from '@shared/models/window-message.model'; @@ -51,6 +50,7 @@ import Timeout = NodeJS.Timeout; import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models'; import { Observable } from 'rxjs/internal/Observable'; import { map, tap } from 'rxjs/operators'; +import { beautifyCss, beautifyHtml, beautifyJs } from '@shared/models/beautify.models'; // @dynamic @Component({ @@ -618,48 +618,63 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe } beautifyCss(): void { - const res = css_beautify(this.widget.templateCss, {indent_size: 4}); - if (this.widget.templateCss !== res) { - this.isDirty = true; - this.widget.templateCss = res; - this.cssEditor.setValue(this.widget.templateCss ? this.widget.templateCss : '', -1); - } + beautifyCss(this.widget.templateCss, {indent_size: 4}).subscribe( + (res) => { + if (this.widget.templateCss !== res) { + this.isDirty = true; + this.widget.templateCss = res; + this.cssEditor.setValue(this.widget.templateCss ? this.widget.templateCss : '', -1); + } + } + ); } beautifyHtml(): void { - const res = html_beautify(this.widget.templateHtml, {indent_size: 4, wrap_line_length: 60}); - if (this.widget.templateHtml !== res) { - this.isDirty = true; - this.widget.templateHtml = res; - this.htmlEditor.setValue(this.widget.templateHtml ? this.widget.templateHtml : '', -1); - } + beautifyHtml(this.widget.templateHtml, {indent_size: 4, wrap_line_length: 60}).subscribe( + (res) => { + if (this.widget.templateHtml !== res) { + this.isDirty = true; + this.widget.templateHtml = res; + this.htmlEditor.setValue(this.widget.templateHtml ? this.widget.templateHtml : '', -1); + } + } + ); } beautifyJson(): void { - const res = js_beautify(this.widget.settingsSchema, {indent_size: 4}); - if (this.widget.settingsSchema !== res) { - this.isDirty = true; - this.widget.settingsSchema = res; - this.jsonSettingsEditor.setValue(this.widget.settingsSchema ? this.widget.settingsSchema : '', -1); - } + beautifyJs(this.widget.settingsSchema, {indent_size: 4}).subscribe( + (res) => { + if (this.widget.settingsSchema !== res) { + this.isDirty = true; + this.widget.settingsSchema = res; + this.jsonSettingsEditor.setValue(this.widget.settingsSchema ? this.widget.settingsSchema : '', -1); + } + } + ); } beautifyDataKeyJson(): void { - const res = js_beautify(this.widget.dataKeySettingsSchema, {indent_size: 4}); - if (this.widget.dataKeySettingsSchema !== res) { - this.isDirty = true; - this.widget.dataKeySettingsSchema = res; - this.dataKeyJsonSettingsEditor.setValue(this.widget.dataKeySettingsSchema ? this.widget.dataKeySettingsSchema : '', -1); - } + beautifyJs(this.widget.dataKeySettingsSchema, {indent_size: 4}).subscribe( + (res) => { + if (this.widget.dataKeySettingsSchema !== res) { + this.isDirty = true; + this.widget.dataKeySettingsSchema = res; + this.dataKeyJsonSettingsEditor.setValue(this.widget.dataKeySettingsSchema ? this.widget.dataKeySettingsSchema : '', -1); + } + } + ); } beautifyJs(): void { - const res = js_beautify(this.widget.controllerScript, {indent_size: 4, wrap_line_length: 60}); - if (this.widget.controllerScript !== res) { - this.isDirty = true; - this.widget.controllerScript = res; - this.jsEditor.setValue(this.widget.controllerScript ? this.widget.controllerScript : '', -1); - } + beautifyJs(this.widget.controllerScript, {indent_size: 4, wrap_line_length: 60}).subscribe( + (res) => { + if (this.widget.controllerScript !== res) { + this.isDirty = true; + this.widget.controllerScript = res; + this.jsEditor.setValue(this.widget.controllerScript ? this.widget.controllerScript : '', -1); + } + } + ); } removeResource(index: number) { diff --git a/ui-ngx/src/app/shared/components/dialog/node-script-test-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/node-script-test-dialog.component.ts index 007a96dbad..8202728131 100644 --- a/ui-ngx/src/app/shared/components/dialog/node-script-test-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/node-script-test-dialog.component.ts @@ -41,6 +41,7 @@ import { TestScriptInputParams } from '@shared/models/rule-node.models'; import { RuleChainService } from '@core/http/rule-chain.service'; import { mergeMap } from 'rxjs/operators'; import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { beautifyJs } from '@shared/models/beautify.models'; export interface NodeScriptTestDialogData { script: string; @@ -110,12 +111,17 @@ export class NodeScriptTestDialogComponent extends DialogComponent { + this.nodeScriptTestFormGroup.get('payload').get('msg').patchValue(res, {emitEvent: false}); + } + ); } ngAfterViewInit(): void { @@ -166,7 +172,11 @@ export class NodeScriptTestDialogComponent extends DialogComponent { - this.nodeScriptTestFormGroup.get('output').setValue(js_beautify(output, {indent_size: 4})); + beautifyJs(output, {indent_size: 4}).subscribe( + (res) => { + this.nodeScriptTestFormGroup.get('output').setValue(res); + } + ); }); } diff --git a/ui-ngx/src/app/shared/components/js-func.component.ts b/ui-ngx/src/app/shared/components/js-func.component.ts index 0fa827e4b7..93e7848072 100644 --- a/ui-ngx/src/app/shared/components/js-func.component.ts +++ b/ui-ngx/src/app/shared/components/js-func.component.ts @@ -37,6 +37,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { ResizeObserver } from '@juggle/resize-observer'; import { TbEditorCompleter } from '@shared/models/ace/completion.models'; +import { beautifyJs } from '@shared/models/beautify.models'; @Component({ selector: 'tb-js-func', @@ -214,9 +215,12 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, } beautifyJs() { - const res = js_beautify(this.modelValue, {indent_size: 4, wrap_line_length: 60}); - this.jsEditor.setValue(res ? res : '', -1); - this.updateView(); + beautifyJs(this.modelValue, {indent_size: 4, wrap_line_length: 60}).subscribe( + (res) => { + this.jsEditor.setValue(res ? res : '', -1); + this.updateView(); + } + ); } validateOnSubmit(): void { diff --git a/ui-ngx/src/app/shared/components/json-content.component.ts b/ui-ngx/src/app/shared/components/json-content.component.ts index cbe5368a8a..00a7745819 100644 --- a/ui-ngx/src/app/shared/components/json-content.component.ts +++ b/ui-ngx/src/app/shared/components/json-content.component.ts @@ -36,6 +36,7 @@ import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { guid } from '@core/utils'; import { ResizeObserver } from '@juggle/resize-observer'; import { getAce } from '@shared/models/ace/ace.models'; +import { beautifyJs } from '@shared/models/beautify.models'; @Component({ selector: 'tb-json-content', @@ -286,9 +287,12 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid } beautifyJSON() { - const res = js_beautify(this.contentBody, {indent_size: 4, wrap_line_length: 60}); - this.jsonEditor.setValue(res ? res : '', -1); - this.updateView(); + beautifyJs(this.contentBody, {indent_size: 4, wrap_line_length: 60}).subscribe( + (res) => { + this.jsonEditor.setValue(res ? res : '', -1); + this.updateView(); + } + ); } minifyJSON() { diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx index ae2e112601..8e630582a3 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx @@ -22,6 +22,7 @@ import { IEditorProps } from 'react-ace/src/types'; import { map, mergeMap } from 'rxjs/operators'; import { loadAceDependencies } from '@shared/models/ace/ace.models'; import { from } from 'rxjs'; +import { Observable } from 'rxjs/internal/Observable'; const ReactAce = React.lazy(() => { return loadAceDependencies().pipe( @@ -33,7 +34,7 @@ const ReactAce = React.lazy(() => { interface ThingsboardAceEditorProps extends JsonFormFieldProps { mode: string; - onTidy: (value: string) => string; + onTidy: (value: string) => Observable; } interface ThingsboardAceEditorState extends JsonFormFieldState { @@ -84,15 +85,18 @@ class ThingsboardAceEditor extends React.Component { + this.setState({ + value: processedValue + }); + this.props.onChangeValidate({ + target: { + value: processedValue + } + }); + } + ); } } diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-css.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-css.tsx index a5107ad4db..e8b3616b8b 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-css.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-css.tsx @@ -16,7 +16,8 @@ import * as React from 'react'; import ThingsboardAceEditor from './json-form-ace-editor'; import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import { css_beautify } from 'js-beautify'; +import { Observable } from 'rxjs/internal/Observable'; +import { beautifyCss } from '@shared/models/beautify.models'; class ThingsboardCss extends React.Component { @@ -25,8 +26,8 @@ class ThingsboardCss extends React.Component { + return beautifyCss(css, {indent_size: 4}); } render() { diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-html.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-html.tsx index 4d7d0c956f..9e929b36aa 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-html.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-html.tsx @@ -15,8 +15,9 @@ */ import * as React from 'react'; import ThingsboardAceEditor from './json-form-ace-editor'; -import { html_beautify } from 'js-beautify'; import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; +import { Observable } from 'rxjs/internal/Observable'; +import { beautifyHtml } from '@shared/models/beautify.models'; class ThingsboardHtml extends React.Component { @@ -25,8 +26,8 @@ class ThingsboardHtml extends React.Component { + return beautifyHtml(html, {indent_size: 4}); } render() { diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-javascript.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-javascript.tsx index 4530434b03..427c79ac65 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-javascript.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-javascript.tsx @@ -16,6 +16,8 @@ import * as React from 'react'; import ThingsboardAceEditor from './json-form-ace-editor'; import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; +import { Observable } from 'rxjs/internal/Observable'; +import { beautifyJs } from '@shared/models/beautify.models'; class ThingsboardJavaScript extends React.Component { @@ -24,8 +26,8 @@ class ThingsboardJavaScript extends React.Component { + return beautifyJs(javascript, {indent_size: 4, wrap_line_length: 60}); } render() { diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-json.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-json.tsx index 5fc5f5d98f..0034b50422 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-json.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-json.tsx @@ -16,6 +16,8 @@ import * as React from 'react'; import ThingsboardAceEditor from './json-form-ace-editor'; import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; +import { Observable } from 'rxjs/internal/Observable'; +import { beautifyJs } from '@shared/models/beautify.models'; class ThingsboardJson extends React.Component { @@ -24,8 +26,8 @@ class ThingsboardJson extends React.Component { + return beautifyJs(json, {indent_size: 4}); } render() { diff --git a/ui-ngx/src/app/shared/components/nav-tree.component.ts b/ui-ngx/src/app/shared/components/nav-tree.component.ts index a06a33d5c5..08b2256646 100644 --- a/ui-ngx/src/app/shared/components/nav-tree.component.ts +++ b/ui-ngx/src/app/shared/components/nav-tree.component.ts @@ -145,128 +145,131 @@ export class NavTreeComponent implements OnInit { }; } - this.treeElement = $('.tb-nav-tree-container', this.elementRef.nativeElement).jstree(config); + import('jstree').then(() => { - this.treeElement.on('changed.jstree', (e: any, data) => { - const node: NavTreeNode = data.instance.get_selected(true)[0]; - if (this.onNodeSelected) { - this.ngZone.run(() => this.onNodeSelected(node, e as Event)); - } - }); - - this.treeElement.on('model.jstree', (e: any, data) => { - if (this.onNodesInserted) { - this.ngZone.run(() => this.onNodesInserted(data.nodes, data.parent)); - } - }); + this.treeElement = $('.tb-nav-tree-container', this.elementRef.nativeElement).jstree(config); - if (this.editCallbacks) { - this.editCallbacks.selectNode = id => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - this.treeElement.jstree('deselect_all', true); - this.treeElement.jstree('select_node', node); - } - }; - this.editCallbacks.deselectAll = () => { - this.treeElement.jstree('deselect_all'); - }; - this.editCallbacks.getNode = (id) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - return node; - }; - this.editCallbacks.getParentNodeId = (id) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - return this.treeElement.jstree('get_parent', node); + this.treeElement.on('changed.jstree', (e: any, data) => { + const node: NavTreeNode = data.instance.get_selected(true)[0]; + if (this.onNodeSelected) { + this.ngZone.run(() => this.onNodeSelected(node, e as Event)); } - }; - this.editCallbacks.openNode = (id, cb) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - this.treeElement.jstree('open_node', node, cb); - } - }; - this.editCallbacks.nodeIsOpen = (id) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - return this.treeElement.jstree('is_open', node); - } else { - return true; - } - }; - this.editCallbacks.nodeIsLoaded = (id) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - return this.treeElement.jstree('is_loaded', node); - } else { - return true; + }); + + this.treeElement.on('model.jstree', (e: any, data) => { + if (this.onNodesInserted) { + this.ngZone.run(() => this.onNodesInserted(data.nodes, data.parent)); } - }; - this.editCallbacks.refreshNode = (id) => { - if (id === '#') { - this.treeElement.jstree('refresh'); - this.treeElement.jstree('redraw'); - } else { + }); + + if (this.editCallbacks) { + this.editCallbacks.selectNode = id => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + this.treeElement.jstree('deselect_all', true); + this.treeElement.jstree('select_node', node); + } + }; + this.editCallbacks.deselectAll = () => { + this.treeElement.jstree('deselect_all'); + }; + this.editCallbacks.getNode = (id) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + return node; + }; + this.editCallbacks.getParentNodeId = (id) => { const node: NavTreeNode = this.treeElement.jstree('get_node', id); if (node) { - const opened = this.treeElement.jstree('is_open', node); - this.treeElement.jstree('refresh_node', node); + return this.treeElement.jstree('get_parent', node); + } + }; + this.editCallbacks.openNode = (id, cb) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + this.treeElement.jstree('open_node', node, cb); + } + }; + this.editCallbacks.nodeIsOpen = (id) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + return this.treeElement.jstree('is_open', node); + } else { + return true; + } + }; + this.editCallbacks.nodeIsLoaded = (id) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + return this.treeElement.jstree('is_loaded', node); + } else { + return true; + } + }; + this.editCallbacks.refreshNode = (id) => { + if (id === '#') { + this.treeElement.jstree('refresh'); this.treeElement.jstree('redraw'); - if (node.children && opened/* && !node.children.length*/) { - this.treeElement.jstree('open_node', node); + } else { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + const opened = this.treeElement.jstree('is_open', node); + this.treeElement.jstree('refresh_node', node); + this.treeElement.jstree('redraw'); + if (node.children && opened/* && !node.children.length*/) { + this.treeElement.jstree('open_node', node); + } } } - } - }; - this.editCallbacks.updateNode = (id, newName) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - this.treeElement.jstree('rename_node', node, newName); - } - }; - this.editCallbacks.createNode = (parentId, node, pos) => { - const parentNode: NavTreeNode = this.treeElement.jstree('get_node', parentId); - if (parentNode) { - this.treeElement.jstree('create_node', parentNode, node, pos); - } - }; - this.editCallbacks.deleteNode = (id) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - this.treeElement.jstree('delete_node', node); - } - }; - this.editCallbacks.disableNode = (id) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - this.treeElement.jstree('disable_node', node); - } - }; - this.editCallbacks.enableNode = (id) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - this.treeElement.jstree('enable_node', node); - } - }; - this.editCallbacks.setNodeHasChildren = (id, hasChildren) => { - const node: NavTreeNode = this.treeElement.jstree('get_node', id); - if (node) { - if (!node.children || (Array.isArray(node.children) && !node.children.length)) { - node.children = hasChildren; - node.state.loaded = !hasChildren; - node.state.opened = false; - this.treeElement.jstree('_node_changed', node.id); - this.treeElement.jstree('redraw'); + }; + this.editCallbacks.updateNode = (id, newName) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + this.treeElement.jstree('rename_node', node, newName); } - } - }; - this.editCallbacks.search = (searchText) => { - this.treeElement.jstree('search', searchText); - }; - this.editCallbacks.clearSearch = () => { - this.treeElement.jstree('clear_search'); - }; - } + }; + this.editCallbacks.createNode = (parentId, node, pos) => { + const parentNode: NavTreeNode = this.treeElement.jstree('get_node', parentId); + if (parentNode) { + this.treeElement.jstree('create_node', parentNode, node, pos); + } + }; + this.editCallbacks.deleteNode = (id) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + this.treeElement.jstree('delete_node', node); + } + }; + this.editCallbacks.disableNode = (id) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + this.treeElement.jstree('disable_node', node); + } + }; + this.editCallbacks.enableNode = (id) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + this.treeElement.jstree('enable_node', node); + } + }; + this.editCallbacks.setNodeHasChildren = (id, hasChildren) => { + const node: NavTreeNode = this.treeElement.jstree('get_node', id); + if (node) { + if (!node.children || (Array.isArray(node.children) && !node.children.length)) { + node.children = hasChildren; + node.state.loaded = !hasChildren; + node.state.opened = false; + this.treeElement.jstree('_node_changed', node.id); + this.treeElement.jstree('redraw'); + } + } + }; + this.editCallbacks.search = (searchText) => { + this.treeElement.jstree('search', searchText); + }; + this.editCallbacks.clearSearch = () => { + this.treeElement.jstree('clear_search'); + }; + } + }); } } diff --git a/ui-ngx/src/app/shared/components/time/timezone-select.component.ts b/ui-ngx/src/app/shared/components/time/timezone-select.component.ts index 7dc89df7da..a65fc28481 100644 --- a/ui-ngx/src/app/shared/components/time/timezone-select.component.ts +++ b/ui-ngx/src/app/shared/components/time/timezone-select.component.ts @@ -16,21 +16,14 @@ import { AfterViewInit, Component, forwardRef, Input, NgZone, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable, of } from 'rxjs'; +import { Observable } from 'rxjs'; import { map, mergeMap, share, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import * as _moment from 'moment-timezone'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; - -interface TimezoneInfo { - id: string; - name: string; - offset: string; - nOffset: number; -} +import { getTimezoneInfo, getTimezones, TimezoneInfo } from '@shared/models/time/time.models'; @Component({ selector: 'tb-timezone-select', @@ -50,28 +43,14 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af defaultTimezoneId: string = null; - defaultTimezoneInfo: TimezoneInfo = null; - - timezones: TimezoneInfo[] = _moment.tz.names().map((zoneName) => { - const tz = _moment.tz(zoneName); - return { - id: zoneName, - name: zoneName.replace(/_/g, ' '), - offset: `UTC${tz.format('Z')}`, - nOffset: tz.utcOffset() - } - }); + timezones$ = getTimezones().pipe( + share() + ); @Input() set defaultTimezone(timezone: string) { if (this.defaultTimezoneId !== timezone) { this.defaultTimezoneId = timezone; - if (this.defaultTimezoneId) { - this.defaultTimezoneInfo = - this.timezones.find((timezoneInfo) => timezoneInfo.id === this.defaultTimezoneId); - } else { - this.defaultTimezoneInfo = null; - } } } @@ -150,23 +129,27 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af writeValue(value: string | null): void { this.searchText = ''; - let foundTimezone: TimezoneInfo = null; if (value !== null) { - foundTimezone = this.timezones.find(timezoneInfo => timezoneInfo.id === value); - } - if (foundTimezone !== null) { - this.modelValue = value; - this.selectTimezoneFormGroup.get('timezone').patchValue(foundTimezone, {emitEvent: false}); + getTimezoneInfo(value, this.defaultTimezoneId).subscribe( + (foundTimezone) => { + if (foundTimezone !== null) { + this.selectTimezoneFormGroup.get('timezone').patchValue(foundTimezone, {emitEvent: false}); + if (foundTimezone.id !== value) { + setTimeout(() => { + this.updateView(foundTimezone.id); + }, 0); + } else { + this.modelValue = value; + } + } else { + this.modelValue = null; + this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); + } + } + ); } else { - if (this.defaultTimezoneInfo) { - this.selectTimezoneFormGroup.get('timezone').patchValue(this.defaultTimezoneInfo, {emitEvent: false}); - setTimeout(() => { - this.updateView(this.defaultTimezoneInfo.id); - }, 0); - } else { - this.modelValue = null; - this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); - } + this.modelValue = null; + this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); } this.dirty = true; } @@ -182,10 +165,15 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af if (this.ignoreClosePanel) { this.ignoreClosePanel = false; } else { - if (!this.modelValue && this.defaultTimezoneInfo) { - this.ngZone.run(() => { - this.selectTimezoneFormGroup.get('timezone').reset(this.defaultTimezoneInfo, {emitEvent: true}); - }); + if (!this.modelValue && this.defaultTimezoneId) { + getTimezoneInfo(this.defaultTimezoneId).subscribe( + (defaultTimezoneInfo) => { + if (defaultTimezoneInfo !== null) { + this.ngZone.run(() => { + this.selectTimezoneFormGroup.get('timezone').reset(defaultTimezoneInfo, {emitEvent: true}); + }); + } + }); } } } @@ -203,12 +191,13 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af fetchTimezones(searchText?: string): Observable> { this.searchText = searchText; - let result = this.timezones; if (searchText && searchText.length) { - result = this.timezones.filter((timezoneInfo) => - timezoneInfo.name.toLowerCase().includes(searchText.toLowerCase())); + return getTimezones().pipe( + map((timezones) => timezones.filter((timezoneInfo) => + timezoneInfo.name.toLowerCase().includes(searchText.toLowerCase()))) + ); } - return of(result); + return getTimezones(); } clear() { diff --git a/ui-ngx/src/app/shared/models/beautify.models.ts b/ui-ngx/src/app/shared/models/beautify.models.ts new file mode 100644 index 0000000000..fa07721e29 --- /dev/null +++ b/ui-ngx/src/app/shared/models/beautify.models.ts @@ -0,0 +1,79 @@ +/// +/// Copyright © 2016-2020 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Observable } from 'rxjs/internal/Observable'; +import { from, of } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; + +let jsBeautifyModule: any; +let htmlBeautifyModule: any; +let cssBeautifyModule: any; + +function loadJsBeautify(): Observable { + if (jsBeautifyModule) { + return of(jsBeautifyModule); + } else { + return from(import('js-beautify/js/lib/beautify.js')).pipe( + tap((module) => { + jsBeautifyModule = module; + }) + ); + } +} + +function loadHtmlBeautify(): Observable { + if (htmlBeautifyModule) { + return of(htmlBeautifyModule); + } else { + return from(import('js-beautify/js/lib/beautify-html.js')).pipe( + tap((module) => { + htmlBeautifyModule = module; + }) + ); + } +} + +function loadCssBeautify(): Observable { + if (cssBeautifyModule) { + return of(cssBeautifyModule); + } else { + return from(import('js-beautify/js/lib/beautify-css.js')).pipe( + tap((module) => { + cssBeautifyModule = module; + }) + ); + } +} + +export function beautifyJs(source: string, options?: JSBeautifyOptions): Observable { + return loadJsBeautify().pipe( + map((mod) => { + return mod.js_beautify(source, options); + }) + ); +} + +export function beautifyCss(source: string, options?: CSSBeautifyOptions): Observable { + return loadCssBeautify().pipe( + map((mod) => mod.css_beautify(source, options)) + ); +} + +export function beautifyHtml(source: string, options?: HTMLBeautifyOptions): Observable { + return loadHtmlBeautify().pipe( + map((mod) => mod.html_beautify(source, options)) + ); +} diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index ffeae2f56d..e4d240d0d5 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -25,7 +25,7 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { EntityInfoData } from '@shared/models/entity.models'; import { KeyFilter } from '@shared/models/query/query.models'; import { TimeUnit } from '@shared/models/time/time.models'; -import * as _moment from 'moment-timezone'; +import * as _moment from 'moment'; import { AbstractControl, ValidationErrors } from '@angular/forms'; export enum DeviceProfileType { diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index b81952dbde..19d2a12e26 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -17,6 +17,9 @@ import { TimeService } from '@core/services/time.service'; import { deepClone, isDefined, isUndefined } from '@app/core/utils'; import * as moment_ from 'moment'; +import { Observable } from 'rxjs/internal/Observable'; +import { from, of } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; const moment = moment_; @@ -481,3 +484,63 @@ export const timeUnitTranslationMap = new Map( [TimeUnit.DAYS, 'timeunit.days'] ] ); + +export interface TimezoneInfo { + id: string; + name: string; + offset: string; + nOffset: number; +} + +let timezones: TimezoneInfo[] = null; +let defaultTimezone: string = null; + +export function getTimezones(): Observable { + if (timezones) { + return of(timezones); + } else { + return from(import('moment-timezone')).pipe( + map((monentTz) => { + return monentTz.tz.names().map((zoneName) => { + const tz = monentTz.tz(zoneName); + return { + id: zoneName, + name: zoneName.replace(/_/g, ' '), + offset: `UTC${tz.format('Z')}`, + nOffset: tz.utcOffset() + }; + }); + }), + tap((zones) => { + timezones = zones; + }) + ); + } +} + +export function getTimezoneInfo(timezoneId: string, defaultTimezoneId?: string): Observable { + return getTimezones().pipe( + map((timezoneList) => { + let foundTimezone = timezoneList.find(timezoneInfo => timezoneInfo.id === timezoneId); + if (!foundTimezone && defaultTimezoneId) { + foundTimezone = timezoneList.find(timezoneInfo => timezoneInfo.id === defaultTimezoneId); + } + return foundTimezone; + }) + ); +} + +export function getDefaultTimezone(): Observable { + if (defaultTimezone) { + return of(defaultTimezone); + } else { + return from(import('moment-timezone')).pipe( + map((monentTz) => { + return monentTz.tz.guess(); + }), + tap((zone) => { + defaultTimezone = zone; + }) + ); + } +} diff --git a/ui-ngx/tsconfig.json b/ui-ngx/tsconfig.json index baaa5cdb3a..1fb19bb630 100644 --- a/ui-ngx/tsconfig.json +++ b/ui-ngx/tsconfig.json @@ -37,6 +37,18 @@ ], "ace": [ "node_modules/ace-builds/src-noconflict/ace.js" + ], + "jquery": [ + "node_modules/jquery/dist/jquery.min.js" + ], + "jquery.terminal": [ + "node_modules/jquery.terminal/js/jquery.terminal.js" + ], + "tooltipster": [ + "node_modules/tooltipster/dist/js/tooltipster.bundle.min.js" + ], + "jstree": [ + "node_modules/jstree/dist/jstree.min.js" ] }, "lib": [