mirror of
https://github.com/enso-org/enso.git
synced 2025-01-05 15:42:52 +03:00
Filter visualization types based on node type (#8004)
- Closes #7925 # Important Notes The type -> matching visualization lookup is populated using a `Promise`, so there is a race condition where the selector will incorrectly show the full list of visualizations if it is opened before the `Promise` resolves.
This commit is contained in:
parent
b64a7d392f
commit
62019f6334
@ -20,6 +20,7 @@ import {
|
|||||||
import { colorFromString } from '@/util/colors'
|
import { colorFromString } from '@/util/colors'
|
||||||
import { usePointer, useResizeObserver } from '@/util/events'
|
import { usePointer, useResizeObserver } from '@/util/events'
|
||||||
import { methodNameToIcon, typeNameToIcon } from '@/util/getIconName'
|
import { methodNameToIcon, typeNameToIcon } from '@/util/getIconName'
|
||||||
|
import type { UnsafeMutable } from '@/util/mutable'
|
||||||
import type { Opt } from '@/util/opt'
|
import type { Opt } from '@/util/opt'
|
||||||
import type { Vec2 } from '@/util/vec2'
|
import type { Vec2 } from '@/util/vec2'
|
||||||
import type { ContentRange, ExprId, VisualizationIdentifier } from 'shared/yjsModel'
|
import type { ContentRange, ExprId, VisualizationIdentifier } from 'shared/yjsModel'
|
||||||
@ -307,9 +308,11 @@ function switchToDefaultPreprocessor() {
|
|||||||
visPreprocessor.value = DEFAULT_VISUALIZATION_CONFIGURATION
|
visPreprocessor.value = DEFAULT_VISUALIZATION_CONFIGURATION
|
||||||
}
|
}
|
||||||
|
|
||||||
const visualizationConfig = ref<VisualizationConfig>({
|
// This usage of `UnsafeMutable` is SAFE, as it is being used on a type without an associated value.
|
||||||
|
const visualizationConfig = ref<UnsafeMutable<VisualizationConfig>>({
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
types: visualizationStore.types,
|
// We do not know the type yet.
|
||||||
|
types: visualizationStore.types(undefined),
|
||||||
width: null,
|
width: null,
|
||||||
height: 150,
|
height: 150,
|
||||||
hide() {
|
hide() {
|
||||||
@ -426,6 +429,10 @@ const icon = computed(() => {
|
|||||||
return 'in_out'
|
return 'in_out'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
visualizationConfig.value.types = visualizationStore.types(expressionInfo.value?.typename)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -3,7 +3,7 @@ import { visIdentifierEquals, type VisualizationIdentifier } from 'shared/yjsMod
|
|||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
types: VisualizationIdentifier[]
|
types: readonly VisualizationIdentifier[]
|
||||||
modelValue: VisualizationIdentifier
|
modelValue: VisualizationIdentifier
|
||||||
}>()
|
}>()
|
||||||
const emit = defineEmits<{ hide: []; 'update:modelValue': [type: VisualizationIdentifier] }>()
|
const emit = defineEmits<{ hide: []; 'update:modelValue': [type: VisualizationIdentifier] }>()
|
||||||
|
@ -5,7 +5,7 @@ import { inject, provide, type InjectionKey, type Ref } from 'vue'
|
|||||||
export interface VisualizationConfig {
|
export interface VisualizationConfig {
|
||||||
/** Possible visualization types that can be switched to. */
|
/** Possible visualization types that can be switched to. */
|
||||||
background?: string
|
background?: string
|
||||||
readonly types: VisualizationIdentifier[]
|
readonly types: readonly VisualizationIdentifier[]
|
||||||
readonly currentType: VisualizationIdentifier
|
readonly currentType: VisualizationIdentifier
|
||||||
readonly isCircularMenuVisible: boolean
|
readonly isCircularMenuVisible: boolean
|
||||||
readonly nodeSize: Vec2
|
readonly nodeSize: Vec2
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import * as vue from 'vue'
|
||||||
|
import { reactive, ref, type DefineComponent, type PropType } from 'vue'
|
||||||
|
|
||||||
import VisualizationContainer from '@/components/VisualizationContainer.vue'
|
import VisualizationContainer from '@/components/VisualizationContainer.vue'
|
||||||
import { useVisualizationConfig } from '@/providers/visualizationConfig'
|
import { useVisualizationConfig } from '@/providers/visualizationConfig'
|
||||||
import { defineKeybinds } from '@/util/shortcuts'
|
import { defineKeybinds } from '@/util/shortcuts'
|
||||||
@ -18,8 +21,6 @@ import Compiler from '@/workers/visualizationCompiler?worker'
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import type { VisualizationConfiguration } from 'shared/languageServerTypes'
|
import type { VisualizationConfiguration } from 'shared/languageServerTypes'
|
||||||
import type { VisualizationIdentifier } from 'shared/yjsModel'
|
import type { VisualizationIdentifier } from 'shared/yjsModel'
|
||||||
import * as vue from 'vue'
|
|
||||||
import { type DefineComponent } from 'vue'
|
|
||||||
|
|
||||||
/** A module containing the default visualization function. */
|
/** A module containing the default visualization function. */
|
||||||
const DEFAULT_VISUALIZATION_MODULE = 'Standard.Visualization.Preprocessor'
|
const DEFAULT_VISUALIZATION_MODULE = 'Standard.Visualization.Preprocessor'
|
||||||
@ -52,7 +53,7 @@ window.__visualizationModules = moduleCache
|
|||||||
|
|
||||||
export type Visualization = DefineComponent<
|
export type Visualization = DefineComponent<
|
||||||
// Props
|
// Props
|
||||||
{ data: { type: vue.PropType<unknown>; required: true } },
|
{ data: { type: PropType<unknown>; required: true } },
|
||||||
{},
|
{},
|
||||||
unknown,
|
unknown,
|
||||||
{},
|
{},
|
||||||
@ -89,23 +90,73 @@ const dynamicVisualizationPaths: Record<string, string> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useVisualizationStore = defineStore('visualization', () => {
|
export const useVisualizationStore = defineStore('visualization', () => {
|
||||||
// TODO [sb]: Figure out how to list visualizations defined by a project.
|
|
||||||
const imports = { ...builtinVisualizationImports }
|
const imports = { ...builtinVisualizationImports }
|
||||||
const paths = { ...dynamicVisualizationPaths }
|
const paths = { ...dynamicVisualizationPaths }
|
||||||
let cache: Record<string, VisualizationModule> = {}
|
let cache: Record<string, VisualizationModule> = {}
|
||||||
const builtinTypes = [...Object.keys(imports), ...Object.keys(paths)]
|
|
||||||
const types = builtinTypes.map(
|
|
||||||
(name): VisualizationIdentifier => ({
|
|
||||||
module: { kind: 'Builtin' },
|
|
||||||
name,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
let worker: Worker | undefined
|
let worker: Worker | undefined
|
||||||
let workerMessageId = 0
|
let workerMessageId = 0
|
||||||
const workerCallbacks: Record<
|
const workerCallbacks: Record<
|
||||||
string,
|
string,
|
||||||
{ resolve: (result: VisualizationModule) => void; reject: () => void }
|
{ resolve: (result: VisualizationModule) => void; reject: () => void }
|
||||||
> = {}
|
> = {}
|
||||||
|
const allVisualizations = [
|
||||||
|
...Object.keys(imports),
|
||||||
|
...Object.keys(paths),
|
||||||
|
].map<VisualizationIdentifier>((name) => ({
|
||||||
|
module: { kind: 'Builtin' },
|
||||||
|
name,
|
||||||
|
}))
|
||||||
|
const visualizationsForType = reactive(new Map<string, readonly VisualizationIdentifier[]>())
|
||||||
|
const visualizationsForAny = ref<readonly VisualizationIdentifier[]>([])
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
...Object.values(builtinVisualizationImports).map((importer) => importer()),
|
||||||
|
...Object.values(dynamicVisualizationPaths).map(compile),
|
||||||
|
])
|
||||||
|
.then((modules) =>
|
||||||
|
Object.fromEntries(
|
||||||
|
modules.map((module) => [
|
||||||
|
module.name,
|
||||||
|
new Set(
|
||||||
|
module.inputType == null
|
||||||
|
? ['Any']
|
||||||
|
: module.inputType.split('|').map((type) => type.trim()),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.then((moduleInputTypes) => {
|
||||||
|
const types = Object.values(moduleInputTypes).flatMap((set) => Array.from(set))
|
||||||
|
for (const type of types) {
|
||||||
|
if (visualizationsForType.has(type)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const matchingTypes = Object.entries(moduleInputTypes).flatMap<VisualizationIdentifier>(
|
||||||
|
([name, inputTypes]) =>
|
||||||
|
inputTypes.has(type) || inputTypes.has('Any')
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
module: { kind: 'Builtin' },
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
)
|
||||||
|
if (type === 'Any') {
|
||||||
|
visualizationsForAny.value = matchingTypes
|
||||||
|
}
|
||||||
|
visualizationsForType.set(type, matchingTypes)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function types(type: string | undefined) {
|
||||||
|
const ret =
|
||||||
|
type === undefined
|
||||||
|
? allVisualizations
|
||||||
|
: visualizationsForType.get(type) ?? visualizationsForAny.value
|
||||||
|
console.log(type, ret, allVisualizations, visualizationsForType, visualizationsForAny)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
function register(module: VisualizationModule) {
|
function register(module: VisualizationModule) {
|
||||||
console.log(`registering visualization: name=${module.name}, inputType=${module.inputType}`)
|
console.log(`registering visualization: name=${module.name}, inputType=${module.inputType}`)
|
||||||
|
2
app/gui2/src/util/mutable.ts
Normal file
2
app/gui2/src/util/mutable.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/** Removes `readonly` from all keys in a type. UNSAFE. */
|
||||||
|
export type UnsafeMutable<T> = { -readonly [K in keyof T]: T[K] }
|
Loading…
Reference in New Issue
Block a user