mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 00:01:55 +00:00
fix: pin table actions column so it stays visible on narrow screens (#10301)
* fix: pin table actions column so it stays visible on narrow screens Wide DataTables (folders, variables, resources) scroll horizontally on small screens, pushing the trailing per-row actions column (the ⋯ menu, Edit/Delete, and folders' "Publish to Hub") off the right edge where it was effectively unreachable. Add an opt-in `stickyEnd` prop to Cell that pins a column to the right of the scroll container with an opaque background and a left divider, and apply it to the actions column (header + body) on the folders, variables, and resources pages. The background is opaque (bg-surface / bg-surface-secondary) rather than the row's translucent hover tint, so cells sliding under the pinned column are occluded instead of bleeding through. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: keep resources actions cell as table-cell so sticky pins correctly The workspace resources actions cell used class="flex justify-end" on the Cell, which forces the <td> to display:flex. A flex box inside a table row is wrapped in an anonymous table-cell, so position:sticky on it is constrained to that wrapper and no longer pins to the scrollport — the header stayed pinned while the row actions scrolled away. Move the flex layout to an inner <div> so the <td> keeps display:table-cell and the stickyEnd pin works. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address review nits on pinned actions column - resources Workspace table: add `last` to the body actions cell so its right padding (sm:pr-6) matches the header and the other tables. - variables table: isolate the refresh-error ping indicator's stacking context so its z-50 can't paint over a sticky-pinned actions column scrolling past it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
shouldStopPropagation?: boolean
|
||||
selected?: boolean
|
||||
sticky?: boolean
|
||||
stickyEnd?: boolean
|
||||
wrap?: boolean
|
||||
children?: import('svelte').Snippet
|
||||
[key: string]: any
|
||||
@@ -24,6 +25,7 @@
|
||||
shouldStopPropagation = false,
|
||||
selected = false,
|
||||
sticky = false,
|
||||
stickyEnd = false,
|
||||
wrap = false,
|
||||
children,
|
||||
...rest
|
||||
@@ -53,6 +55,11 @@
|
||||
last && size === 'xs' ? 'sm:pr-3' : '',
|
||||
|
||||
numeric ? 'text-right' : '',
|
||||
// Pin an actions column to the right so it stays visible when a wide table
|
||||
// scrolls horizontally. The background must be opaque so cells sliding under it
|
||||
// are occluded — the row's hover tint is translucent and would bleed through.
|
||||
stickyEnd ? 'sticky right-0 border-l' : '',
|
||||
stickyEnd ? (head ? 'bg-surface-secondary' : 'bg-surface') : '',
|
||||
sticky ? `!p-0 sticky ${first ? 'left-0' : 'right-0'}` : 'px-2 py-2',
|
||||
size === 'sm' ? 'px-1.5 py-2.5' : '',
|
||||
size === 'lg' ? 'px-3 py-4' : '',
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
<Cell head class="w-20">Variables</Cell>
|
||||
<Cell head class="w-20">Resources</Cell>
|
||||
<Cell head class="w-20">Participants</Cell>
|
||||
<Cell head last />
|
||||
<Cell head last stickyEnd />
|
||||
</tr>
|
||||
</Head>
|
||||
<tbody class="divide-y">
|
||||
@@ -242,7 +242,7 @@
|
||||
<FolderUsageInfo {name} tabular />
|
||||
|
||||
<Cell><FolderInfo members={computeMembers(owners, extra_perms)} /></Cell>
|
||||
<Cell shouldStopPropagation>
|
||||
<Cell last stickyEnd shouldStopPropagation>
|
||||
<Dropdown
|
||||
items={[
|
||||
{
|
||||
|
||||
@@ -1008,7 +1008,7 @@
|
||||
<Cell head>Resource type</Cell>
|
||||
<Cell head>Description</Cell>
|
||||
<Cell head />
|
||||
<Cell head last />
|
||||
<Cell head last stickyEnd />
|
||||
</Row>
|
||||
</Head>
|
||||
<tbody class="divide-y bg-surface">
|
||||
@@ -1176,83 +1176,85 @@
|
||||
{/if}
|
||||
</div>
|
||||
</Cell>
|
||||
<Cell class="flex justify-end">
|
||||
{#if path && assetCanBeExplored({ kind: 'resource', path }, { resource_type }) && !$userStore?.operator}
|
||||
<ExploreAssetButton
|
||||
asset={{ kind: 'resource', path }}
|
||||
_resourceMetadata={{ resource_type }}
|
||||
class="w-24"
|
||||
/>
|
||||
{/if}
|
||||
<Dropdown
|
||||
class="w-fit"
|
||||
items={[
|
||||
{
|
||||
displayName: 'Permissions',
|
||||
icon: Shield,
|
||||
action: () => {
|
||||
shareModal?.openDrawer?.(path, 'resource')
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Edit',
|
||||
icon: Pen,
|
||||
disabled: !canWrite || !showCreateButtons,
|
||||
action: () => {
|
||||
resourceEditor?.initEdit?.(path)
|
||||
}
|
||||
},
|
||||
...(!ws_specific && isDeployable('resource', path, deployUiSettings)
|
||||
? [
|
||||
{
|
||||
displayName: 'Deploy to prod/staging',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
deploymentDrawer?.openDrawer(path, 'resource')
|
||||
<Cell last stickyEnd>
|
||||
<div class="flex justify-end">
|
||||
{#if path && assetCanBeExplored({ kind: 'resource', path }, { resource_type }) && !$userStore?.operator}
|
||||
<ExploreAssetButton
|
||||
asset={{ kind: 'resource', path }}
|
||||
_resourceMetadata={{ resource_type }}
|
||||
class="w-24"
|
||||
/>
|
||||
{/if}
|
||||
<Dropdown
|
||||
class="w-fit"
|
||||
items={[
|
||||
{
|
||||
displayName: 'Permissions',
|
||||
icon: Shield,
|
||||
action: () => {
|
||||
shareModal?.openDrawer?.(path, 'resource')
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Edit',
|
||||
icon: Pen,
|
||||
disabled: !canWrite || !showCreateButtons,
|
||||
action: () => {
|
||||
resourceEditor?.initEdit?.(path)
|
||||
}
|
||||
},
|
||||
...(!ws_specific && isDeployable('resource', path, deployUiSettings)
|
||||
? [
|
||||
{
|
||||
displayName: 'Deploy to prod/staging',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
deploymentDrawer?.openDrawer(path, 'resource')
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
displayName: 'Delete',
|
||||
disabled: !canWrite || !showCreateButtons,
|
||||
icon: Trash,
|
||||
type: 'delete',
|
||||
action: (event) => {
|
||||
// TODO
|
||||
// @ts-ignore
|
||||
if (event?.shiftKey) {
|
||||
deleteResource(path, account)
|
||||
} else {
|
||||
deleteIsLinked = is_linked ?? false
|
||||
deleteConfirmedCallback = () => {
|
||||
]
|
||||
: []),
|
||||
{
|
||||
displayName: 'Delete',
|
||||
disabled: !canWrite || !showCreateButtons,
|
||||
icon: Trash,
|
||||
type: 'delete',
|
||||
action: (event) => {
|
||||
// TODO
|
||||
// @ts-ignore
|
||||
if (event?.shiftKey) {
|
||||
deleteResource(path, account)
|
||||
} else {
|
||||
deleteIsLinked = is_linked ?? false
|
||||
deleteConfirmedCallback = () => {
|
||||
deleteResource(path, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
...(account != undefined
|
||||
? [
|
||||
{
|
||||
displayName: 'Refresh token',
|
||||
icon: RotateCw,
|
||||
action: async () => {
|
||||
await OauthService.refreshToken({
|
||||
workspace: $workspaceStore ?? '',
|
||||
id: account ?? 0,
|
||||
requestBody: {
|
||||
path
|
||||
}
|
||||
})
|
||||
sendUserToast('Token refreshed')
|
||||
loadResources()
|
||||
},
|
||||
...(account != undefined
|
||||
? [
|
||||
{
|
||||
displayName: 'Refresh token',
|
||||
icon: RotateCw,
|
||||
action: async () => {
|
||||
await OauthService.refreshToken({
|
||||
workspace: $workspaceStore ?? '',
|
||||
id: account ?? 0,
|
||||
requestBody: {
|
||||
path
|
||||
}
|
||||
})
|
||||
sendUserToast('Token refreshed')
|
||||
loadResources()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
: [])
|
||||
]}
|
||||
/>
|
||||
</Cell>
|
||||
]
|
||||
: [])
|
||||
]}
|
||||
/>
|
||||
</div></Cell
|
||||
>
|
||||
</Row>
|
||||
{/each}
|
||||
{/if}
|
||||
@@ -1280,7 +1282,7 @@
|
||||
<Row>
|
||||
<Cell head first>Name</Cell>
|
||||
<Cell head>Description</Cell>
|
||||
<Cell head last />
|
||||
<Cell head last stickyEnd />
|
||||
</Row>
|
||||
</Head>
|
||||
<tbody class="divide-y bg-surface">
|
||||
@@ -1316,7 +1318,7 @@
|
||||
{removeMarkdown(truncate(description ?? '', 200))}
|
||||
</span>
|
||||
</Cell>
|
||||
<Cell last>
|
||||
<Cell last stickyEnd>
|
||||
{#if !canWrite}
|
||||
<Badge>
|
||||
Shared globally
|
||||
|
||||
@@ -362,7 +362,7 @@
|
||||
<Cell head>Value</Cell>
|
||||
<Cell head>Description</Cell>
|
||||
<Cell head />
|
||||
<Cell head last />
|
||||
<Cell head last stickyEnd />
|
||||
</tr>
|
||||
</Head>
|
||||
<tbody class="divide-y">
|
||||
@@ -465,7 +465,11 @@
|
||||
<div class="">
|
||||
{#if refresh_error}
|
||||
<Popover notClickable>
|
||||
<div class="relative inline-flex justify-center items-center w-4 h-4">
|
||||
<!-- isolate: confine the ping indicator's z-50 to a local stacking context
|
||||
so it can't paint over a sticky-pinned actions column scrolling past it -->
|
||||
<div
|
||||
class="relative inline-flex justify-center items-center w-4 h-4 isolate"
|
||||
>
|
||||
<Circle
|
||||
class="text-red-600 animate-ping absolute z-50 w-4 h-4 fill-current"
|
||||
size={12}
|
||||
@@ -514,7 +518,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
</Cell>
|
||||
<Cell last shouldStopPropagation>
|
||||
<Cell last stickyEnd shouldStopPropagation>
|
||||
<Dropdown
|
||||
items={() => {
|
||||
let owner = isOwner(path, $userStore, $workspaceStore)
|
||||
|
||||
Reference in New Issue
Block a user