mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-20 18:51:26 -03:00
fix(ui): re-measure autocomplete scrollbar inset on programmatic value changes
The --lm-vscrollbar-width inset from 634ea7f2 was only refreshed on input
events, mount and mode changes. Programmatic value updates (widget.setValue
from "send lora to workflow", external value-change events) change the
textarea content without an input event, leaving the corner clear (x)
button overlapping a freshly appeared classic scrollbar until the next
keystroke. Mount-time pending value replay was already covered.
- onExternalValueChange and widget.onSetValue now call
updateVScrollbarWidth() alongside the hasText update
- tests: cover both paths by overriding textarea metrics to an overflowing
state and asserting the 15px gutter lands in the CSS var
This commit is contained in:
@@ -200,6 +200,9 @@ const onWheel = (event: WheelEvent) => {
|
|||||||
|
|
||||||
// Handle external value changes (e.g., from "send lora to workflow")
|
// Handle external value changes (e.g., from "send lora to workflow")
|
||||||
const onExternalValueChange = () => {
|
const onExternalValueChange = () => {
|
||||||
|
// The DOM value was set synchronously by the caller; the new content may
|
||||||
|
// have toggled the vertical scrollbar on/off.
|
||||||
|
updateVScrollbarWidth()
|
||||||
updateHasTextState()
|
updateHasTextState()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +212,9 @@ const setupWidgetOnSetValue = () => {
|
|||||||
props.widget.onSetValue = (value: string) => {
|
props.widget.onSetValue = (value: string) => {
|
||||||
// The DOM value is already set by setValue, just update our state
|
// The DOM value is already set by setValue, just update our state
|
||||||
hasText.value = value.length > 0
|
hasText.value = value.length > 0
|
||||||
|
// Programmatic sets can toggle the scrollbar; re-measure so the
|
||||||
|
// corner clear button stays clear of it.
|
||||||
|
updateVScrollbarWidth()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import AutocompleteTextWidget from '@/components/AutocompleteTextWidget.vue'
|
|||||||
function createMockWidget() {
|
function createMockWidget() {
|
||||||
return {
|
return {
|
||||||
callback: vi.fn(),
|
callback: vi.fn(),
|
||||||
onSetValue: undefined,
|
onSetValue: undefined as ((v: string) => void) | undefined,
|
||||||
inputEl: undefined,
|
inputEl: undefined,
|
||||||
metadataWidget: undefined,
|
metadataWidget: undefined,
|
||||||
name: 'text',
|
name: 'text',
|
||||||
@@ -217,4 +217,65 @@ describe('AutocompleteTextWidget vertical scrollbar inset', () => {
|
|||||||
expect(wrapperEl.style.getPropertyValue('--lm-vscrollbar-width')).toBe('0px')
|
expect(wrapperEl.style.getPropertyValue('--lm-vscrollbar-width')).toBe('0px')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('re-measures the inset when the value is set programmatically via onSetValue', async () => {
|
||||||
|
const { wrapper, widget } = mountWidget()
|
||||||
|
const textarea = wrapper.find('textarea').element as HTMLTextAreaElement
|
||||||
|
|
||||||
|
// Start with no overflow
|
||||||
|
overrideTextareaMetrics(textarea, {
|
||||||
|
scrollHeight: 100,
|
||||||
|
clientHeight: 100,
|
||||||
|
offsetWidth: 320,
|
||||||
|
clientWidth: 320,
|
||||||
|
})
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Simulate an external setValue (e.g. "send lora to workflow"): the DOM
|
||||||
|
// value is set by the caller and widget.onSetValue fires without an
|
||||||
|
// input event. Content now overflows → inset must be re-measured.
|
||||||
|
overrideTextareaMetrics(textarea, {
|
||||||
|
scrollHeight: 200,
|
||||||
|
clientHeight: 100,
|
||||||
|
offsetWidth: 320,
|
||||||
|
clientWidth: 305, // 15px scrollbar gutter
|
||||||
|
})
|
||||||
|
if (!widget.onSetValue) throw new Error('onSetValue not installed by component')
|
||||||
|
widget.onSetValue('long content')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const wrapperEl = wrapper.find('.input-wrapper').element as HTMLElement
|
||||||
|
expect(wrapperEl.style.getPropertyValue('--lm-vscrollbar-width')).toBe('15px')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-measures the inset on external value-change events', async () => {
|
||||||
|
const { wrapper } = mountWidget()
|
||||||
|
const textarea = wrapper.find('textarea').element as HTMLTextAreaElement
|
||||||
|
|
||||||
|
// Start with no overflow
|
||||||
|
overrideTextareaMetrics(textarea, {
|
||||||
|
scrollHeight: 100,
|
||||||
|
clientHeight: 100,
|
||||||
|
offsetWidth: 320,
|
||||||
|
clientWidth: 320,
|
||||||
|
})
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// The lora-manager:autocomplete-value-changed event fires when the
|
||||||
|
// widget value is set externally; content now overflows → re-measure.
|
||||||
|
overrideTextareaMetrics(textarea, {
|
||||||
|
scrollHeight: 200,
|
||||||
|
clientHeight: 100,
|
||||||
|
offsetWidth: 320,
|
||||||
|
clientWidth: 305, // 15px scrollbar gutter
|
||||||
|
})
|
||||||
|
textarea.dispatchEvent(
|
||||||
|
new CustomEvent('lora-manager:autocomplete-value-changed', {
|
||||||
|
detail: { value: 'long content' },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const wrapperEl = wrapper.find('.input-wrapper').element as HTMLElement
|
||||||
|
expect(wrapperEl.style.getPropertyValue('--lm-vscrollbar-width')).toBe('15px')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user