From 062992bb8b2b5347e383b6039dac99573737c75a Mon Sep 17 00:00:00 2001 From: somebody1234 Date: Mon, 20 Nov 2023 23:23:50 +1000 Subject: [PATCH] Fixes for GUI2 (#8310) Addresses several issues found during book club. - Add placeholder icons for visualizations (see screenshot) - Also add the corresponding export to visualizations, and relevant support in the visualization store and visualization metadata DB. - Show icon both on the visualization selector button, and in each visualization selector entry - Adjust gaps between visualization selector entries - Port table viz fixes (#8102) to Vue - Fix height of table rows to avoid cutting off descenders on letters like `y` and `g` - Make the space and enter keys work on visualization toolbar buttons, if they are selected - `.blur()` code editor when clicking outside of it - And similarly `.blur()` visualization selector - Move selection brush on scroll by scaling the delta of `scrollTop` and `scrollLeft` - Note that mouse position is technically still incorrect when scrolling past the end. I think this is fine - the new behavior is less broken, plus I am not aware of any way to fix this. # Important Notes None --- app/gui2/package.json | 1 + app/gui2/src/assets/icons.svg | 65 ++++++- app/gui2/src/components/CodeEditor.vue | 2 + .../GraphEditor/GraphVisualization.vue | 8 + app/gui2/src/components/SvgIcon.vue | 5 +- .../src/components/VisualizationContainer.vue | 25 ++- .../src/components/VisualizationSelector.vue | 22 ++- .../visualizations/GeoMapVisualization.vue | 1 + .../visualizations/HeatmapVisualization.vue | 1 + .../ImageBase64Visualization.vue | 1 + .../visualizations/JSONVisualization.vue | 1 + .../visualizations/SQLVisualization.vue | 1 + .../ScatterplotVisualization.vue | 1 + .../visualizations/TableVisualization.vue | 163 +++++++----------- .../visualizations/WarningsVisualization.vue | 1 + .../visualizations/dependencyTypesPatches.ts | 9 +- app/gui2/src/providers/visualizationConfig.ts | 3 + .../stores/visualization/compilerMessaging.ts | 16 +- app/gui2/src/stores/visualization/index.ts | 15 +- app/gui2/src/stores/visualization/metadata.ts | 3 +- app/gui2/src/util/autoBlur.ts | 25 +++ app/gui2/src/util/events.ts | 5 + app/gui2/src/util/navigator.ts | 44 ++++- package-lock.json | 24 ++- 24 files changed, 304 insertions(+), 138 deletions(-) create mode 100644 app/gui2/src/util/autoBlur.ts diff --git a/app/gui2/package.json b/app/gui2/package.json index 9b1a15f5e7..a1c553c925 100644 --- a/app/gui2/package.json +++ b/app/gui2/package.json @@ -33,6 +33,7 @@ "@ag-grid-community/core": "^30.2.0", "@ag-grid-community/styles": "^30.2.0", "@ag-grid-enterprise/core": "^30.2.0", + "@ag-grid-enterprise/range-selection": "^30.2.1", "@babel/parser": "^7.22.16", "@fast-check/vitest": "^0.0.8", "@lezer/common": "^1.1.0", diff --git a/app/gui2/src/assets/icons.svg b/app/gui2/src/assets/icons.svg index 9bcabc09d3..10fb1a93da 100644 --- a/app/gui2/src/assets/icons.svg +++ b/app/gui2/src/assets/icons.svg @@ -1,5 +1,5 @@ - + @@ -904,6 +904,22 @@ + + + + + + + + + + + + + + + + @@ -1271,5 +1287,50 @@ fill="black" fill-opacity="0.6" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/gui2/src/components/CodeEditor.vue b/app/gui2/src/components/CodeEditor.vue index d7b55b36e7..5f480d75ab 100644 --- a/app/gui2/src/components/CodeEditor.vue +++ b/app/gui2/src/components/CodeEditor.vue @@ -2,6 +2,7 @@ import { useGraphStore } from '@/stores/graph' import { useProjectStore } from '@/stores/project' import { useSuggestionDbStore } from '@/stores/suggestionDatabase' +import { useAutoBlur } from '@/util/autoBlur' import type { Highlighter } from '@/util/codemirror' import { usePointer } from '@/util/events' import { useLocalStorage } from '@vueuse/core' @@ -30,6 +31,7 @@ const projectStore = useProjectStore() const graphStore = useGraphStore() const suggestionDbStore = useSuggestionDbStore() const rootElement = ref() +useAutoBlur(rootElement) // == CodeMirror editor setup == diff --git a/app/gui2/src/components/GraphEditor/GraphVisualization.vue b/app/gui2/src/components/GraphEditor/GraphVisualization.vue index 3f93184efa..1e35beeccd 100644 --- a/app/gui2/src/components/GraphEditor/GraphVisualization.vue +++ b/app/gui2/src/components/GraphEditor/GraphVisualization.vue @@ -9,7 +9,9 @@ import { useVisualizationStore, type Visualization, } from '@/stores/visualization' +import type { URLString } from '@/stores/visualization/compilerMessaging' import { toError } from '@/util/error' +import type { Icon } from '@/util/iconName' import type { Opt } from '@/util/opt' import type { Vec2 } from '@/util/vec2' import type { ExprId, VisualizationIdentifier } from 'shared/yjsModel' @@ -35,6 +37,7 @@ const emit = defineEmits<{ }>() const visualization = shallowRef() +const icon = ref() onErrorCaptured((vueError) => { error.value = vueError @@ -76,6 +79,7 @@ watch( watchEffect(async () => { if (props.currentType == null) return visualization.value = undefined + icon.value = undefined try { const module = await visualizationStore.get(props.currentType).value if (module) { @@ -85,6 +89,7 @@ watchEffect(async () => { switchToDefaultPreprocessor() } visualization.value = module.default + icon.value = module.icon } else { switch (props.currentType.module.kind) { case 'Builtin': { @@ -128,6 +133,9 @@ provideVisualizationConfig({ get currentType() { return props.currentType ?? DEFAULT_VISUALIZATION_IDENTIFIER }, + get icon() { + return icon.value + }, hide: () => emit('setVisualizationVisible', false), updateType: (id) => emit('setVisualizationId', id), }) diff --git a/app/gui2/src/components/SvgIcon.vue b/app/gui2/src/components/SvgIcon.vue index 854bd38b1c..e9b8a05f79 100644 --- a/app/gui2/src/components/SvgIcon.vue +++ b/app/gui2/src/components/SvgIcon.vue @@ -5,14 +5,15 @@ * It displays one group defined in `@/assets/icons.svg` file, specified by `variant` property. */ import icons from '@/assets/icons.svg' +import type { URLString } from '@/stores/visualization/compilerMessaging' import type { Icon } from '@/util/iconName' -const props = defineProps<{ name: Icon; width?: number; height?: number }>() +const props = defineProps<{ name: Icon | URLString; width?: number; height?: number }>() diff --git a/app/gui2/src/components/VisualizationContainer.vue b/app/gui2/src/components/VisualizationContainer.vue index 17bf60dbf0..57a134d69f 100644 --- a/app/gui2/src/components/VisualizationContainer.vue +++ b/app/gui2/src/components/VisualizationContainer.vue @@ -2,7 +2,7 @@ import SvgIcon from '@/components/SvgIcon.vue' import VisualizationSelector from '@/components/VisualizationSelector.vue' import { useVisualizationConfig } from '@/providers/visualizationConfig' -import { PointerButtonMask, usePointer } from '@/util/events' +import { PointerButtonMask, isClick, usePointer } from '@/util/events' import { ref } from 'vue' const props = defineProps<{ @@ -28,6 +28,17 @@ function onWheel(event: WheelEvent) { } } +function blur(event: Event) { + const target = event.target + if ( + !(target instanceof HTMLElement) && + !(target instanceof SVGElement) && + !(target instanceof MathMLElement) + ) + return + setTimeout(() => target.blur(), 0) +} + const rootNode = ref() const contentNode = ref() @@ -102,14 +113,19 @@ const resizeBottomRight = usePointer((pos, _, type) => { hidden: config.fullscreen, }" > -
@@ -117,8 +133,9 @@ const resizeBottomRight = usePointer((pos, _, type) => { +import SvgIcon from '@/components/SvgIcon.vue' +import { useVisualizationStore } from '@/stores/visualization' +import { useAutoBlur } from '@/util/autoBlur' import { visIdentifierEquals, type VisualizationIdentifier } from 'shared/yjsModel' import { onMounted, ref } from 'vue' @@ -8,7 +11,10 @@ const props = defineProps<{ }>() const emit = defineEmits<{ hide: []; 'update:modelValue': [type: VisualizationIdentifier] }>() +const visualizationStore = useVisualizationStore() + const rootNode = ref() +useAutoBlur(rootNode) function visIdLabel(id: VisualizationIdentifier) { switch (id.module.kind) { @@ -26,9 +32,7 @@ function visIdKey(id: VisualizationIdentifier) { return `${kindKey}::${id.name}` } -onMounted(() => { - setTimeout(() => rootNode.value?.focus(), 0) -}) +onMounted(() => setTimeout(() => rootNode.value?.focus(), 0)) @@ -72,14 +78,18 @@ onMounted(() => { position: relative; } -.VisualizationSelector > ul { +ul { display: flex; flex-flow: column; + gap: 2px; list-style-type: none; padding: 4px; } li { + display: flex; + gap: 4px; + align-items: center; cursor: pointer; padding: 0 8px; border-radius: 12px; diff --git a/app/gui2/src/components/visualizations/GeoMapVisualization.vue b/app/gui2/src/components/visualizations/GeoMapVisualization.vue index 6740d3f23f..e494e91fc4 100644 --- a/app/gui2/src/components/visualizations/GeoMapVisualization.vue +++ b/app/gui2/src/components/visualizations/GeoMapVisualization.vue @@ -1,5 +1,6 @@ diff --git a/app/gui2/src/components/visualizations/SQLVisualization.vue b/app/gui2/src/components/visualizations/SQLVisualization.vue index d45bd3dd52..c8bfecbb37 100644 --- a/app/gui2/src/components/visualizations/SQLVisualization.vue +++ b/app/gui2/src/components/visualizations/SQLVisualization.vue @@ -1,5 +1,6 @@