mirror of
https://github.com/enso-org/enso.git
synced 2024-11-26 08:52:58 +03:00
CB preview debounce (#8909)
To make engine's life a bit easier, we added 200ms debounce to CB preview updates.
This commit is contained in:
parent
9a2bb19a89
commit
ed65af7005
@ -52,10 +52,11 @@ test('Different ways of opening Component Browser', async ({ page }) => {
|
||||
await page.mouse.click(40, 300)
|
||||
await expectAndCancelBrowser('final.')
|
||||
// Double-clicking port
|
||||
// TODO[ao] Without timeout, even the first click would be treated as double due to previous
|
||||
// event. Probably we need a better way to simulate double clicks.
|
||||
await page.waitForTimeout(600)
|
||||
await page.mouse.click(outputPortX, outputPortY)
|
||||
await page.mouse.click(outputPortX, outputPortY)
|
||||
// TODO[ao] the above click is already treated as double (due to previous event)
|
||||
// But perhaps we should have more reliable method of simulating double clicks.
|
||||
// await outputPortArea.dispatchEvent('pointerdown')
|
||||
await expectAndCancelBrowser('final.')
|
||||
})
|
||||
|
||||
|
@ -21,6 +21,7 @@ import { tryGetIndex } from '@/util/data/array'
|
||||
import type { Opt } from '@/util/data/opt'
|
||||
import { allRanges } from '@/util/data/range'
|
||||
import { Vec2 } from '@/util/data/vec2'
|
||||
import { debouncedGetter } from '@/util/reactivity'
|
||||
import type { SuggestionId } from 'shared/languageServerTypes/suggestions'
|
||||
import { computed, onMounted, ref, watch, type ComputedRef, type Ref } from 'vue'
|
||||
|
||||
@ -158,29 +159,9 @@ useEvent(
|
||||
{ capture: true },
|
||||
)
|
||||
|
||||
// === Preview ===
|
||||
|
||||
const inputElement = ref<HTMLElement>()
|
||||
const inputSize = useResizeObserver(inputElement, false)
|
||||
|
||||
const previewedExpression = computed(() => {
|
||||
if (selectedSuggestion.value == null) return input.code.value
|
||||
else return input.inputAfterApplyingSuggestion(selectedSuggestion.value).newCode
|
||||
})
|
||||
|
||||
const previewDataSource: ComputedRef<VisualizationDataSource | undefined> = computed(() => {
|
||||
if (!previewedExpression.value.trim()) return
|
||||
if (!graphStore.methodAst) return
|
||||
const body = graphStore.methodAst.body
|
||||
if (!body) return
|
||||
|
||||
return {
|
||||
type: 'expression',
|
||||
expression: previewedExpression.value,
|
||||
contextId: body.externalId,
|
||||
}
|
||||
})
|
||||
|
||||
// === Components List and Positions ===
|
||||
|
||||
const components = computed(() =>
|
||||
@ -257,6 +238,26 @@ function selectWithoutScrolling(index: number) {
|
||||
selected.value = index
|
||||
}
|
||||
|
||||
// === Preview ===
|
||||
|
||||
const previewedExpression = debouncedGetter(() => {
|
||||
if (selectedSuggestion.value == null) return input.code.value
|
||||
else return input.inputAfterApplyingSuggestion(selectedSuggestion.value).newCode
|
||||
}, 200)
|
||||
|
||||
const previewDataSource: ComputedRef<VisualizationDataSource | undefined> = computed(() => {
|
||||
if (!previewedExpression.value.trim()) return
|
||||
if (!graphStore.methodAst) return
|
||||
const body = graphStore.methodAst.body
|
||||
if (!body) return
|
||||
|
||||
return {
|
||||
type: 'expression',
|
||||
expression: previewedExpression.value,
|
||||
contextId: body.externalId,
|
||||
}
|
||||
})
|
||||
|
||||
// === Scrolling ===
|
||||
|
||||
const scroller = ref<HTMLElement>()
|
||||
|
@ -119,16 +119,37 @@ export function cachedGetter<T>(
|
||||
getter: () => T,
|
||||
equalFn: (a: T, b: T) => boolean = defaultEquality,
|
||||
): Ref<T> {
|
||||
const valueRef = shallowRef<T>()
|
||||
const valueRef = shallowRef<T>(getter())
|
||||
watch(
|
||||
getter,
|
||||
(newValue) => {
|
||||
const oldValue = valueRef.value
|
||||
if (oldValue === undefined || !equalFn(oldValue, newValue)) valueRef.value = newValue
|
||||
if (!equalFn(oldValue, newValue)) valueRef.value = newValue
|
||||
},
|
||||
{ immediate: true, flush: 'sync' },
|
||||
{ flush: 'sync' },
|
||||
)
|
||||
|
||||
// Since the watch is immediate, the value is guaranteed to be assigned at least once this point.
|
||||
return valueRef as Ref<T>
|
||||
return valueRef
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `cachedGetter`, except that any changes will be not applied immediately, but only after
|
||||
* the timer set for `delayMs` milliseconds will expire. If any further update arrives in that
|
||||
* time, the timer is restarted
|
||||
*/
|
||||
export function debouncedGetter<T>(
|
||||
getter: () => T,
|
||||
delayMs: number,
|
||||
equalFn: (a: T, b: T) => boolean = defaultEquality,
|
||||
): Ref<T> {
|
||||
const valueRef = shallowRef<T>(getter())
|
||||
let currentTimer: ReturnType<typeof setTimeout> | undefined
|
||||
watch(getter, (newValue) => {
|
||||
clearTimeout(currentTimer)
|
||||
currentTimer = setTimeout(() => {
|
||||
const oldValue = valueRef.value
|
||||
if (!equalFn(oldValue, newValue)) valueRef.value = newValue
|
||||
}, delayMs)
|
||||
})
|
||||
return valueRef
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user