Skip to content

Commit ea31a3b

Browse files
committed
refac
1 parent 50d3c92 commit ea31a3b

1 file changed

Lines changed: 55 additions & 16 deletions

File tree

src/lib/components/chat/ModelSelector/Selector.svelte

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
let show = false;
7575
let triggerElement: HTMLElement | null = null;
7676
let contentElement: HTMLElement | null = null;
77+
let panelElement: HTMLElement | null = null;
7778
let dropdownPosition = { top: 0, left: 0, maxHeight: undefined as number | undefined };
7879
let positionFrame: number | undefined;
7980
let settleTimers: number[] = [];
@@ -87,6 +88,19 @@
8788
};
8889
};
8990
91+
const measureContent = () => {
92+
if (!contentElement) return { width: 0, height: 0 };
93+
94+
const previousMaxHeight = panelElement?.style.maxHeight;
95+
if (panelElement) panelElement.style.maxHeight = '';
96+
const rect = contentElement.getBoundingClientRect();
97+
if (panelElement && previousMaxHeight !== undefined) {
98+
panelElement.style.maxHeight = previousMaxHeight;
99+
}
100+
101+
return { width: rect.width, height: rect.height };
102+
};
103+
90104
const visualViewportRect = () => {
91105
const viewport = window.visualViewport;
92106
return {
@@ -100,9 +114,7 @@
100114
const updatePosition = () => {
101115
if (!show || !triggerElement) return;
102116
const rect = triggerElement.getBoundingClientRect();
103-
const contentRect = contentElement?.getBoundingClientRect();
104-
const contentWidth = contentRect?.width ?? 0;
105-
const contentHeight = contentRect?.height ?? 0;
117+
const { width: contentWidth, height: contentHeight } = measureContent();
106118
const viewport = visualViewportRect();
107119
const viewportRight = viewport.left + viewport.width;
108120
const viewportBottom = viewport.top + viewport.height;
@@ -155,6 +167,11 @@
155167
}
156168
};
157169
170+
const handleScroll = (event: Event) => {
171+
if (event.target instanceof Node && contentElement?.contains(event.target)) return;
172+
schedulePositionUpdate();
173+
};
174+
158175
const toggleOpen = async () => {
159176
show = !show;
160177
if (show) {
@@ -550,14 +567,14 @@
550567
tags = Array.from(new Set(tags)).sort((a, b) => a.localeCompare(b));
551568
}
552569
553-
window.addEventListener('scroll', schedulePositionUpdate, true);
570+
window.addEventListener('scroll', handleScroll, true);
554571
window.visualViewport?.addEventListener('resize', scheduleSettledPositionUpdates);
555572
window.visualViewport?.addEventListener('scroll', schedulePositionUpdate);
556573
557574
return () => {
558575
if (positionFrame != null) cancelAnimationFrame(positionFrame);
559576
for (const timer of settleTimers) window.clearTimeout(timer);
560-
window.removeEventListener('scroll', schedulePositionUpdate, true);
577+
window.removeEventListener('scroll', handleScroll, true);
561578
window.visualViewport?.removeEventListener('resize', scheduleSettledPositionUpdates);
562579
window.visualViewport?.removeEventListener('scroll', schedulePositionUpdate);
563580
};
@@ -642,7 +659,28 @@
642659
643660
let listScrollTop = 0;
644661
let listContainer;
645-
$: listViewportHeight = Math.min(288, Math.max(96, (dropdownPosition.maxHeight ?? 352) - 64));
662+
let listViewportHeight = 288;
663+
664+
const trackListViewport = (node: HTMLElement) => {
665+
const updateHeight = () => {
666+
listViewportHeight = node.clientHeight || 288;
667+
};
668+
669+
updateHeight();
670+
671+
if (!('ResizeObserver' in window)) {
672+
return { destroy() {} };
673+
}
674+
675+
const observer = new ResizeObserver(updateHeight);
676+
observer.observe(node);
677+
678+
return {
679+
destroy() {
680+
observer.disconnect();
681+
}
682+
};
683+
};
646684
647685
$: visibleStart = Math.max(0, Math.floor(listScrollTop / ITEM_HEIGHT) - OVERSCAN);
648686
$: visibleEnd = Math.min(
@@ -707,17 +745,17 @@
707745
<div
708746
use:portal
709747
bind:this={contentElement}
710-
style="position: fixed; z-index: 9999; top: {dropdownPosition.top}px; left: {dropdownPosition.left}px; {dropdownPosition.maxHeight
711-
? `max-height: ${dropdownPosition.maxHeight}px;`
712-
: ''}"
748+
style="position: fixed; z-index: 9999; top: {dropdownPosition.top}px; left: {dropdownPosition.left}px;"
713749
>
714750
<div
715-
class="z-40 {className} max-w-[calc(100vw-1rem)] justify-start rounded-xl border border-gray-100 bg-white p-0.5 shadow-lg outline-hidden dark:border-gray-800 dark:bg-gray-850 dark:text-white"
751+
bind:this={panelElement}
752+
class="z-40 {className} max-w-[calc(100vw-1rem)] justify-start rounded-xl border border-gray-100 bg-white p-0.5 shadow-lg outline-hidden dark:border-gray-800 dark:bg-gray-850 dark:text-white flex flex-col overflow-hidden"
753+
style={dropdownPosition.maxHeight ? `max-height: ${dropdownPosition.maxHeight}px;` : ''}
716754
transition:flyAndScale
717755
>
718756
<slot>
719757
{#if searchEnabled}
720-
<div class="my-0.5 flex h-[1.6875rem] items-center gap-2">
758+
<div class="my-0.5 flex h-[1.6875rem] shrink-0 items-center gap-2">
721759
<Search className="ml-2 size-3.5 shrink-0" strokeWidth="2" />
722760

723761
<input
@@ -788,7 +826,7 @@
788826
</div>
789827
{/if}
790828

791-
<div class="group relative">
829+
<div class="group relative flex min-h-0 flex-1 flex-col">
792830
{#if filteredItems.length === 0}
793831
{#if items.length === 0 && $user?.role === 'admin'}
794832
<div class="flex flex-col items-start justify-center py-6 px-4 text-start">
@@ -818,11 +856,12 @@
818856
{:else}
819857
<!-- svelte-ignore a11y-no-static-element-interactions -->
820858
<div
821-
class="overflow-y-auto"
822-
style="max-height: {listViewportHeight}px;"
859+
class="min-h-0 flex-1 overflow-y-auto"
860+
style="max-height: 288px;"
823861
role="listbox"
824862
aria-label={$i18n.t('Available models')}
825863
bind:this={listContainer}
864+
use:trackListViewport
826865
on:scroll={() => {
827866
listScrollTop = listContainer.scrollTop;
828867
}}
@@ -936,7 +975,7 @@
936975
</div>
937976

938977
{#if showSetDefault}
939-
<div class="flex items-center justify-end px-2 py-1 leading-none">
978+
<div class="flex shrink-0 items-center justify-end px-2 py-1 leading-none">
940979
<button
941980
type="button"
942981
class="text-[0.65rem] font-normal leading-none text-gray-500 underline-offset-2 transition-colors duration-100 hover:text-gray-700 hover:underline dark:text-gray-500 dark:hover:text-gray-300"
@@ -946,7 +985,7 @@
946985
</button>
947986
</div>
948987
{:else}
949-
<div class="pb-1"></div>
988+
<div class="shrink-0 pb-1"></div>
950989
{/if}
951990

952991
<div class="hidden w-[42rem]" />

0 commit comments

Comments
 (0)