--- name: fantasia-select-input description: >- Reusable FaSelectInput QSelect chips wrapper and src/scripts/faSelectInput helpers: modes simple / document / otherType / tags (media stub), filter highlight, create-new, clearIsNewFlags. Use when adding select/chip fields or wiring document/template options. --- # Fantasia Archive — FaSelectInput ## When to use Reusable **`q-select`** with chips, filter input, whole-word label highlight, optional create-new → **`FaSelectInput`** from **`src/components/elements/FaSelectInput/`**. **No** one-off chip menus that reimplement this contract. **Do not** use **`mode="media"`** yet — typed stub; options always normalize to **`[]`**. **Live smoke only** (not persisted document field): **`DocumentWorkspacePageSelectSmoke`** under dead toggle on **`DocumentWorkspacePage`**. App Settings enum **`q-select`** stays in **`DialogAppSettingsSettingBlock`** unless product wants this chip UX. ## Public API (SFC) ```vue ``` | Prop | Role | | --- | --- | | **`mode`** | Required — **`simple`** \| **`document`** \| **`otherType`** \| **`tags`** \| **`media`** (stub) | | **`modelValue`** | String(s) or object item(s) / **`null`** per mode | | **`options`** | Strings or **`I_faSelectInputObjectItem`** list | | **`testLocator`** | Root **`data-test-locator`**; chip / option / separator-alt attrs | | **`allowCreateNew`** | Opt-in Enter create-new (default **`false`**) | | **`clearInputOnSelect`** | Opt-in clear filter text after select/create (default **`false`**; chip stays) | | **`activateOnly`** | Opt-in: option click/Enter emit **`option-activate` only** — no Quasar model select / menu close (Quick Search stay-open) | | **`chipRemovable`** | Chip X remove control (default **`true`**); set **`false`** for mandatory single (chip stays, no X) | | **`filterFn`** | Optional override of default filter | | **`multiple`**, **`label`**, **`loading`**, **`disable`**, Quasar chrome | Standard defaults: dense filled dark, color **`primary-bright`** | **Emits:** **`update:modelValue`**, **`change`** (`{ action, value }`), **`new-value`**, **`request-options`** (focus + popup-show), **`option-activate`** (option click or Enter on focused option, including single reselect). **Expose:** **`clearIsNewFlags(ids)`**, **`openPopup()`**, **`getFilterNeedle()`**. **Test hooks:** **`{testLocator}`**, **`-filter`**, **`-chip`**, **`-selected`** (inline), **`-option-{index}`**, **`data-test-locator-separator-alt`** on option (index ≥ 1). ## UX behavior - Default presentation **chips**; **`selectionPresentation="inline"`** for icon+label closed field (no chips) - **`chipRemovable`** default **`true`**; mandatory single parents set **`false`** (chip, no X) - Optional object **`color`** tints icons via **`fa-color-glyph`** (object icons always get that class + glyph CSS vars; flat = solid color; fantasy = shading; missing color uses workspace-row default base) - Hide empty single selection chip; **`isNew`** → teal-3 chip else accent; text dark - Object **`icon`** on chip/option/inline when non-empty - Filter = FA 1.0 multi-token match: space-split query words AND-matched against distinct label words (equality and/or includes); highlight claimed words only (not letter-only wash); results sorted exact → full-word → partial - **Default:** keyboard-highlight first option on popup-show and after every filter update (model unchanged) - **Focus opens menu:** Tab / Shift+Tab via **`keyup` Tab** (not `@focus` `showPopup` — that races Quasar click toggle; **`QSelect` `inheritAttrs: false`** drops wrapper `@mousedown`); parents may still call **`openPopup()`** (Quick Add template) - Menu fixed **600px**, center under field (**`bottom middle`** / **`top middle`**); optional **`popupContentClass`**; teleported menu **`z-index` 7002** (above **`QDialog`**) - **separatorAlt** via CSS border on option item (single virtual-scroll root); **`virtual-scroll-slice-size` 80** fills tall menus on first open; selected = side bars on **`::before`** + idle non-match text; filter **optionMatch** gold on selected too; fantasy hover/keyboard = menu bottom-edge glow (**`faMenuItemGlow`** on **`::after`**); label/icon stay idle color (no hover wash tint) - Create-new: trim; simple string or object **`{ id: crypto.randomUUID(), name, isNew: true }`** ## File map | Area | Path | | --- | --- | | SFC + styles | **`FaSelectInput.vue`**, **`styles/_variables.scss`**, **`styles/FaSelectInput.unscoped.scss`** | | Composable | **`scripts/faSelectInput_manager.ts`**, **`scripts/functions/createUseFaSelectInput.ts`** | | Domain | **`src/scripts/faSelectInput/functions/`** + **`faSelectInput_manager.ts`** barrel | | Types | **`types/I_faSelectInput.ts`** | | Storybook | **`_tests/FaSelectInput.stories.ts`** | | Smoke | **`DocumentWorkspacePageSelectSmoke.vue`** + **`mapDocumentWorkspacePageSelectSmokeOptions.ts`** | ## Modes | Mode | Options | Empty single | | --- | --- | --- | | **`simple`** | strings | **`''`** | | **`document`** / **`otherType`** / **`tags`** | objects | **`null`** | | **`media`** | always **`[]`** | stub | Smoke today: simple + **`otherType`** templates + **`document`** docs via projectContent IPC on **`request-options`**. ## Adding to a new screen 1. Import **`FaSelectInput`**; pick **`mode`** 2. Parent owns **`options`** + reload on **`@request-options`** 3. Unique **`test-locator`** 4. Persist create-new: listen **`@new-value`** / **`@change`**; call **`clearIsNewFlags`** when ids saved 5. Stub in parent Vitest when testing layout only ## Two-level layout - Level 1: **`src/scripts/faSelectInput/functions/*.ts`** + element **`createUseFaSelectInput`** - Level 2: element **`faSelectInput_manager.ts`** wires Vue + domain barrel See [fantasia-two-level-architecture](../fantasia-two-level-architecture/SKILL.md). ## Tests | Suite | Location | | --- | --- | | Vitest (SFC) | **`FaSelectInput/_tests/FaSelectInput.vitest.test.ts`** | | Vitest (composable) | **`scripts/_tests/createUseFaSelectInput.vitest.test.ts`** | | Vitest (domain) | **`src/scripts/faSelectInput/functions/_tests/faSelectInputDomain.vitest.test.ts`** | | Smoke | **`DocumentWorkspacePageSelectSmoke.vitest.test.ts`** | | Playwright component | **`FaSelectInput/_tests/FaSelectInput.playwright.test.ts`** — Tab open, reopen click, filter type, separator-alt, tall menu fill, selected-option close | Quick Add dialog PW/E2E also exercise **`FaSelectInput`** world/template fields. ## Related - [fa-select-input.mdc](../../rules/fa-select-input.mdc) - [fa-context-menu-dividers.mdc](../../rules/fa-context-menu-dividers.mdc) - [fantasia-quasar-vue](../fantasia-quasar-vue/SKILL.md)