-
-
Notifications
You must be signed in to change notification settings - Fork 456
fix(lit-virtual): add setOptions to VirtualizerController for reactive option updates #1253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,12 +18,15 @@ class VirtualizerControllerBase< | |
|
|
||
| private readonly virtualizer: Virtualizer<TScrollElement, TItemElement> | ||
|
|
||
| private options: VirtualizerOptions<TScrollElement, TItemElement> | ||
|
|
||
| private cleanup: () => void = () => {} | ||
|
|
||
| constructor( | ||
| host: ReactiveControllerHost, | ||
| options: VirtualizerOptions<TScrollElement, TItemElement>, | ||
| ) { | ||
| this.options = options | ||
| const resolvedOptions: VirtualizerOptions<TScrollElement, TItemElement> = { | ||
| ...options, | ||
| onChange: (instance, sync) => { | ||
|
|
@@ -39,6 +42,21 @@ class VirtualizerControllerBase< | |
| return this.virtualizer | ||
| } | ||
|
|
||
| public setOptions( | ||
| options: Partial<VirtualizerOptions<TScrollElement, TItemElement>>, | ||
| ) { | ||
| this.options = { ...this.options, ...options } | ||
| const resolvedOptions: VirtualizerOptions<TScrollElement, TItemElement> = { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||
| ...this.options, | ||
| onChange: (instance, sync) => { | ||
| this.host.updateComplete.then(() => this.host.requestUpdate()) | ||
| this.options.onChange?.(instance, sync) | ||
| }, | ||
| } | ||
| this.virtualizer.setOptions(resolvedOptions) | ||
| this.virtualizer._willUpdate() | ||
| } | ||
|
|
||
| hostConnected() { | ||
| this.cleanup = this.virtualizer._didMount() | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -113,3 +113,86 @@ test('should render virtual items', async () => { | |
| 'Element did not render virtual items', | ||
| ) | ||
| }) | ||
|
|
||
| test('should apply updated options via setOptions', async () => { | ||
| @customElement('test-list-setoptions' as any) | ||
| class ListSetOptions extends LitElement { | ||
| private scrollElementRef: Ref<HTMLDivElement> = createRef() | ||
|
|
||
| private virtualizerController: VirtualizerController< | ||
| HTMLDivElement, | ||
| Element | ||
| > | ||
|
|
||
| constructor() { | ||
| super() | ||
| this.virtualizerController = new VirtualizerController(this, { | ||
| getScrollElement: () => this.scrollElementRef.value, | ||
| count: 10, | ||
| estimateSize: () => 50, | ||
| observeElementRect: (_, cb) => { | ||
| cb({ height, width }) | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| render() { | ||
| this.virtualizerController.setOptions({ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling Driving it from a reactive |
||
| count: 20, | ||
| }) | ||
| const virtualizer = this.virtualizerController.getVirtualizer() | ||
| const virtualRows = virtualizer.getVirtualItems() | ||
| return html` | ||
| <div class="list scroll-container" ${ref(this.scrollElementRef)}> | ||
| <div | ||
| style="position: relative; height: ${virtualizer.getTotalSize()}px; width: 100%;" | ||
| > | ||
| <div | ||
| style="position:absolute;top:0;left:0;width:100%;transform:translateY(${virtualRows[0] | ||
| ? virtualRows[0].start | ||
| : 0}px);" | ||
| > | ||
| ${repeat( | ||
| virtualRows, | ||
| (virtualRow: any) => virtualRow.key, | ||
| (virtualRow: any) => | ||
| html` <div | ||
| data-index="${virtualRow.index}" | ||
| class="${virtualRow.index % 2 === 0 | ||
| ? 'list-item-even' | ||
| : 'list-item-odd'}" | ||
| > | ||
| <div style="padding: 10px 0;"> | ||
| <div>Row ${virtualRow.index}</div> | ||
| <div>Item ${virtualRow.index}</div> | ||
| </div> | ||
| </div>`, | ||
| )} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <style> | ||
| .list { | ||
| border: 1px solid #e6e4dc; | ||
| max-width: 100%; | ||
| } | ||
| .scroll-container { | ||
| height: ${height}px; | ||
| width: ${width}px; | ||
| overflow-y: auto; | ||
| } | ||
| </style> | ||
| ` | ||
| } | ||
| } | ||
|
|
||
| const el = await fixture( | ||
| html`<test-list-setoptions></test-list-setoptions>` as any, | ||
| ) | ||
| await elementUpdated(el) | ||
| await waitUntil( | ||
| () => el.shadowRoot.querySelector('[data-index="15"]'), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is what fails. Index 15 is never rendered: the viewport is 400px with
The follow-up |
||
| 'Element did not render items beyond initial count of 10', | ||
| ) | ||
| expect(el.shadowRoot.querySelector('[data-index="15"]')).toBeTruthy() | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A plain spread lets an explicit
undefinedwipe a stored option:setOptions({ estimateSize: undefined })leavesestimateSizeundefined for good, and core then falls back to its own default rather than the value set earlier.Core's
setOptionsguards against exactly this (it skips keys whose value isundefined) — worth matching here.