mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 08:01:35 +00:00
fix(frontend): open the template picker downward and size it from the popover
The popover's default positioning caps its height to the viewport, and the list inside it carried a fixed one, so a capped box overflowed its own frame — visible with the AI composer hidden, where the caption sits high and `placement: top` left almost no room above it. It opens downward now, with flip fallbacks, at a definite `min(72vh, 520px)`; the list fills what the header leaves, which is still the definite height it needs to page. `creating` on the create form becomes `onCreatingChange`: `$bindable(default)` on an optional prop is banned, and this is something the form reports rather than state it shares. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9
This commit is contained in:
co-authored by
Claude Opus 5
parent
5537247082
commit
b3aa4a4cd3
@@ -16,7 +16,6 @@
|
||||
let { onPick }: Props = $props()
|
||||
|
||||
let list: InfiniteList | undefined = $state(undefined)
|
||||
let loaded = $state(0)
|
||||
|
||||
// The hub serves its whole catalogue in one response, so paging happens here: the list
|
||||
// asks for a window and gets a slice of what `hubProjectCatalogue` already holds. Should
|
||||
@@ -46,7 +45,9 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex w-[380px] flex-col">
|
||||
<!-- The popover gives this box a definite height; the list takes what the header leaves and
|
||||
scrolls inside it, which is also what lets it page. -->
|
||||
<div class="flex min-h-0 w-[380px] flex-col">
|
||||
<!-- The hub is named once, as the link to it: a footer row saying the same thing again is
|
||||
a second line spent on somewhere the reader is not going. -->
|
||||
<p class="px-3 pb-2 pt-3 text-[11.5px] leading-snug text-hint">
|
||||
@@ -62,17 +63,8 @@
|
||||
— imported as a folder in this workspace.
|
||||
</p>
|
||||
|
||||
<!-- A definite height is what makes the list page: its scroll container only asks for
|
||||
the next window once it actually scrolls. Given only to a list long enough to need
|
||||
it, so a hub with a handful of projects gets a popover that hugs them. -->
|
||||
<div class="border-t border-border-light">
|
||||
<InfiniteList
|
||||
bind:this={list}
|
||||
bind:length={loaded}
|
||||
noBorder
|
||||
rounded={false}
|
||||
containerClass={loaded > 4 ? 'h-[420px]' : ''}
|
||||
>
|
||||
<div class="min-h-0 flex-1 border-t border-border-light">
|
||||
<InfiniteList bind:this={list} noBorder rounded={false} containerClass="h-full">
|
||||
{#snippet customRow({ item }: { item: HubProjectPick })}
|
||||
{@const Icon = hubAppIcon(item.iconApps[0] ?? '')}
|
||||
<tr>
|
||||
|
||||
@@ -68,9 +68,22 @@
|
||||
class="border-t border-dashed border-border-light px-4 pb-[22px] pt-[18px] text-center text-[13.5px] leading-relaxed text-hint"
|
||||
>
|
||||
Your scripts, flows and apps will show up here.
|
||||
<!-- Opens downward into the page rather than upward into the hero: the caption sits high
|
||||
when the AI composer is hidden, so the room is below it. `fitViewport` caps the box on
|
||||
a short viewport, which is why the height below is definite and the list inside fills
|
||||
it — a squeezed box with a fixed-height list inside overflows its own frame. -->
|
||||
<Popover
|
||||
placement="top"
|
||||
contentClasses="p-0"
|
||||
floatingConfig={{
|
||||
placement: 'bottom',
|
||||
strategy: 'absolute',
|
||||
gutter: 8,
|
||||
overflowPadding: 16,
|
||||
flip: { fallbackPlacements: ['top', 'bottom-start', 'top-start'] },
|
||||
fitViewport: true,
|
||||
overlap: false
|
||||
}}
|
||||
contentClasses="p-0 flex"
|
||||
contentStyle="height: min(72vh, 520px);"
|
||||
class="border-b border-transparent text-accent hover:border-accent"
|
||||
triggerAttrs={{ 'aria-label': 'Start from a template' }}
|
||||
on:openChange={(e) => e.detail && logFeatureUsage('home', 'template_picker_open')}
|
||||
|
||||
@@ -22,16 +22,23 @@
|
||||
/** Where to go once the workspace exists. It is already the active one by then. */
|
||||
onCreated: (workspaceId: string) => void
|
||||
/**
|
||||
* True from the click until the navigation, so a surface with chrome of its own around
|
||||
* this form — onboarding's Previous button — can stand down for the hand-over instead
|
||||
* of offering a way back out of a workspace that now exists.
|
||||
* Told when the form starts handing over to the new workspace and if it comes back, so
|
||||
* a surface with chrome of its own around this one — onboarding's Previous button — can
|
||||
* stand down for the hand-over instead of offering a way back out of a workspace that
|
||||
* now exists. A callback rather than a bound prop: this is something the form reports,
|
||||
* not state it shares, and `$bindable(default)` on an optional prop is banned.
|
||||
*/
|
||||
creating?: boolean
|
||||
onCreatingChange?: (creating: boolean) => void
|
||||
}
|
||||
|
||||
let { onCreated, creating = $bindable(false) }: Props = $props()
|
||||
let { onCreated, onCreatingChange }: Props = $props()
|
||||
|
||||
let name = $state('')
|
||||
let creating = $state(false)
|
||||
function setCreating(next: boolean) {
|
||||
creating = next
|
||||
onCreatingChange?.(next)
|
||||
}
|
||||
|
||||
// The full form — id, colour, username, invites — for the person who wants it. Forced on
|
||||
// when the instance does not derive usernames: one is required and a name field has
|
||||
@@ -91,7 +98,7 @@
|
||||
|
||||
async function create() {
|
||||
if (problem || creating) return
|
||||
creating = true
|
||||
setCreating(true)
|
||||
const workspaceName = name.trim()
|
||||
const started = Date.now()
|
||||
try {
|
||||
@@ -102,7 +109,7 @@
|
||||
true
|
||||
)
|
||||
advanced = true
|
||||
creating = false
|
||||
setCreating(false)
|
||||
return
|
||||
}
|
||||
await WorkspaceService.createWorkspace({
|
||||
@@ -123,7 +130,7 @@
|
||||
} catch (error) {
|
||||
console.error('Could not create the workspace:', error)
|
||||
sendUserToast('Could not create the workspace: ' + (error?.body || error?.message), true)
|
||||
creating = false
|
||||
setCreating(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -315,7 +315,10 @@
|
||||
<!-- The same one-field form the workspace picker falls back to, so a user who leaves
|
||||
onboarding early meets it again rather than something new. It owns the name, the
|
||||
id, the advanced form and the hand-over into the workspace. -->
|
||||
<SimpleCreateWorkspace onCreated={leaveOnboarding} bind:creating={creatingWorkspace} />
|
||||
<SimpleCreateWorkspace
|
||||
onCreated={leaveOnboarding}
|
||||
onCreatingChange={(v) => (creatingWorkspace = v)}
|
||||
/>
|
||||
|
||||
{#if !skippedSurvey && !creatingWorkspace}
|
||||
<div class="flex flex-row justify-start items-center pt-6">
|
||||
|
||||
Reference in New Issue
Block a user