Browse Source

[editor] Fix the editor related components

This takes care of:

- Naming issue with wrapper
- Executable comparison broken due to Vue proxy objects
- Autocompleter not registered correctly
- Autocomplete categories not handled correctly in Vue
- Removed HueTable component generics
- Syntax worker broken due to Vue proxy not being serializable
Johan Ahlen 4 years ago
parent
commit
23d2138684

+ 7 - 19
desktop/core/src/desktop/js/apps/editor/components/ExecutableActions.vue

@@ -106,28 +106,17 @@
 
 
     emits: ['limit-changed'],
     emits: ['limit-changed'],
 
 
-    setup(): {
-      subTracker: SubscriptionTracker;
-    } {
-      return {
-        subTracker: new SubscriptionTracker()
-      };
+    setup() {
+      const subTracker = new SubscriptionTracker();
+      return { subTracker };
     },
     },
 
 
-    data(): {
-      loadingSession: boolean;
-      lastSession: Session | null;
-      partOfRunningExecution: boolean;
-      limit: number | null;
-      stopping: boolean;
-      status: ExecutionStatus;
-      hasStatement: boolean;
-    } {
+    data() {
       return {
       return {
         loadingSession: true,
         loadingSession: true,
-        lastSession: null,
+        lastSession: null as Session | null,
         partOfRunningExecution: false,
         partOfRunningExecution: false,
-        limit: null,
+        limit: null as number | null,
         stopping: false,
         stopping: false,
         status: ExecutionStatus.ready,
         status: ExecutionStatus.ready,
         hasStatement: false
         hasStatement: false
@@ -172,8 +161,7 @@
 
 
     mounted(): void {
     mounted(): void {
       this.subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
       this.subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
-        debugger;
-        if (this.executable === executable) {
+        if (this.executable.id === executable.id) {
           this.updateFromExecutable(executable);
           this.updateFromExecutable(executable);
         }
         }
       });
       });

+ 1 - 0
desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceEditor.scss

@@ -280,6 +280,7 @@
   .autocompleter-dot {
   .autocompleter-dot {
     display: inline-block;
     display: inline-block;
     margin-top: 5px;
     margin-top: 5px;
+    margin-right: 5px;
     width: 8px;
     width: 8px;
     height: 8px;
     height: 8px;
     border-radius: 4px;
     border-radius: 4px;

+ 7 - 21
desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceEditor.vue

@@ -69,7 +69,6 @@
     components: {
     components: {
       AceAutocomplete
       AceAutocomplete
     },
     },
-
     props: {
     props: {
       initialValue: {
       initialValue: {
         type: String,
         type: String,
@@ -93,7 +92,6 @@
         required: false,
         required: false,
         default: () => ({})
         default: () => ({})
       },
       },
-
       sqlParserProvider: {
       sqlParserProvider: {
         type: Object as PropType<SqlParserProvider>,
         type: Object as PropType<SqlParserProvider>,
         default: undefined
         default: undefined
@@ -103,7 +101,6 @@
         default: undefined
         default: undefined
       }
       }
     },
     },
-
     emits: [
     emits: [
       'value-changed',
       'value-changed',
       'create-new-doc',
       'create-new-doc',
@@ -112,29 +109,18 @@
       'ace-created',
       'ace-created',
       'cursor-changed'
       'cursor-changed'
     ],
     ],
-
-    setup(): {
-      subTracker: SubscriptionTracker;
-    } {
-      return {
-        subTracker: new SubscriptionTracker()
-      };
+    setup() {
+      const subTracker = new SubscriptionTracker();
+      return { subTracker };
     },
     },
-
-    data(): {
-      editor: Ace.Editor | null;
-      autocompleteParser: AutocompleteParser | null;
-      aceLocationHandler: AceLocationHandler | null;
-      lastFocusedEditor: boolean;
-    } {
+    data() {
       return {
       return {
-        editor: null,
-        autocompleteParser: null,
-        aceLocationHandler: null,
+        editor: null as Ace.Editor | null,
+        autocompleteParser: null as AutocompleteParser | null,
+        aceLocationHandler: null as AceLocationHandler | null,
         lastFocusedEditor: false
         lastFocusedEditor: false
       };
       };
     },
     },
-
     mounted(): void {
     mounted(): void {
       const editorElement = <HTMLElement>this.$refs['editorElement'];
       const editorElement = <HTMLElement>this.$refs['editorElement'];
       if (!editorElement) {
       if (!editorElement) {

+ 11 - 23
desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceEditorKoBridge.vue

@@ -46,16 +46,13 @@
   import AceEditor from './AceEditor.vue';
   import AceEditor from './AceEditor.vue';
   import Executor from 'apps/editor/execution/executor';
   import Executor from 'apps/editor/execution/executor';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
-  import sqlParserRepository, { SqlParserRepository } from 'parse/sql/sqlParserRepository';
-  import sqlReferenceRepository, {
-    SqlReferenceRepository
-  } from 'sql/reference/sqlReferenceRepository';
+  import sqlParserRepository from 'parse/sql/sqlParserRepository';
+  import sqlReferenceRepository from 'sql/reference/sqlReferenceRepository';
 
 
   const AceEditorKoBridge = defineComponent({
   const AceEditorKoBridge = defineComponent({
     components: {
     components: {
       AceEditor
       AceEditor
     },
     },
-
     props: {
     props: {
       executor: {
       executor: {
         type: Object as PropType<Executor>,
         type: Object as PropType<Executor>,
@@ -78,30 +75,21 @@
         default: undefined
         default: undefined
       }
       }
     },
     },
-
-    setup(): {
-      sqlParserProvider: SqlParserRepository;
-      sqlReferenceProvider: SqlReferenceRepository;
-      subTracker: SubscriptionTracker;
-    } {
+    setup() {
       return {
       return {
         sqlParserProvider: sqlParserRepository,
         sqlParserProvider: sqlParserRepository,
         sqlReferenceProvider: sqlReferenceRepository,
         sqlReferenceProvider: sqlReferenceRepository,
         subTracker: new SubscriptionTracker()
         subTracker: new SubscriptionTracker()
       };
       };
     },
     },
-
-    data(): {
-      cursorPosition?: Ace.Position;
-      editorId?: string;
-      initialized: boolean;
-      value?: string;
-    } {
+    data() {
       return {
       return {
+        cursorPosition: null as Ace.Position | null,
+        editorId: null as string | null,
+        value: null as string | null,
         initialized: false
         initialized: false
       };
       };
     },
     },
-
     updated(): void {
     updated(): void {
       if (
       if (
         !this.initialized &&
         !this.initialized &&
@@ -109,12 +97,12 @@
         this.idObservable &&
         this.idObservable &&
         this.cursorPositionObservable
         this.cursorPositionObservable
       ) {
       ) {
-        this.value = this.valueObservable();
+        this.value = this.valueObservable() || null;
         this.subTracker.subscribe(this.valueObservable, (value?: string) => {
         this.subTracker.subscribe(this.valueObservable, (value?: string) => {
-          this.value = value;
+          this.value = value || null;
         });
         });
 
 
-        this.editorId = this.idObservable();
+        this.editorId = this.idObservable() || null;
         if (!this.editorId) {
         if (!this.editorId) {
           this.subTracker
           this.subTracker
             .whenDefined<string>(this.idObservable)
             .whenDefined<string>(this.idObservable)
@@ -124,7 +112,7 @@
             .catch(noop);
             .catch(noop);
         }
         }
 
 
-        this.cursorPosition = this.cursorPositionObservable();
+        this.cursorPosition = this.cursorPositionObservable() || null;
         if (!this.cursorPosition) {
         if (!this.cursorPosition) {
           this.subTracker
           this.subTracker
             .whenDefined<Ace.Position>(this.cursorPositionObservable)
             .whenDefined<Ace.Position>(this.cursorPositionObservable)

+ 43 - 90
desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AceAutocomplete.vue

@@ -31,9 +31,10 @@
             v-for="category in availableCategories"
             v-for="category in availableCategories"
             :key="category.label"
             :key="category.label"
             :style="{
             :style="{
-              'border-color': activeCategory === category ? category.color : 'transparent'
+              'border-color':
+                activeCategory.categoryId === category.categoryId ? category.color : 'transparent'
             }"
             }"
-            :class="{ active: activeCategory === category }"
+            :class="{ active: activeCategory.categoryId === category.categoryId }"
             @click="clickCategory(category, $event)"
             @click="clickCategory(category, $event)"
           >
           >
             {{ category.label }}
             {{ category.label }}
@@ -96,7 +97,7 @@
   import huePubSub from 'utils/huePubSub';
   import huePubSub from 'utils/huePubSub';
   import I18n from 'utils/i18n';
   import I18n from 'utils/i18n';
 
 
-  import AutocompleteResults, { Suggestion } from './AutocompleteResults';
+  import { Suggestion } from './AutocompleteResults';
   import MatchedText from './MatchedText.vue';
   import MatchedText from './MatchedText.vue';
   import SqlAutocompleter from './SqlAutocompleter';
   import SqlAutocompleter from './SqlAutocompleter';
   import CatalogEntryDetailsPanel from './details/CatalogEntryDetailsPanel.vue';
   import CatalogEntryDetailsPanel from './details/CatalogEntryDetailsPanel.vue';
@@ -123,11 +124,9 @@
       Spinner,
       Spinner,
       UdfDetailsPanel
       UdfDetailsPanel
     },
     },
-
     directives: {
     directives: {
       'click-outside': clickOutsideDirective
       'click-outside': clickOutsideDirective
     },
     },
-
     props: {
     props: {
       autocompleteParser: {
       autocompleteParser: {
         type: Object as PropType<AutocompleteParser>,
         type: Object as PropType<AutocompleteParser>,
@@ -149,49 +148,33 @@
         type: Object as PropType<Executor>,
         type: Object as PropType<Executor>,
         required: true
         required: true
       },
       },
-
       temporaryOnly: {
       temporaryOnly: {
         type: Boolean,
         type: Boolean,
         required: false,
         required: false,
         default: false
         default: false
       }
       }
     },
     },
+    setup(props) {
+      const subTracker = new SubscriptionTracker();
+
+      const autocompleter = new SqlAutocompleter({
+        editorId: props.editorId,
+        executor: props.executor,
+        editor: props.editor,
+        temporaryOnly: props.temporaryOnly,
+        autocompleteParser: props.autocompleteParser,
+        sqlReferenceProvider: props.sqlReferenceProvider
+      });
 
 
-    setup(): {
-      subTracker: SubscriptionTracker;
-    } {
-      return {
-        subTracker: new SubscriptionTracker()
-      };
-    },
+      const autocompleteResults = autocompleter.autocompleteResults;
+
+      subTracker.addDisposable(autocompleter);
 
 
-    data(): {
-      startLayout: DOMRect | null;
-      startPixelRatio: number;
-      left: number;
-      top: number;
-
-      loading: boolean;
-      active: boolean;
-      filter: string;
-      availableCategories: CategoryInfo[];
-      activeCategory: CategoryInfo;
-      selectedIndex: number | null;
-      hoveredIndex: number | null;
-      base: Ace.Anchor | null;
-      sortOverride?: SortOverride | null;
-      autocompleter?: SqlAutocompleter;
-      autocompleteResults?: AutocompleteResults;
-      suggestions: Suggestion[];
-
-      reTriggerTimeout: number;
-      changeTimeout: number;
-      positionInterval: number;
-      keyboardHandler: Ace.HashHandler | null;
-      changeListener: (() => void) | null;
-    } {
+      return { subTracker, autocompleter, autocompleteResults };
+    },
+    data(component) {
       return {
       return {
-        startLayout: null,
+        startLayout: null as DOMRect | null,
         startPixelRatio: window.devicePixelRatio,
         startPixelRatio: window.devicePixelRatio,
         left: 0,
         left: 0,
         top: 0,
         top: 0,
@@ -199,34 +182,23 @@
         loading: false,
         loading: false,
         active: false,
         active: false,
         filter: '',
         filter: '',
-        availableCategories: [Category.All],
         activeCategory: Category.All,
         activeCategory: Category.All,
-        selectedIndex: null,
-        hoveredIndex: null,
-        base: null,
-        sortOverride: null,
-        suggestions: [],
+        selectedIndex: null as number | null,
+        hoveredIndex: null as number | null,
+        base: null as Ace.Anchor | null,
+        sortOverride: null as SortOverride | null,
+        suggestions: [] as Suggestion[],
 
 
         reTriggerTimeout: -1,
         reTriggerTimeout: -1,
         changeTimeout: -1,
         changeTimeout: -1,
         positionInterval: -1,
         positionInterval: -1,
-        keyboardHandler: null,
-        changeListener: null
-      };
-    },
+        keyboardHandler: null as Ace.HashHandler | null,
+        changeListener: null as (() => void) | null,
 
 
-    data(
-      thisComp
-    ): {
-      mousedownListener: (e: Event) => void;
-      mousewheelListener: (e: Event) => void;
-    } {
-      return {
-        mousedownListener: thisComp.detach.bind(thisComp),
-        mousewheelListener: thisComp.closeOnScroll.bind(thisComp)
+        mousedownListener: component.detach.bind(component),
+        mousewheelListener: component.closeOnScroll.bind(component)
       };
       };
     },
     },
-
     computed: {
     computed: {
       connector(): Connector | undefined {
       connector(): Connector | undefined {
         if (this.executor) {
         if (this.executor) {
@@ -244,7 +216,9 @@
         }
         }
         return undefined;
         return undefined;
       },
       },
-
+      availableCategories(): CategoryInfo[] {
+        return extractCategories(this.suggestions);
+      },
       filtered(): Suggestion[] {
       filtered(): Suggestion[] {
         if (!this.autocompleteResults) {
         if (!this.autocompleteResults) {
           return [];
           return [];
@@ -257,18 +231,9 @@
           huePubSub.publish('hue.ace.autocompleter.match.updated');
           huePubSub.publish('hue.ace.autocompleter.match.updated');
         }
         }
 
 
-        const categories = extractCategories(result);
-        if (categories.indexOf(this.activeCategory) === -1) {
-          // eslint-disable-next-line vue/no-side-effects-in-computed-properties
-          this.activeCategory = Category.All;
-        }
-        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
-        this.availableCategories = categories;
-
         const activeCategory = this.activeCategory;
         const activeCategory = this.activeCategory;
 
 
         const categoriesCount = new Map<CategoryId, number>();
         const categoriesCount = new Map<CategoryId, number>();
-
         result = result.filter(suggestion => {
         result = result.filter(suggestion => {
           categoriesCount.set(
           categoriesCount.set(
             suggestion.category.categoryId,
             suggestion.category.categoryId,
@@ -282,16 +247,15 @@
             return false;
             return false;
           }
           }
           return (
           return (
-            activeCategory === Category.All ||
-            activeCategory === suggestion.category ||
-            (activeCategory === Category.Popular && suggestion.popular)
+            activeCategory.categoryId === Category.All.categoryId ||
+            activeCategory.categoryId === suggestion.category.categoryId ||
+            (activeCategory.categoryId === Category.Popular.categoryId && suggestion.popular)
           );
           );
         });
         });
 
 
         sqlUtils.sortSuggestions(result, this.filter, this.sortOverride);
         sqlUtils.sortSuggestions(result, this.filter, this.sortOverride);
         // eslint-disable-next-line vue/no-side-effects-in-computed-properties
         // eslint-disable-next-line vue/no-side-effects-in-computed-properties
-        this.sortOverride = undefined;
-
+        this.sortOverride = null;
         return result;
         return result;
       },
       },
 
 
@@ -310,7 +274,6 @@
         return this.active && (this.loading || !!this.filtered.length);
         return this.active && (this.loading || !!this.filtered.length);
       }
       }
     },
     },
-
     watch: {
     watch: {
       filter(): void {
       filter(): void {
         if (this.selectedIndex !== null) {
         if (this.selectedIndex !== null) {
@@ -320,9 +283,13 @@
             scrollDiv.scrollTop = 0;
             scrollDiv.scrollTop = 0;
           }
           }
         }
         }
+      },
+      availableCategories(categories): void {
+        if (categories.indexOf(this.activeCategory) === -1) {
+          this.activeCategory = categories[0];
+        }
       }
       }
     },
     },
-
     created(): void {
     created(): void {
       this.keyboardHandler = new HashHandler();
       this.keyboardHandler = new HashHandler();
       this.registerKeybindings(this.keyboardHandler);
       this.registerKeybindings(this.keyboardHandler);
@@ -359,21 +326,7 @@
         }
         }
       };
       };
     },
     },
-
     mounted(): void {
     mounted(): void {
-      this.autocompleter = new SqlAutocompleter({
-        editorId: this.editorId,
-        executor: this.executor,
-        editor: this.editor,
-        temporaryOnly: this.temporaryOnly,
-        autocompleteParser: this.autocompleteParser,
-        sqlReferenceProvider: this.sqlReferenceProvider
-      });
-
-      this.subTracker.addDisposable(this.autocompleter);
-
-      this.autocompleteResults = this.autocompleter.autocompleteResults;
-
       const showAutocomplete = async () => {
       const showAutocomplete = async () => {
         // The autocomplete can be triggered right after insertion of a suggestion
         // The autocomplete can be triggered right after insertion of a suggestion
         // when live autocomplete is enabled, hence if already active we ignore.
         // when live autocomplete is enabled, hence if already active we ignore.

+ 3 - 6
desktop/core/src/desktop/js/apps/editor/components/executionAnalysis/ExecutionAnalysisPanel.vue

@@ -72,7 +72,7 @@
     props: {
     props: {
       executable: {
       executable: {
         type: Object as PropType<Executable>,
         type: Object as PropType<Executable>,
-        default: undefined
+        required: true
       }
       }
     },
     },
 
 
@@ -102,10 +102,7 @@
 
 
     computed: {
     computed: {
       analysisAvailable(): boolean {
       analysisAvailable(): boolean {
-        return (
-          (!!this.executable && this.executable.status !== ExecutionStatus.ready) ||
-          !!this.errors.length
-        );
+        return this.executable.status !== ExecutionStatus.ready || !!this.errors.length;
       },
       },
 
 
       jobsWithUrls(): ExecutionJob[] {
       jobsWithUrls(): ExecutionJob[] {
@@ -143,7 +140,7 @@
     methods: {
     methods: {
       I18n,
       I18n,
       updateFromExecutionLogs(executionLogs: ExecutionLogs): void {
       updateFromExecutionLogs(executionLogs: ExecutionLogs): void {
-        if (this.executable === executionLogs.executable) {
+        if (this.executable.id === executionLogs.executable.id) {
           this.logs = executionLogs.fullLog;
           this.logs = executionLogs.fullLog;
           this.jobs = executionLogs.jobs;
           this.jobs = executionLogs.jobs;
           this.errors = executionLogs.errors;
           this.errors = executionLogs.errors;

+ 12 - 14
desktop/core/src/desktop/js/apps/editor/components/executionAnalysis/ExecutionAnalysisPanelKoBridge.vue

@@ -17,7 +17,11 @@
 -->
 -->
 
 
 <template>
 <template>
-  <ExecutionAnalysisPanel :executable="executable" @execution-error="onExecutionError" />
+  <ExecutionAnalysisPanel
+    v-if="executable"
+    :executable="executable"
+    @execution-error="onExecutionError"
+  />
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
@@ -36,26 +40,20 @@
 
 
     props: {
     props: {
       executableObservable: {
       executableObservable: {
-        type: Object as PropType<KnockoutObservable<SqlExecutable | undefined>>,
-        default: undefined
+        type: Object as PropType<KnockoutObservable<SqlExecutable | undefined>> | null,
+        default: null
       }
       }
     },
     },
 
 
-    setup(): {
-      subTracker: SubscriptionTracker;
-    } {
-      return {
-        subTracker: new SubscriptionTracker()
-      };
+    setup() {
+      const subTracker = new SubscriptionTracker();
+      return { subTracker };
     },
     },
 
 
-    data(): {
-      initialized: boolean;
-      executable: SqlExecutable | null;
-    } {
+    data() {
       return {
       return {
         initialized: false,
         initialized: false,
-        executable: null
+        executable: null as SqlExecutable | null
       };
       };
     },
     },
 
 

+ 14 - 30
desktop/core/src/desktop/js/apps/editor/components/result/ResultTable.vue

@@ -26,7 +26,7 @@
       :sticky-first-column="true"
       :sticky-first-column="true"
       @scroll-to-end="onScrollToEnd"
       @scroll-to-end="onScrollToEnd"
     />
     />
-    <div v-if="isExecuting">
+    <div v-else-if="isExecuting">
       <h1 class="empty"><i class="fa fa-spinner fa-spin" /> {{ I18n('Executing...') }}</h1>
       <h1 class="empty"><i class="fa fa-spinner fa-spin" /> {{ I18n('Executing...') }}</h1>
     </div>
     </div>
     <div v-else-if="hasEmptySuccessResult">
     <div v-else-if="hasEmptySuccessResult">
@@ -74,46 +74,30 @@
     props: {
     props: {
       executable: {
       executable: {
         type: Object as PropType<Executable>,
         type: Object as PropType<Executable>,
-        default: undefined
+        required: true
       }
       }
     },
     },
 
 
-    setup(): {
-      subTracker: SubscriptionTracker;
-    } {
-      return {
-        subTracker: new SubscriptionTracker()
-      };
+    setup() {
+      const subTracker = new SubscriptionTracker();
+      return { subTracker };
     },
     },
 
 
-    data(): {
-      grayedOut: boolean;
-      fetchedOnce: boolean;
-      hasResultSet: boolean;
-      streaming: boolean;
-      hasMore: boolean;
-      rows: ResultRow[];
-      meta: ResultMeta[];
-
-      status: ExecutionStatus | null;
-      type: ResultType;
-      images: [];
-      lastFetchedRows: ResultRow[];
-      lastRenderedResult?: ExecutionResult;
-    } {
+    data() {
       return {
       return {
         grayedOut: false,
         grayedOut: false,
         fetchedOnce: false,
         fetchedOnce: false,
         hasResultSet: false,
         hasResultSet: false,
         streaming: false,
         streaming: false,
         hasMore: false,
         hasMore: false,
-        rows: [],
-        meta: [],
+        rows: [] as ResultRow[],
+        meta: [] as ResultMeta[],
 
 
-        status: null,
+        status: null as ExecutionStatus | null,
         type: ResultType.Table,
         type: ResultType.Table,
-        images: [],
-        lastFetchedRows: []
+        images: [] as string[],
+        lastFetchedRows: [] as ResultRow[],
+        lastRenderedResult: null as ExecutionResult | null
       };
       };
     },
     },
 
 
@@ -165,13 +149,13 @@
 
 
     mounted(): void {
     mounted(): void {
       this.subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, (executable: Executable) => {
       this.subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, (executable: Executable) => {
-        if (this.executable === executable) {
+        if (this.executable.id === executable.id) {
           this.updateFromExecutable(executable);
           this.updateFromExecutable(executable);
         }
         }
       });
       });
 
 
       this.subTracker.subscribe(RESULT_UPDATED_EVENT, (executionResult: ExecutionResult) => {
       this.subTracker.subscribe(RESULT_UPDATED_EVENT, (executionResult: ExecutionResult) => {
-        if (this.executable === executionResult.executable) {
+        if (this.executable.id === executionResult.executable.id) {
           this.handleResultChange();
           this.handleResultChange();
         }
         }
       });
       });

+ 6 - 9
desktop/core/src/desktop/js/apps/editor/components/result/ResultTableKoBridge.vue

@@ -17,7 +17,7 @@
 -->
 -->
 
 
 <template>
 <template>
-  <ResultTable :executable="executable" />
+  <ResultTable v-if="executable" :executable="executable" />
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
@@ -36,17 +36,14 @@
 
 
     props: {
     props: {
       executableObservable: {
       executableObservable: {
-        type: Object as PropType<KnockoutObservable<SqlExecutable | undefined>>,
-        default: undefined
+        type: Object as PropType<KnockoutObservable<SqlExecutable | null>> | null,
+        default: null
       }
       }
     },
     },
 
 
-    setup(): {
-      subTracker: SubscriptionTracker;
-    } {
-      return {
-        subTracker: new SubscriptionTracker()
-      };
+    setup() {
+      const subTracker = new SubscriptionTracker();
+      return { subTracker };
     },
     },
 
 
     data(): {
     data(): {

+ 52 - 53
desktop/core/src/desktop/js/components/HueTable.vue

@@ -62,69 +62,68 @@
 <!-- eslint-enable vue/no-v-html -->
 <!-- eslint-enable vue/no-v-html -->
 
 
 <script lang="ts">
 <script lang="ts">
-  import { defineComponent, PropType, Component } from 'vue';
+  import { defineComponent, PropType } from 'vue';
 
 
   import { Column, Row } from './HueTable';
   import { Column, Row } from './HueTable';
 
 
-  export default <T>(): Component =>
-    defineComponent({
-      props: {
-        rows: {
-          type: Object as PropType<Row[]>,
-          required: false,
-          default: () => []
-        },
-        columns: {
-          type: Object as PropType<Column<T>[]>,
-          required: false,
-          default: () => []
-        },
-        caption: {
-          type: String,
-          default: undefined
-        },
-        stickyHeader: {
-          type: Boolean,
-          required: false,
-          default: false
-        },
-        stickyFirstColumn: {
-          type: Boolean,
-          required: false,
-          default: false
-        }
+  export default defineComponent({
+    props: {
+      rows: {
+        type: Object as PropType<Row[]>,
+        required: false,
+        default: () => []
+      },
+      columns: {
+        type: Object as PropType<Column<unknown>[]>,
+        required: false,
+        default: () => []
+      },
+      caption: {
+        type: String,
+        default: undefined
+      },
+      stickyHeader: {
+        type: Boolean,
+        required: false,
+        default: false
       },
       },
+      stickyFirstColumn: {
+        type: Boolean,
+        required: false,
+        default: false
+      }
+    },
 
 
-      emits: ['scroll-to-end', 'row-clicked'],
+    emits: ['scroll-to-end', 'row-clicked'],
 
 
-      methods: {
-        hasCellSlot(column: Column<T>): boolean {
-          return !!this.$slots[this.cellSlotName(column)];
-        },
+    methods: {
+      hasCellSlot(column: Column<unknown>): boolean {
+        return !!this.$slots[this.cellSlotName(column)];
+      },
 
 
-        cellSlotName(column: Column<T>): string {
-          return 'cell-' + column.key;
-        },
+      cellSlotName(column: Column<unknown>): string {
+        return 'cell-' + column.key;
+      },
 
 
-        onContainerScroll(): void {
-          const containerEl = <HTMLElement>this.$refs.tableContainer;
-          if (containerEl.scrollHeight === containerEl.scrollTop + containerEl.clientHeight) {
-            this.$emit('scroll-to-end');
-          }
-        },
-
-        cellClass(cellClass: string | undefined, index: number): string | null {
-          // This prevents rendering of empty class="" for :class="[x,y]" when x and y are undefined
-          // Possibly fixed in Vue 3
-          if (cellClass && this.stickyFirstColumn && index === 0) {
-            return `${cellClass} sticky-first-col`;
-          } else if (this.stickyFirstColumn && index === 0) {
-            return 'sticky-first-col';
-          }
-          return cellClass || null;
+      onContainerScroll(): void {
+        const containerEl = <HTMLElement>this.$refs.tableContainer;
+        if (containerEl.scrollHeight === containerEl.scrollTop + containerEl.clientHeight) {
+          this.$emit('scroll-to-end');
+        }
+      },
+
+      cellClass(cellClass: string | undefined, index: number): string | null {
+        // This prevents rendering of empty class="" for :class="[x,y]" when x and y are undefined
+        // Possibly fixed in Vue 3
+        if (cellClass && this.stickyFirstColumn && index === 0) {
+          return `${cellClass} sticky-first-col`;
+        } else if (this.stickyFirstColumn && index === 0) {
+          return 'sticky-first-col';
         }
         }
+        return cellClass || null;
       }
       }
-    });
+    }
+  });
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>

+ 1 - 1
desktop/core/src/desktop/js/ko/components/contextPopover/ko.quickQueryContext.js

@@ -85,7 +85,7 @@ const TEMPLATE = `
         }
         }
       "></div>
       "></div>
       <executable-actions-ko-bridge data-bind="vueKoProps: {
       <executable-actions-ko-bridge data-bind="vueKoProps: {
-          executableObservable: $parent.activeExecutable
+          'executable-observable': $parent.activeExecutable
         }"></executable-actions-ko-bridge>
         }"></executable-actions-ko-bridge>
       <div data-bind="
       <div data-bind="
         component: {
         component: {

+ 2 - 1
desktop/core/src/desktop/js/sql/sqlWorkerHandler.js

@@ -75,7 +75,8 @@ export default {
             whenWorkerIsReady(worker, message);
             whenWorkerIsReady(worker, message);
           }, 500);
           }, 500);
         } else {
         } else {
-          worker.postMessage(message);
+          // To JSON and back as Vue creates proxy objects with methods which are not serializable
+          worker.postMessage(JSON.parse(JSON.stringify(message)));
         }
         }
       };
       };
 
 

+ 12 - 12
desktop/libs/notebook/src/notebook/templates/editor2.mako

@@ -322,10 +322,10 @@
       },
       },
       vueKoProps: {
       vueKoProps: {
         executor: executor,
         executor: executor,
-        valueObservable: statement_raw,
-        cursorPositionObservable: aceCursorPosition,
-        idObservable: id,
-        aceOptions: {
+        'value-observable': statement_raw,
+        'cursor-position-observable': aceCursorPosition,
+        'id-observable': id,
+        'ace-options': {
           showLineNumbers: $root.editorMode(),
           showLineNumbers: $root.editorMode(),
           showGutter: $root.editorMode(),
           showGutter: $root.editorMode(),
           maxLines: $root.editorMode() ? null : 25,
           maxLines: $root.editorMode() ? null : 25,
@@ -584,7 +584,7 @@
 
 
 <script type="text/html" id="editor-snippet-code-resizer">
 <script type="text/html" id="editor-snippet-code-resizer">
   <editor-resizer-ko-bridge data-bind="vueKoProps: {
   <editor-resizer-ko-bridge data-bind="vueKoProps: {
-      editorObservable: ace
+      'editor-observable': ace
     }"></editor-resizer-ko-bridge>
     }"></editor-resizer-ko-bridge>
 </script>
 </script>
 
 
@@ -607,8 +607,8 @@
   <div class="snippet-actions" style="padding: 5px;">
   <div class="snippet-actions" style="padding: 5px;">
     <div class="pull-left">
     <div class="pull-left">
       <executable-actions-ko-bridge data-bind="vueKoProps: {
       <executable-actions-ko-bridge data-bind="vueKoProps: {
-          executableObservable: activeExecutable,
-          beforeExecute: beforeExecute
+          'executable-observable': activeExecutable,
+          'before-execute': beforeExecute
         }"></executable-actions-ko-bridge>
         }"></executable-actions-ko-bridge>
     </div>
     </div>
     <!-- ko if: isSqlDialect() && !$root.isPresentationMode() -->
     <!-- ko if: isSqlDialect() && !$root.isPresentationMode() -->
@@ -875,8 +875,8 @@
         },
         },
         vueKoProps: {
         vueKoProps: {
           executor: executor,
           executor: executor,
-          titleObservable: parentNotebook.name,
-          descriptionObservable: parentNotebook.description
+          'title-observable': parentNotebook.name,
+          'description-observable': parentNotebook.description
         }
         }
       "></presentation-mode-ko-bridge>
       "></presentation-mode-ko-bridge>
     <!-- /ko -->
     <!-- /ko -->
@@ -907,7 +907,7 @@
               }
               }
             },
             },
             vueKoProps: {
             vueKoProps: {
-              initialVariables: executor.variables
+              'initial-variables': executor.variables
             }
             }
           "></variable-substitution-ko-bridge>
           "></variable-substitution-ko-bridge>
   ##      <!-- ko template: { name: 'editor-executable-snippet-body' } --><!-- /ko -->
   ##      <!-- ko template: { name: 'editor-executable-snippet-body' } --><!-- /ko -->
@@ -992,7 +992,7 @@
           <div class="tab-pane" id="queryResults" data-bind="css: {'active': currentQueryTab() == 'queryResults'}">
           <div class="tab-pane" id="queryResults" data-bind="css: {'active': currentQueryTab() == 'queryResults'}">
             <div class="execution-results-tab-panel">
             <div class="execution-results-tab-panel">
               <result-table-ko-bridge class="table-results-bridge" data-bind="vueKoProps: {
               <result-table-ko-bridge class="table-results-bridge" data-bind="vueKoProps: {
-                  executableObservable: activeExecutable
+                  'executable-observable': activeExecutable
                 }"></result-table-ko-bridge>
                 }"></result-table-ko-bridge>
             </div>
             </div>
           </div>
           </div>
@@ -1023,7 +1023,7 @@
             <div class="execution-analysis-tab-panel">
             <div class="execution-analysis-tab-panel">
               <execution-analysis-panel-ko-bridge class="execution-analysis-bridge" data-bind="
               <execution-analysis-panel-ko-bridge class="execution-analysis-bridge" data-bind="
                 vueKoProps: {
                 vueKoProps: {
-                  executableObservable: activeExecutable
+                  'executable-observable': activeExecutable
                 },
                 },
                 vueEvents: {
                 vueEvents: {
                   'execution-error': function () { currentQueryTab('executionAnalysis') }
                   'execution-error': function () { currentQueryTab('executionAnalysis') }