{% from "admin/components/icons.html" import icon %} {# Combobox, from shadcn's Popover + cmdk. cmdk filters a client-side array, but the point of autocomplete_fields is never loading the target's full dataset into the page, so filtering stays server-side on /lookup: htmx owns the round-trip, Alpine only open/close and arrow keys. A result's pk and label ride in on data-pk/data-label and are read back through `$el.dataset`; the initial selection is the inputs' own server-rendered `value`. selectItem writes the pk imperatively rather than through a reactive `:value`, because x-bind runs before x-init and would blank the server-rendered value on every load. Neither route puts a label inside a JS expression. Its own partial rather than a branch of ui/field, because two callers need it: the record form's autocomplete relation, and the filter panel's relation filter. #} {% macro combobox(name, id, selected_pk=None, selected_label=None, lookup_url="", placeholder="", disabled=False) %}
{{ icon("search", ui('combobox', 'icon')) }} {# role=combobox on the input, not the wrapper: the input is what holds focus, so it is what has to advertise the popup and name the highlighted option. Mirrors ui/multi-select. #}
{# htmx replaces these children on every keystroke, so the remembered highlight is stale the moment a swap lands. #} {# Portalled to , as Radix does: the popover is taller than the row it opens from, and any clipping ancestor -- a collapsible fieldset, a table's scroll area, the filter drawer -- would slice it off. x-anchor then positions it against the trigger, and x-effect matches its width, which "w-full" can no longer do from the body. #}
{% endmacro %}