mirror of
https://github.com/chenasraf/input-form.nvim.git
synced 2026-05-17 17:38:01 +00:00
feat: compact dropdown popover style
This commit is contained in:
@@ -173,17 +173,40 @@ function M:open_dropdown()
|
||||
local max_h = config.options.select.max_height
|
||||
local height = math.min(#lines, max_h)
|
||||
|
||||
-- Position the dropdown's top border immediately beneath the input's bottom
|
||||
-- border. Content origin row = (input content row) + (input bottom border = 1)
|
||||
-- + (dropdown top border = 1) + 1 = self._layout.row + 3.
|
||||
-- Prefer stitching the dropdown's top border into the select's bottom
|
||||
-- border for a compact, merged look:
|
||||
--
|
||||
-- ╭─ Label ─────╮
|
||||
-- │ Option 1 ⌃ │
|
||||
-- ├─────────────┤ <- shared row (dropdown's top border with T-junction
|
||||
-- │ Option 1 │ connectors, overlaid on the select's bottom)
|
||||
-- │ Option 2 │
|
||||
-- ╰─────────────╯
|
||||
--
|
||||
-- The dropdown is positioned so its top border row coincides with the
|
||||
-- select's bottom border row. With a higher zindex, the dropdown's top
|
||||
-- border (├─┤ / ╠═╣) wins, producing the visible T-junctions.
|
||||
local cfg_border = config.options.window.border
|
||||
local merged_border = utils.merged_top_border(cfg_border)
|
||||
local dropdown_row, dropdown_border
|
||||
if merged_border then
|
||||
dropdown_row = self._layout.row + 2
|
||||
dropdown_border = merged_border
|
||||
else
|
||||
-- Fallback for unmergeable borders (`"none"`, `"shadow"`, ...): keep the
|
||||
-- dropdown on its own, one row below the select.
|
||||
dropdown_row = self._layout.row + 3
|
||||
dropdown_border = cfg_border
|
||||
end
|
||||
|
||||
self.dropdown_win = vim.api.nvim_open_win(self.dropdown_buf, true, {
|
||||
relative = "editor",
|
||||
row = self._layout.row + 3,
|
||||
row = dropdown_row,
|
||||
col = self._layout.col,
|
||||
width = self._layout.width,
|
||||
height = height,
|
||||
style = "minimal",
|
||||
border = "rounded",
|
||||
border = dropdown_border,
|
||||
focusable = true,
|
||||
zindex = 100,
|
||||
})
|
||||
|
||||
@@ -43,6 +43,57 @@ end
|
||||
--- their UI plugins' exclusion lists as a fallback.
|
||||
M.FORM_FILETYPE = "input-form"
|
||||
|
||||
--- Character sets for the built-in border styles accepted by `nvim_open_win`.
|
||||
--- Order is clockwise from top-left: TL, T, TR, R, BR, B, BL, L.
|
||||
local BORDER_CHARS = {
|
||||
rounded = { "╭", "─", "╮", "│", "╯", "─", "╰", "│" },
|
||||
single = { "┌", "─", "┐", "│", "┘", "─", "└", "│" },
|
||||
double = { "╔", "═", "╗", "║", "╝", "═", "╚", "║" },
|
||||
solid = { " ", " ", " ", " ", " ", " ", " ", " " },
|
||||
}
|
||||
|
||||
-- T-junction connectors used to replace the top corners when stitching two
|
||||
-- boxes together (the bottom of box A and the top of box B share a row).
|
||||
local MERGE_CONNECTORS = {
|
||||
rounded = { left = "├", right = "┤" },
|
||||
single = { left = "├", right = "┤" },
|
||||
double = { left = "╠", right = "╣" },
|
||||
solid = { left = " ", right = " " },
|
||||
}
|
||||
|
||||
--- Build an 8-element border array whose top row is a T-junction stitching
|
||||
--- into the bottom of a parent box above it. Accepts either one of the
|
||||
--- built-in border style names or an existing 8-element border array.
|
||||
---
|
||||
--- Returns `nil` for unrecognised / non-mergeable borders (e.g. `"none"`,
|
||||
--- `"shadow"`) so the caller can fall back to an unmerged layout.
|
||||
---@param border string|table
|
||||
---@return table|nil
|
||||
function M.merged_top_border(border)
|
||||
local chars, connectors
|
||||
if type(border) == "string" then
|
||||
chars = BORDER_CHARS[border]
|
||||
connectors = MERGE_CONNECTORS[border]
|
||||
elseif type(border) == "table" and #border == 8 then
|
||||
chars = vim.deepcopy(border)
|
||||
-- Best-effort fallback for custom arrays: use the straight T's.
|
||||
connectors = { left = "├", right = "┤" }
|
||||
end
|
||||
if not chars or not connectors then
|
||||
return nil
|
||||
end
|
||||
return {
|
||||
connectors.left,
|
||||
chars[2],
|
||||
connectors.right,
|
||||
chars[4],
|
||||
chars[5],
|
||||
chars[6],
|
||||
chars[7],
|
||||
chars[8],
|
||||
}
|
||||
end
|
||||
|
||||
local _excluded_registered = false
|
||||
|
||||
-- Append `ft` to a list-shaped config field if missing.
|
||||
|
||||
@@ -113,6 +113,27 @@ T["select input"]["uses custom chevrons from config"] = function()
|
||||
helpers.expect.no_match(open_line, "⌃")
|
||||
end
|
||||
|
||||
T["select input"]["dropdown border merges into select's bottom border"] = function()
|
||||
-- `rounded` default → T-junctions are ├ and ┤.
|
||||
child.lua([[
|
||||
_G.t = _G.mk('a')
|
||||
_G.t:mount({ row = 5, col = 5, width = 30 })
|
||||
_G.t._layout = { row = 10, col = 5, width = 30 }
|
||||
_G.t:open_dropdown()
|
||||
]])
|
||||
local cfg = child.lua_get([[vim.api.nvim_win_get_config(_G.t.dropdown_win)]])
|
||||
-- Dropdown row must overlap the select's bottom border row (= layout.row + 2
|
||||
-- for the content origin, putting the top border at layout.row + 1).
|
||||
eq(cfg.row, 12)
|
||||
-- Border is an 8-element array with T-junctions in the top corners.
|
||||
eq(type(cfg.border), "table")
|
||||
-- nvim_win_get_config returns borders as { { char, hl_group }, ... }.
|
||||
local tl = type(cfg.border[1]) == "table" and cfg.border[1][1] or cfg.border[1]
|
||||
local tr = type(cfg.border[3]) == "table" and cfg.border[3][1] or cfg.border[3]
|
||||
eq(tl, "├")
|
||||
eq(tr, "┤")
|
||||
end
|
||||
|
||||
T["select input"]["rejects empty options list"] = function()
|
||||
local ok = child.lua_get([[
|
||||
(function()
|
||||
|
||||
Reference in New Issue
Block a user