{"_id":"@affino/combobox-core","_rev":"2-e352712ac6d5480ef40046318d133145","name":"@affino/combobox-core","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@affino/combobox-core","version":"0.1.0","keywords":["combobox","headless","listbox","accessibility"],"author":{"name":"Anton Pavlov","email":"a.pavlov@affino.dev"},"license":"MIT","_id":"@affino/combobox-core@0.1.0","maintainers":[{"name":"affino","email":"anton.pavlov.personal@gmail.com"}],"homepage":"https://affino.dev","bugs":{"url":"https://github.com/affinio/affinio/issues"},"dist":{"shasum":"ea3dda47f54f0277c1d239559442bedf7d7bd263","tarball":"https://registry.npmjs.org/@affino/combobox-core/-/combobox-core-0.1.0.tgz","fileCount":6,"integrity":"sha512-iEVvKzLteVD5fBANU7brupodyCDcE2RfK7Z7dpkq+Ca2/JsB/5MOhmPR1TZjX4OzceJYSs194VNt1PCUmJ8taQ==","signatures":[{"sig":"MEUCIEdt4ma+K4Zfcyg+XWrBDnSCPOHnqR+iG6vfuRTsrwqgAiEAg6NJ6VmgmhIZVEoZXYP7dg9yycplkV6wdZFi/1nSluo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":13861},"main":"dist/index.js","type":"module","_from":"file:affino-combobox-core-0.1.0.tgz","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"pnpm run clean && tsc -p tsconfig.json","clean":"rm -rf dist tsconfig.tsbuildinfo"},"_npmUser":{"name":"affino","email":"anton.pavlov.personal@gmail.com"},"_resolved":"/tmp/d8eb268dbb61aad7399c2c17b266a58d/affino-combobox-core-0.1.0.tgz","_integrity":"sha512-iEVvKzLteVD5fBANU7brupodyCDcE2RfK7Z7dpkq+Ca2/JsB/5MOhmPR1TZjX4OzceJYSs194VNt1PCUmJ8taQ==","repository":{"url":"git+https://github.com/affinio/affinio.git#main","type":"git"},"_npmVersion":"10.8.2","description":"Headless combobox state helpers composed with @affino/listbox-core","directories":{},"sideEffects":false,"_nodeVersion":"20.20.0","dependencies":{"@affino/listbox-core":"^1.1.0","@affino/selection-core":"^1.1.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.15","@vitest/coverage-v8":"^4.0.15"},"_npmOperationalInternal":{"tmp":"tmp/combobox-core_0.1.0_1771104678489_0.28994348860794195","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@affino/combobox-core","version":"0.1.1","author":{"name":"Anton Pavlov","email":"a.pavlov@affino.dev"},"type":"module","description":"Headless combobox core for Affino UI primitives","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"main":"dist/index.js","types":"dist/index.d.ts","sideEffects":false,"dependencies":{"@affino/selection-core":"^1.1.1","@affino/listbox-core":"^1.1.0"},"devDependencies":{"@vitest/coverage-v8":"^4.0.15","vitest":"^4.0.15"},"repository":{"type":"git","url":"git+https://github.com/affinio/affinio.git","directory":"packages/combobox-core"},"homepage":"https://github.com/affinio/affinio/tree/main/packages/combobox-core#readme","license":"MIT","keywords":["combobox","headless","listbox","accessibility"],"bugs":{"url":"https://github.com/affinio/affinio/issues"},"module":"dist/index.js","scripts":{"clean":"rm -rf dist tsconfig.tsbuildinfo","build":"pnpm run clean && tsc -p tsconfig.json","test":"vitest run"},"_id":"@affino/combobox-core@0.1.1","_integrity":"sha512-yjpQdtMtCrbRbK3n0TNDToYN2XxblPuJ8sQLknUiYTjHsQqLsT8JuTBJEcrP3vFMTVwtrVfA5C+7ENL3B3Pf6Q==","_resolved":"/tmp/e0209a1ea1fee03bf690e02b4432ce9c/affino-combobox-core-0.1.1.tgz","_from":"file:affino-combobox-core-0.1.1.tgz","_nodeVersion":"20.20.1","_npmVersion":"10.8.2","dist":{"integrity":"sha512-yjpQdtMtCrbRbK3n0TNDToYN2XxblPuJ8sQLknUiYTjHsQqLsT8JuTBJEcrP3vFMTVwtrVfA5C+7ENL3B3Pf6Q==","shasum":"f6cdb956b29cc748e8117bbe90a2613ef64b2170","tarball":"https://registry.npmjs.org/@affino/combobox-core/-/combobox-core-0.1.1.tgz","fileCount":5,"unpackedSize":12853,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCCQXNhWwIKQTk3CDsdBScRcNNwBSC3iXxxKTSdoSfUVwIhANi0KmAbX1zbjuBjXFZQCJc3+Sjj1dYoFzKJLhS3IdeC"}]},"_npmUser":{"name":"affino","email":"anton.pavlov.personal@gmail.com"},"directories":{},"maintainers":[{"name":"affino","email":"anton.pavlov.personal@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/combobox-core_0.1.1_1775161451698_0.23485052579380095"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-14T21:31:18.373Z","modified":"2026-04-02T20:24:12.419Z","0.1.0":"2026-02-14T21:31:18.633Z","0.1.1":"2026-04-02T20:24:11.976Z"},"bugs":{"url":"https://github.com/affinio/affinio/issues"},"author":{"name":"Anton Pavlov","email":"a.pavlov@affino.dev"},"license":"MIT","homepage":"https://github.com/affinio/affinio/tree/main/packages/combobox-core#readme","keywords":["combobox","headless","listbox","accessibility"],"repository":{"type":"git","url":"git+https://github.com/affinio/affinio.git","directory":"packages/combobox-core"},"description":"Headless combobox core for Affino UI primitives","maintainers":[{"name":"affino","email":"anton.pavlov.personal@gmail.com"}],"readme":"# @affino/combobox-core\n\nHeadless combobox reducer that layers filtering + disclosure state on top of the listbox primitives from `@affino/listbox-core`.\n\n## Highlights\n\n- Pure data structures: no DOM, frameworks, or side effects\n- Compatible with single or multiple selection modes\n- Delegates navigation + activation to `@affino/listbox-core` for predictable focus management\n- Ships tiny helpers for working with selection snapshots in DOM adapters\n\n## Quick start\n\n```ts\nimport {\n  createComboboxState,\n  moveComboboxFocus,\n  activateComboboxIndex,\n  setComboboxOpen,\n  setComboboxFilter,\n} from \"@affino/combobox-core\"\n\nconst context = {\n  mode: \"single\" as const,\n  loop: true,\n  disabled: false,\n  optionCount: filteredOptions.length,\n  isDisabled: (index: number) => filteredOptions[index]?.disabled ?? false,\n}\n\nlet state = createComboboxState()\nstate = setComboboxOpen(state, true)\nstate = setComboboxFilter(state, \"a\")\nstate = moveComboboxFocus({ state, context, delta: 1 })\nstate = activateComboboxIndex({ state, context, index: state.listbox.activeIndex })\n```\n\n## Filter lifecycle (recommended)\n\nTreat the adapter as owner of the option projection and keep this order:\n\n1. User types: call `setComboboxFilter(state, value)`.\n2. Adapter recomputes `filteredOptions` from the new `state.filter`.\n3. Adapter rebuilds `ComboboxContext` from `filteredOptions`.\n4. Keyboard/pointer actions call `moveComboboxFocus` / `activateComboboxIndex` with that context.\n5. On clear action, call `clearComboboxSelection(state)` and then decide in adapter whether to close (`setComboboxOpen(..., false)`) or keep open.\n\nContract details:\n\n- `setComboboxFilter` only updates `state.filter` (no implicit open/close, no selection mutation).\n- `setComboboxOpen` only updates disclosure state.\n- `clearComboboxSelection` resets `filter` + listbox selection/focus and intentionally keeps `open` unchanged.\n\n## Adapter boundaries\n\n`combobox-core` owns:\n\n- filter string state,\n- open/closed flag state,\n- focus/selection transitions delegated to `listbox-core`,\n- single vs multiple mode guardrails (`toggle`/`extend` ignored in single mode).\n\nAdapter owns:\n\n- how options are filtered and rendered,\n- DOM indexing and `aria-activedescendant` wiring,\n- side effects (scrolling, focus restore, announcements, async fetch, virtualization),\n- policy choices (open on input, close on select, keep-open multi-select flow).\n\nAnti-patterns to avoid:\n\n- Passing full dataset counts to `optionCount` while rendering only filtered rows.\n- Mutating `state.listbox.selection` directly after calling core operations.\n- Encoding close/open side effects inside filter logic instead of explicit `setComboboxOpen` calls.\n\nUse this module inside renderer-specific adapters (Vue, React, DOM, Livewire, etc.) to keep UI code declarative and focused on attributes + events.\n","readmeFilename":"README.md"}