{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "advance-select",
  "title": "Advance Select",
  "type": "registry:ui",
  "description": "Searchable, async-capable select with keyboard navigation, multi-select, and option grouping. Drop in when the native `<select>` or the basic Select primitive runs out of room — large lists, debounced server-side filtering, custom rendered items.",
  "categories": [
    "form"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/advance-select/advance-select.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { styleMap } from 'lit/directives/style-map.js'\nimport { Check, ChevronDown, Loader2, X } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\nimport { computePosition } from '../../lib/position'\n\nexport interface AdvanceSelectFieldNames {\n  label?: string\n  value?: string\n  group?: string\n  disabled?: string\n}\n\nfunction readKey<T>(option: T, key: string, fallback?: unknown): unknown {\n  if (option == null || typeof option !== 'object') return fallback\n  const v = (option as Record<string, unknown>)[key]\n  return v === undefined ? fallback : v\n}\n\nconst sizeClasses = {\n  sm: 'h-8 text-xs px-2.5 py-1',\n  default: 'h-9 text-sm px-3 py-1.5',\n  lg: 'h-11 text-base px-4 py-2',\n}\n\nconst variantClasses = {\n  outlined:\n    'border-input bg-transparent shadow-xs focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n  filled:\n    'border-transparent bg-muted/50 shadow-none focus-visible:bg-muted focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n  borderless:\n    'border-transparent bg-transparent shadow-none focus-visible:bg-muted/30 focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n}\n\nconst statusClasses = {\n  default: '',\n  error:\n    'border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 aria-invalid:border-destructive',\n  warning: 'border-warning focus-visible:border-warning focus-visible:ring-warning/20',\n}\n\nconst jsonConverter = {\n  fromAttribute: (v: string | null) => {\n    if (!v) return undefined\n    try {\n      return JSON.parse(v)\n    } catch {\n      return v\n    }\n  },\n  toAttribute: (v: unknown) => (v ? (typeof v === 'string' ? v : JSON.stringify(v)) : null),\n}\n\nconst stringListConverter = {\n  fromAttribute: (v: string | null): string[] => {\n    if (!v) return []\n    try {\n      const parsed = JSON.parse(v)\n      if (Array.isArray(parsed)) return parsed\n    } catch {}\n    return v.split(',').map((s) => s.trim()).filter(Boolean)\n  },\n  toAttribute: (v: string[] | undefined): string | null => (v ? JSON.stringify(v) : null),\n}\n\nconst trueUnlessFalse = {\n  fromAttribute: (v: string | null) => v !== 'false',\n}\n\n/**\n * <uip-advance-select> — the registry AdvanceSelect as a web component.\n *\n * Form-associated: submits selected value(s) with its <form>.\n */\nexport class UipAdvanceSelect extends LitElement {\n  static formAssociated = true\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  static styles = [tailwind, css`:host { display: block; width: 100%; }`]\n\n  static properties = {\n    value: { converter: jsonConverter },\n    defaultValue: { attribute: 'default-value', converter: jsonConverter },\n    options: { converter: jsonConverter },\n    mode: { reflect: true },\n    fieldNames: { attribute: 'field-names', converter: jsonConverter },\n    size: { reflect: true },\n    variant: { reflect: true },\n    status: { reflect: true },\n    placeholder: {},\n    showSearch: { type: Boolean, attribute: 'show-search' },\n    searchValue: { attribute: 'search-value' },\n    autoClearSearchValue: { converter: trueUnlessFalse, attribute: 'auto-clear-search-value' },\n    maxCount: { type: Number, attribute: 'max-count' },\n    maxTagCount: { type: Number, attribute: 'max-tag-count' },\n    maxTagTextLength: { type: Number, attribute: 'max-tag-text-length' },\n    maxTagPlaceholder: { attribute: 'max-tag-placeholder' },\n    tokenSeparators: { attribute: 'token-separators', converter: stringListConverter },\n    hideSelected: { type: Boolean, attribute: 'hide-selected' },\n    allowCreate: { type: Boolean, attribute: 'allow-create' },\n    disabled: { type: Boolean, reflect: true },\n    loading: { type: Boolean, reflect: true },\n    allowClear: { converter: trueUnlessFalse, attribute: 'allow-clear' },\n    open: { type: Boolean, reflect: true },\n    defaultOpen: { type: Boolean, attribute: 'default-open' },\n    defaultActiveFirstOption: { converter: trueUnlessFalse, attribute: 'default-active-first-option' },\n    notFoundContent: { attribute: 'not-found-content' },\n    loadingText: { attribute: 'loading-text' },\n    listHeight: { type: Number, attribute: 'list-height' },\n    virtual: { converter: trueUnlessFalse },\n    name: { reflect: true },\n    accessibleLabel: { attribute: 'aria-label' },\n    query: { state: true },\n    pos: { state: true },\n  }\n\n  value: unknown = undefined\n  defaultValue: unknown = undefined\n  options: any[] = []\n  mode: 'single' | 'multiple' | 'tags' = 'single'\n  fieldNames: AdvanceSelectFieldNames = {}\n  size: 'sm' | 'default' | 'lg' = 'default'\n  variant: 'outlined' | 'filled' | 'borderless' = 'outlined'\n  status: 'default' | 'error' | 'warning' = 'default'\n  placeholder = 'Select...'\n  showSearch = false\n  searchValue?: string\n  autoClearSearchValue = true\n  maxCount?: number\n  maxTagCount?: number\n  maxTagTextLength?: number\n  maxTagPlaceholder?: string\n  tokenSeparators: string[] = []\n  hideSelected = false\n  allowCreate = false\n  disabled = false\n  loading = false\n  allowClear = true\n  open = false\n  defaultOpen = false\n  defaultActiveFirstOption = true\n  notFoundContent = 'No results.'\n  loadingText = 'Loading...'\n  listHeight = 300\n  virtual = true\n  name?: string\n  accessibleLabel?: string\n\n  private query = ''\n  private pos: Record<string, string> = {}\n  private initialValue: unknown = undefined\n  private internals = this.attachInternals()\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'advance-select')\n    if (this.defaultValue !== undefined && this.getAttribute('value') === null) {\n      this.value = this.defaultValue\n    }\n    this.initialValue = this.value\n    if (this.defaultOpen) {\n      this.open = true\n    }\n    document.addEventListener('pointerdown', this.onDocumentPointerDown)\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    document.removeEventListener('pointerdown', this.onDocumentPointerDown)\n  }\n\n  private onDocumentPointerDown = (e: PointerEvent) => {\n    if (!this.open) return\n    const path = e.composedPath()\n    if (!path.includes(this)) {\n      this.setOpen(false)\n    }\n  }\n\n  protected willUpdate(changed: Map<string, unknown>) {\n    if (changed.has('value') || changed.has('name')) {\n      if (!this.name || this.value == null) {\n        this.internals.setFormValue(null)\n      } else if (Array.isArray(this.value)) {\n        const fd = new FormData()\n        this.value.forEach((v) => fd.append(`${this.name}[]`, String(v)))\n        this.internals.setFormValue(fd)\n      } else {\n        this.internals.setFormValue(String(this.value))\n      }\n    }\n    if (changed.has('searchValue') && this.searchValue !== undefined) {\n      this.query = this.searchValue\n    }\n  }\n\n  formResetCallback() {\n    this.commitValue(this.initialValue)\n  }\n\n  formDisabledCallback(disabled: boolean) {\n    this.disabled = disabled\n  }\n\n  private setOpen(v: boolean) {\n    if (this.disabled || this.loading) return\n    if (this.open === v) return\n    this.open = v\n    if (v) {\n      this.place()\n    } else if (this.autoClearSearchValue) {\n      this.query = ''\n    }\n    this.dispatchEvent(new CustomEvent('open-change', { detail: { open: v }, bubbles: true, composed: true }))\n  }\n\n  private place() {\n    this.updateComplete.then(() => {\n      const t = this.trigger\n      const p = this.popoverEl\n      if (!t || !p) return\n      const { style } = computePosition(t, p, { side: 'bottom', align: 'start', sideOffset: 4, matchWidth: true })\n      this.pos = style\n    })\n  }\n\n  private get trigger(): HTMLElement | null {\n    return this.renderRoot?.querySelector('[role=combobox]')\n  }\n\n  private get popoverEl(): HTMLElement | null {\n    return this.renderRoot?.querySelector('[data-slot=advance-select-popover]')\n  }\n\n  private get valueKey(): string {\n    return this.fieldNames?.value ?? 'value'\n  }\n  private get labelKey(): string {\n    return this.fieldNames?.label ?? 'label'\n  }\n  private get groupKey(): string {\n    return this.fieldNames?.group ?? 'group'\n  }\n  private get disabledKey(): string {\n    return this.fieldNames?.disabled ?? 'disabled'\n  }\n\n  private getValue(o: any): unknown {\n    return readKey(o, this.valueKey, o)\n  }\n  private getLabel(o: any): string {\n    return String(readKey(o, this.labelKey, ''))\n  }\n  private getGroup(o: any): string | undefined {\n    const g = readKey(o, this.groupKey)\n    return g == null ? undefined : String(g)\n  }\n  private isDisabledOption(o: any): boolean {\n    return Boolean(readKey(o, this.disabledKey, false))\n  }\n\n  private get isMultiple(): boolean {\n    return this.mode === 'multiple' || this.mode === 'tags'\n  }\n\n  private get selectedValues(): unknown[] {\n    if (this.value == null) return []\n    if (this.isMultiple) {\n      return Array.isArray(this.value) ? this.value : []\n    }\n    return [this.value]\n  }\n\n  private get selectedSet(): Set<unknown> {\n    return new Set(this.selectedValues)\n  }\n\n  private get selectedOptions(): any[] {\n    return this.selectedValues.map((v) => {\n      const found = this.options.find((o) => this.getValue(o) === v)\n      if (found) return found\n      return { [this.labelKey]: String(v), [this.valueKey]: v }\n    })\n  }\n\n  private commitValue(next: unknown, opt?: any) {\n    this.value = next\n    this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n    this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n    this.dispatchEvent(\n      new CustomEvent('value-change', {\n        detail: { value: next, option: opt },\n        bubbles: true,\n        composed: true,\n      }),\n    )\n  }\n\n  private matchesFilter(o: any, q: string): boolean {\n    const label = this.getLabel(o).toLowerCase()\n    const search = q.toLowerCase()\n    return label.includes(search)\n  }\n\n  private get filteredOptions(): any[] {\n    let result = this.options ?? []\n    const q = this.query.trim()\n    if (q) {\n      result = result.filter((o) => this.matchesFilter(o, q))\n    }\n    if (this.hideSelected && this.isMultiple) {\n      const set = this.selectedSet\n      result = result.filter((o) => !set.has(this.getValue(o)))\n    }\n    return result\n  }\n\n  private get grouped(): Array<{ heading?: string; items: any[] }> {\n    const groups = new Map<string, any[]>()\n    for (const opt of this.filteredOptions) {\n      const key = this.getGroup(opt) ?? ''\n      if (!groups.has(key)) groups.set(key, [])\n      groups.get(key)!.push(opt)\n    }\n    return Array.from(groups, ([heading, items]) => ({ heading: heading || undefined, items }))\n  }\n\n  private get atMax(): boolean {\n    if (typeof this.maxCount !== 'number') return false\n    const count = Array.isArray(this.value) ? this.value.length : this.value ? 1 : 0\n    return count >= this.maxCount\n  }\n\n  private displayLabel(o: any): string {\n    let label = this.getLabel(o)\n    if (this.maxTagTextLength && label.length > this.maxTagTextLength) {\n      label = label.slice(0, this.maxTagTextLength) + '...'\n    }\n    return label\n  }\n\n  private selectOption(opt: any) {\n    if (this.isDisabledOption(opt)) return\n    const v = this.getValue(opt)\n\n    if (!this.isMultiple) {\n      this.commitValue(v, opt)\n      this.dispatchEvent(new CustomEvent('select', { detail: { value: v, option: opt }, bubbles: true, composed: true }))\n      this.setOpen(false)\n      if (this.autoClearSearchValue) this.setSearch('')\n      return\n    }\n\n    const current = Array.isArray(this.value) ? [...this.value] : []\n    const set = this.selectedSet\n    if (set.has(v)) {\n      const next = current.filter((x) => x !== v)\n      this.commitValue(next, opt)\n      this.dispatchEvent(new CustomEvent('deselect', { detail: { value: v, option: opt }, bubbles: true, composed: true }))\n    } else {\n      if (this.atMax) return\n      const next = [...current, v]\n      this.commitValue(next, opt)\n      this.dispatchEvent(new CustomEvent('select', { detail: { value: v, option: opt }, bubbles: true, composed: true }))\n    }\n\n    if (this.autoClearSearchValue) this.setSearch('')\n  }\n\n  private removeTag(v: unknown, e: Event) {\n    e.stopPropagation()\n    if (this.disabled) return\n    const current = Array.isArray(this.value) ? [...this.value] : []\n    const next = current.filter((x) => x !== v)\n    const opt = this.options.find((o) => this.getValue(o) === v)\n    this.commitValue(next, opt)\n    if (opt) {\n      this.dispatchEvent(new CustomEvent('deselect', { detail: { value: v, option: opt }, bubbles: true, composed: true }))\n    }\n  }\n\n  private clearAll(e?: Event) {\n    e?.stopPropagation()\n    if (this.disabled) return\n    this.dispatchEvent(new CustomEvent('clear', { bubbles: true, composed: true }))\n    if (this.isMultiple) {\n      this.commitValue([])\n    } else {\n      this.commitValue(null)\n    }\n    this.setSearch('')\n  }\n\n  private setSearch(q: string) {\n    this.query = q\n    this.dispatchEvent(new CustomEvent('search-change', { detail: { searchValue: q }, bubbles: true, composed: true }))\n  }\n\n  private createTag() {\n    if (!this.allowCreate && this.mode !== 'tags') return\n    const q = this.query.trim()\n    if (!q) return\n    const exists = this.options.some((o) => this.getLabel(o) === q || String(this.getValue(o)) === q)\n    if (exists) return\n\n    const newOption = { [this.labelKey]: q, [this.valueKey]: q }\n\n    if (!this.isMultiple) {\n      this.commitValue(q, newOption)\n      this.dispatchEvent(new CustomEvent('select', { detail: { value: q, option: newOption }, bubbles: true, composed: true }))\n      this.setOpen(false)\n      this.setSearch('')\n      return\n    }\n\n    if (this.atMax) return\n    const current = Array.isArray(this.value) ? [...this.value] : []\n    const next = [...current, q]\n    this.commitValue(next, newOption)\n    this.dispatchEvent(new CustomEvent('select', { detail: { value: q, option: newOption }, bubbles: true, composed: true }))\n    this.setSearch('')\n  }\n\n  private handleInputKeydown(e: KeyboardEvent) {\n    if (e.key === 'Enter' && this.query.trim() && this.mode === 'tags') {\n      e.preventDefault()\n      this.createTag()\n    }\n    if (this.tokenSeparators.length && this.mode === 'tags') {\n      if (this.tokenSeparators.includes(e.key)) {\n        e.preventDefault()\n        this.createTag()\n      }\n    }\n  }\n\n  render() {\n    const isMultiple = this.isMultiple\n    const selectedOpts = this.selectedOptions\n    const selectedSet = this.selectedSet\n    const atMax = this.atMax\n\n    const visibleTags =\n      typeof this.maxTagCount === 'number' ? selectedOpts.slice(0, this.maxTagCount) : selectedOpts\n    const hiddenTagCount =\n      typeof this.maxTagCount === 'number' ? Math.max(0, selectedOpts.length - this.maxTagCount) : 0\n\n    const isEmpty = isMultiple\n      ? !Array.isArray(this.value) || this.value.length === 0\n      : this.value == null || this.value === ''\n\n    const showClear = this.allowClear && !isEmpty && !this.disabled && !this.loading\n    const showSearchInput = this.showSearch || this.mode === 'tags'\n\n    const triggerClasses = cn(\n      'flex w-full items-center justify-between gap-2 rounded-md border text-sm transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 text-foreground',\n      sizeClasses[this.size] ?? sizeClasses.default,\n      variantClasses[this.variant] ?? variantClasses.outlined,\n      statusClasses[this.status] ?? statusClasses.default,\n    )\n\n    const filtered = this.filteredOptions\n    const grouped = this.grouped\n\n    return html`\n      <div part=\"base\" class=\"relative w-full\">\n        <button\n          part=\"trigger\"\n          type=\"button\"\n          role=\"combobox\"\n          aria-expanded=${this.open ? 'true' : 'false'}\n          aria-invalid=${this.status === 'error' ? 'true' : nothing}\n          aria-label=${this.accessibleLabel ?? nothing}\n          ?disabled=${this.disabled || this.loading}\n          data-slot=\"advance-select\"\n          class=${triggerClasses}\n          @click=${() => this.setOpen(!this.open)}\n        >\n          <slot name=\"prefix\"></slot>\n\n          ${isMultiple\n            ? html`\n                <div\n                  class=\"flex flex-1 flex-nowrap items-center gap-1 overflow-x-auto [scrollbar-width:none]\"\n                >\n                  ${selectedOpts.length\n                    ? html`\n                        ${visibleTags.map(\n                          (opt) => html`\n                            <span\n                              class=\"bg-muted text-foreground inline-flex h-6 items-center gap-1 rounded px-2 text-xs font-normal\"\n                            >\n                              <span class=\"truncate\">${this.displayLabel(opt)}</span>\n                              ${!this.disabled\n                                ? html`\n                                    <span\n                                      role=\"button\"\n                                      tabindex=\"0\"\n                                      class=\"hover:bg-muted-foreground/20 inline-flex items-center justify-center rounded-full p-0.5 transition-colors cursor-pointer\"\n                                      aria-label=${`Remove ${this.getLabel(opt)}`}\n                                      @click=${(e: Event) => this.removeTag(this.getValue(opt), e)}\n                                    >\n                                      ${icon(X, 'x', 'size-3')}\n                                    </span>\n                                  `\n                                : nothing}\n                            </span>\n                          `,\n                        )}\n                        ${hiddenTagCount > 0\n                          ? html`\n                              <span\n                                class=\"bg-muted text-foreground inline-flex h-6 items-center rounded px-2 text-xs font-normal\"\n                              >\n                                ${this.maxTagPlaceholder ?? `+${hiddenTagCount}`}\n                              </span>\n                            `\n                          : nothing}\n                      `\n                    : html`<span class=\"text-muted-foreground truncate\">${this.placeholder}</span>`}\n                </div>\n              `\n            : html`\n                <span\n                  class=${cn(\n                    'flex-1 truncate text-left',\n                    selectedOpts.length ? 'text-foreground' : 'text-muted-foreground',\n                  )}\n                >\n                  ${selectedOpts[0] ? this.getLabel(selectedOpts[0]) : this.placeholder}\n                </span>\n              `}\n\n          <span class=\"flex shrink-0 items-center gap-1\">\n            <slot name=\"suffix\"></slot>\n            ${this.loading\n              ? icon(Loader2, 'loader-2', 'text-muted-foreground size-4 animate-spin')\n              : showClear\n                ? html`\n                    <span\n                      role=\"button\"\n                      tabindex=\"0\"\n                      class=\"text-muted-foreground hover:text-foreground inline-flex cursor-pointer items-center rounded transition-colors\"\n                      aria-label=\"Clear selection\"\n                      @click=${(e: Event) => this.clearAll(e)}\n                    >\n                      ${icon(X, 'x', 'size-4')}\n                    </span>\n                  `\n                : html`\n                    <span class=${cn('text-muted-foreground size-4 shrink-0 opacity-50 transition-transform duration-200', this.open && 'rotate-180')}>\n                      ${icon(ChevronDown, 'chevron-down', 'size-4')}\n                    </span>\n                  `}\n          </span>\n        </button>\n\n        ${this.open\n          ? html`\n              <div\n                part=\"content\"\n                data-slot=\"advance-select-popover\"\n                style=${styleMap({ ...this.pos, maxHeight: `${this.listHeight}px` })}\n                class=\"bg-popover text-popover-foreground fixed inset-auto m-0 z-50 overflow-hidden rounded-md border shadow-md flex flex-col\"\n              >\n                ${showSearchInput\n                  ? html`\n                      <div class=\"border-b p-2\">\n                        <input\n                          .value=${this.query}\n                          placeholder=${this.placeholder}\n                          class=\"border-input focus-visible:ring-ring/50 h-9 w-full rounded-md border bg-transparent px-3 text-sm shadow-xs outline-none focus-visible:ring-[3px] text-foreground placeholder:text-muted-foreground\"\n                          @input=${(e: Event) => this.setSearch((e.target as HTMLInputElement).value)}\n                          @keydown=${this.handleInputKeydown}\n                        />\n                      </div>\n                    `\n                  : nothing}\n\n                <div class=\"flex-1 overflow-y-auto p-1 max-h-64\">\n                  ${!this.loading && filtered.length === 0\n                    ? html`<div class=\"text-muted-foreground py-6 text-center text-sm\">${this.notFoundContent}</div>`\n                    : nothing}\n                  ${this.loading && filtered.length === 0\n                    ? html`<div class=\"text-muted-foreground py-6 text-center text-sm\">${this.loadingText}</div>`\n                    : nothing}\n\n                  ${grouped.map(\n                    (group, gi) => html`\n                      ${group.heading\n                        ? html`\n                            <div class=\"text-muted-foreground px-2 py-1.5 text-xs font-semibold\">\n                              ${group.heading}\n                            </div>\n                          `\n                        : nothing}\n                      ${group.items.map((opt) => {\n                        const v = this.getValue(opt)\n                        const isSelected = selectedSet.has(v)\n                        const disabled = this.isDisabledOption(opt) || (atMax && !isSelected)\n                        return html`\n                          <button\n                            type=\"button\"\n                            ?disabled=${disabled}\n                            class=${cn(\n                              'relative flex w-full cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-none select-none hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground',\n                              disabled && 'pointer-events-none opacity-50 cursor-not-allowed',\n                            )}\n                            @click=${() => this.selectOption(opt)}\n                          >\n                            <span class=${cn('size-4 shrink-0 flex items-center justify-center', isSelected ? 'opacity-100' : 'opacity-0')}>\n                              ${icon(Check, 'check', 'size-4')}\n                            </span>\n                            <span class=\"flex-1 truncate text-left\">${this.getLabel(opt)}</span>\n                          </button>\n                        `\n                      })}\n                    `,\n                  )}\n\n                  ${this.query.trim() &&\n                  (this.allowCreate || this.mode === 'tags') &&\n                  !this.options.some((o) => this.getLabel(o) === this.query.trim())\n                    ? html`\n                        <button\n                          type=\"button\"\n                          class=\"relative flex w-full cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-none select-none hover:bg-accent hover:text-accent-foreground text-left\"\n                          @click=${this.createTag}\n                        >\n                          <span class=\"size-4 shrink-0 opacity-0\"></span>\n                          <span>Create \"${this.query.trim()}\"</span>\n                        </button>\n                      `\n                    : nothing}\n                </div>\n\n                ${isMultiple && selectedOpts.length > 0\n                  ? html`\n                      <div class=\"flex items-center justify-between border-t px-2 py-1.5 text-xs\">\n                        <span class=\"text-muted-foreground\">${selectedOpts.length} selected</span>\n                        <button\n                          type=\"button\"\n                          class=\"text-muted-foreground hover:text-foreground cursor-pointer\"\n                          @click=${() => this.clearAll()}\n                        >\n                          Clear all\n                        </button>\n                      </div>\n                    `\n                  : nothing}\n              </div>\n            `\n          : nothing}\n      </div>\n    `\n  }\n}\n\ncustomElements.get('uip-advance-select') || customElements.define('uip-advance-select', UipAdvanceSelect)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-advance-select': UipAdvanceSelect\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/advance-select/advance-select.ts"
    }
  ],
  "dependencies": [
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
