mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 08:01:35 +00:00
fix: teams selection not sticking in workspace settings (#8309)
Fix portal class mismatch in clickOutside that caused premature dropdown closing, and simplify TeamSelector/ChannelSelector state sync to use getter/setter bindings instead of bidirectional $effect chains. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
let {
|
||||
disabled = false,
|
||||
placeholder = 'Select channel',
|
||||
selectedChannel = $bindable(undefined),
|
||||
selectedChannel = $bindable(),
|
||||
containerClass = 'w-64',
|
||||
minWidth = '160px',
|
||||
channels = undefined,
|
||||
@@ -40,8 +40,6 @@
|
||||
let loadedChannels = $state<ChannelItem[]>([])
|
||||
let loadedForTeamId = $state<string | undefined>(undefined)
|
||||
|
||||
let selectedChannelId = $state<string | undefined>(selectedChannel?.channel_id)
|
||||
|
||||
const searchMode = $derived(!channels && !!teamId)
|
||||
|
||||
let displayChannels = $derived.by(() => {
|
||||
@@ -55,21 +53,17 @@
|
||||
return baseChannels
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
const newChannel = selectedChannelId
|
||||
? displayChannels.find((c) => c.channel_id === selectedChannelId)
|
||||
: undefined
|
||||
|
||||
if (newChannel?.channel_id !== selectedChannel?.channel_id) {
|
||||
selectedChannel = newChannel
|
||||
// Single setter to bridge Select's string value -> selectedChannel object.
|
||||
function setSelectedChannelById(newId: string | undefined) {
|
||||
if (newId) {
|
||||
const channel = displayChannels.find((c) => c.channel_id === newId)
|
||||
if (channel && channel.channel_id !== selectedChannel?.channel_id) {
|
||||
selectedChannel = channel
|
||||
}
|
||||
} else if (selectedChannel !== undefined) {
|
||||
selectedChannel = undefined
|
||||
}
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (selectedChannel?.channel_id !== selectedChannelId) {
|
||||
selectedChannelId = selectedChannel?.channel_id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let previousChannelId = $state<string | undefined>(undefined)
|
||||
|
||||
@@ -136,7 +130,10 @@
|
||||
clearable
|
||||
disabled={disabled || !teamId}
|
||||
loading={isFetching}
|
||||
bind:value={selectedChannelId}
|
||||
bind:value={
|
||||
() => selectedChannel?.channel_id,
|
||||
(newId) => setSelectedChannelById(newId)
|
||||
}
|
||||
/>
|
||||
{:else}
|
||||
<Select
|
||||
@@ -150,7 +147,10 @@
|
||||
{placeholder}
|
||||
clearable
|
||||
disabled={disabled || displayChannels.length === 0}
|
||||
bind:value={selectedChannelId}
|
||||
bind:value={
|
||||
() => selectedChannel?.channel_id,
|
||||
(newId) => setSelectedChannelById(newId)
|
||||
}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -45,8 +45,6 @@
|
||||
let preSearchNextLink = $state<string | null>(null)
|
||||
let preSearchTotalCount = $state(0)
|
||||
|
||||
let selectedTeamId = $state<string | undefined>(selectedTeam?.team_id)
|
||||
|
||||
const searchMode = $derived(!teams)
|
||||
|
||||
// Check if there are more teams to load (based on next_link presence)
|
||||
@@ -62,21 +60,18 @@
|
||||
return baseTeams
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
const newTeam = selectedTeamId
|
||||
? displayTeams.find((t) => t.team_id === selectedTeamId)
|
||||
: undefined
|
||||
|
||||
if (newTeam?.team_id !== selectedTeam?.team_id) {
|
||||
selectedTeam = newTeam
|
||||
// Single getter/setter to bridge Select's string value ↔ selectedTeam object.
|
||||
// This replaces the previous two bidirectional $effect sync blocks.
|
||||
function setSelectedTeamById(newId: string | undefined) {
|
||||
if (newId) {
|
||||
const team = displayTeams.find((t) => t.team_id === newId)
|
||||
if (team && team.team_id !== selectedTeam?.team_id) {
|
||||
selectedTeam = team
|
||||
}
|
||||
} else if (selectedTeam !== undefined) {
|
||||
selectedTeam = undefined
|
||||
}
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (selectedTeam?.team_id !== selectedTeamId) {
|
||||
selectedTeamId = selectedTeam?.team_id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let previousTeamId = $state<string | undefined>(undefined)
|
||||
|
||||
@@ -120,6 +115,7 @@
|
||||
})
|
||||
|
||||
function restorePreSearchState() {
|
||||
debouncedSearch.clearDebounce()
|
||||
searchRequestId++ // Invalidate any in-flight search
|
||||
if (preSearchTeams !== null) {
|
||||
// Restore the accumulated teams from before the search
|
||||
@@ -262,7 +258,10 @@
|
||||
disabled={disabled || isFetching}
|
||||
loading={isFetching}
|
||||
bind:filterText={searchFilterText}
|
||||
bind:value={selectedTeamId}
|
||||
bind:value={
|
||||
() => selectedTeam?.team_id,
|
||||
(newId) => setSelectedTeamById(newId)
|
||||
}
|
||||
/>
|
||||
{:else}
|
||||
<Select
|
||||
@@ -274,7 +273,10 @@
|
||||
placeholder="Select a team"
|
||||
clearable
|
||||
disabled={disabled || isFetching}
|
||||
bind:value={selectedTeamId}
|
||||
bind:value={
|
||||
() => selectedTeam?.team_id,
|
||||
(newId) => setSelectedTeamById(newId)
|
||||
}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -293,7 +293,7 @@ export function validatePassword(password: string): boolean {
|
||||
return re.test(password)
|
||||
}
|
||||
|
||||
const portalDivs = ['#app-editor-select', '.select-dropdown-portal', '[data-context-menu]']
|
||||
const portalDivs = ['#app-editor-select', '.dropdown-portal', '[data-context-menu]']
|
||||
|
||||
interface ClickOutsideOptions {
|
||||
capture?: boolean
|
||||
|
||||
Reference in New Issue
Block a user