mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-21 16:03:26 +00:00
* feat: replace ag-grid in the db manager table viewer with a native grid Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: add user-managed data, diagram and sql editor tabs to the db manager Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: add schema autocomplete to the db manager sql editor and polish its layout Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: add joined foreign key columns and draggable tabs to the db manager Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: move db manager tabs on drop instead of mid-drag Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: open followed foreign keys in a new db manager tab Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: scroll large tables, drop stale joins and return to the last tab on close Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: tolerate the db manager tabs going away while switching data table Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep saved joined columns while metadata loads, test joins in every dialect Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: put the db manager grid on the input surface in dark mode Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: address db manager review nits on joins, boolean keys and sql quoting Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep the dark border color on the left pinned column edge Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: tone down the db manager tree menu icons Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: write json and jsonb values from the db manager through a text cast Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: dim unrelated diagram tables less on hover Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: match json columns as text when deleting a db manager row Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: compare postgres json columns as text in exact db manager filters Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: pick the columns the db manager grid shows Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: toggle every db manager column from one checkbox Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: word joined columns as a view in the db manager Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: keep data table roles and access visible when unavailable, with the reason Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: keep data table and instance roles visible in settings when unavailable Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: hide a db manager column from its header menu Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: format db manager columns with a unit, significant digits and color rules Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: drop the before/after hints from the db manager unit picker Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: write the euro after the amount and keep units off non-numeric values Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: color rule presets, bold and italic, layered and reorderable rules in the db manager Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: decimals, thousands separator, compact notation and alignment in db manager column formats Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: compact db manager format controls, a notation toggle group and a reset button Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: db manager format and filter nits Keep the value's scale when decimals are auto, read boolean color-rule conditions as booleans, let a rule's text color reach foreign-key links, close the formatter when the columns picker opens, and filter BigQuery complex columns through TO_JSON_STRING. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
166 lines
5.3 KiB
Svelte
166 lines
5.3 KiB
Svelte
<script lang="ts" generics="T">
|
|
import { deepEqual } from 'fast-equals'
|
|
import ConditionalPortal from '../common/drawer/ConditionalPortal.svelte'
|
|
import { untrack, type Snippet } from 'svelte'
|
|
import { twMerge } from 'tailwind-merge'
|
|
import { useReducedMotion } from '$lib/svelte5Utils.svelte'
|
|
import { watch } from 'runed'
|
|
|
|
let {
|
|
listAutoWidth = true,
|
|
strictWidth = false,
|
|
disablePortal = false,
|
|
open,
|
|
disabled = false,
|
|
class: className = '',
|
|
innerClass = '',
|
|
maxHeight = 256,
|
|
instantClose = false,
|
|
getInputRect,
|
|
portalName = 'dropdown-portal',
|
|
children
|
|
}: {
|
|
listAutoWidth?: boolean
|
|
strictWidth?: boolean
|
|
disablePortal?: boolean
|
|
open: boolean
|
|
disabled?: boolean
|
|
class?: string
|
|
innerClass?: string
|
|
maxHeight?: number
|
|
// Close with no height-collapse animation (still animates open). Used where the
|
|
// dropdown is toggled off as the user types, so the panel vanishes at once.
|
|
instantClose?: boolean
|
|
getInputRect?: () => DOMRect
|
|
/** Outside-click handlers ignore clicks in `.dropdown-portal`: a panel that holds Selects of
|
|
* its own needs another name, or their lists would not close on clicks elsewhere in it. */
|
|
portalName?: string
|
|
children?: Snippet
|
|
} = $props()
|
|
|
|
let listEl: HTMLDivElement | undefined = $state()
|
|
let dropdownPos = $state(computeDropdownPos())
|
|
let reducedMotion = useReducedMotion()
|
|
|
|
function computeDropdownPos(): {
|
|
width: number
|
|
height: number
|
|
x: number
|
|
y: number
|
|
isBelow: boolean
|
|
} {
|
|
if (!getInputRect || !listEl) return { width: 0, height: 0, x: 0, y: 0, isBelow: true }
|
|
let inputR = getInputRect()
|
|
const listR = listEl.getBoundingClientRect()
|
|
const fitsBelow = inputR.y + inputR.height + listR.height <= window.innerHeight
|
|
const spaceBelow = window.innerHeight - (inputR.y + inputR.height)
|
|
const spaceAbove = inputR.y
|
|
const isBelow = fitsBelow || spaceBelow >= spaceAbove
|
|
let [x, y] = disablePortal ? [0, 0] : [inputR.x, inputR.y]
|
|
// The list grows to its widest item, so one wider than the room right of its input
|
|
// would run off screen: slide it left instead.
|
|
if (!disablePortal) x = Math.max(0, Math.min(x, window.innerWidth - listR.width))
|
|
if (isBelow)
|
|
return { width: inputR.width, height: listR.height, x: x, y: y + inputR.height, isBelow }
|
|
else {
|
|
return { width: inputR.width, height: listR.height, x: x, y: y - listR.height, isBelow }
|
|
}
|
|
}
|
|
|
|
$effect(() => {
|
|
function updateDropdownPos() {
|
|
let nPos = computeDropdownPos()
|
|
if (!deepEqual(nPos, dropdownPos)) dropdownPos = nPos
|
|
if (open) requestAnimationFrame(updateDropdownPos)
|
|
}
|
|
if (open) untrack(() => updateDropdownPos())
|
|
})
|
|
|
|
// We do not want to render the dropdown when it is closed for performance reasons
|
|
// but we want to keep it in the DOM for a short time to allow for transitions to finish
|
|
//
|
|
// We do not use Svelte transitions because they can not animate in the opposite direction
|
|
// when the dropdown is opens above the input
|
|
// Also CSS transitions are smoother because they do not rely on JS / animation frames
|
|
let uiState = $state({
|
|
domExists: untrack(() => open),
|
|
visible: untrack(() => open),
|
|
timeout: null as number | null
|
|
})
|
|
let initial = true
|
|
watch(
|
|
() => open && !disabled,
|
|
(isOpen) => {
|
|
untrack(() => {
|
|
if (initial) {
|
|
initial = false
|
|
return
|
|
}
|
|
// Reduced motion skips all animation; instantClose skips only the closing
|
|
// one (open still animates) so the panel disappears the instant it's toggled off.
|
|
if (reducedMotion.val || (instantClose && !isOpen)) {
|
|
if (uiState.timeout) clearTimeout(uiState.timeout)
|
|
uiState = {
|
|
domExists: open && !disabled,
|
|
visible: open && !disabled,
|
|
timeout: null
|
|
}
|
|
return
|
|
}
|
|
if (uiState.timeout) clearTimeout(uiState.timeout)
|
|
uiState = {
|
|
domExists: true,
|
|
visible: !isOpen,
|
|
timeout: setTimeout(() => {
|
|
if (isOpen) {
|
|
uiState.visible = true
|
|
uiState.timeout = null
|
|
} else if (!isOpen) {
|
|
uiState.visible = false
|
|
uiState.timeout = setTimeout(() => {
|
|
uiState.domExists = false
|
|
uiState.timeout = null
|
|
}, 500) // leave time for transition to finish
|
|
}
|
|
}, 0) // We need the height to be 0 then change immediately for the transition to play
|
|
}
|
|
})
|
|
}
|
|
)
|
|
</script>
|
|
|
|
<ConditionalPortal condition={!disablePortal} name={portalName}>
|
|
{#if uiState.domExists}
|
|
<div
|
|
class={twMerge(
|
|
open ? 'dropdown-open' : 'dropdown-closed',
|
|
disablePortal ? 'absolute z-[5002]' : 'fixed z-[10000]',
|
|
'text-primary text-sm select-none',
|
|
dropdownPos.isBelow ? '' : 'flex flex-col justify-end',
|
|
uiState.visible ? '' : 'pointer-events-none',
|
|
className
|
|
)}
|
|
style="{`top: ${dropdownPos.y}px; left: ${dropdownPos.x}px;`} {listAutoWidth
|
|
? `${strictWidth ? 'width' : 'min-width'}: ${dropdownPos.width}px; height: ${dropdownPos.height}px;`
|
|
: ''}"
|
|
>
|
|
<div
|
|
class={twMerge(
|
|
'overflow-clip rounded-md drop-shadow-base',
|
|
!reducedMotion.val ? 'transition-height' : '',
|
|
dropdownPos.isBelow ? '' : 'flex flex-col justify-end'
|
|
)}
|
|
style="height: {uiState.visible ? dropdownPos.height : 0}px;"
|
|
>
|
|
<div
|
|
bind:this={listEl}
|
|
class="flex flex-col rounded-md bg-surface-input {innerClass}"
|
|
style="max-height: {maxHeight}px;"
|
|
>
|
|
{@render children?.()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</ConditionalPortal>
|