Files
tools/vue/primitives/package.json
T
robonen 6da5ecaa83
Publish to NPM / Check version changes and publish (push) Has been cancelled
fix(primitives): place the select panel when nothing is selected
Item-aligned placement centres the panel on the selected item, and `position()`
returned early unless both the item and its text node were known. The content
already adopts the first valid item as the anchor when the model matches no
option, but only the item is registered — the text node registers solely for the
selected value, so the pair was never complete and the early return fired. The
wrapper is `position: fixed`, so it stayed at the viewport origin: to a user the
dropdown simply does not open.

This is not an edge case. A stale id, a deleted record or a directory that has
not finished loading all leave the model unmatched, and the whole select then
looks broken rather than merely unlabelled.

Recover the text node from the item's own `aria-labelledby` instead of adding a
second registration — writing the anchor refs from inside the item's tracking
effect closes a reactive cycle ("Maximum recursive updates exceeded"). Reset the
text ref alongside the item ref per open cycle so a stale node cannot pair with
a fresh item. Finally, keep the guard from ever stranding the panel again: with
no anchors at all — an empty option list — fall back to a plain trigger-aligned
drop instead of returning with the wrapper unplaced.

Both paths are covered by browser tests that fail on the previous code.
2026-08-10 04:30:24 +07:00

85 lines
1.9 KiB
JSON

{
"name": "@robonen/primitives",
"version": "0.0.4",
"license": "Apache-2.0",
"description": "Collection of UI primitives",
"keywords": [
"ui",
"primitives",
"components",
"tools"
],
"author": "Robonen Andrew <robonenandrew@gmail.com>",
"repository": {
"type": "git",
"url": "git+https://github.com/robonen/tools.git",
"directory": "vue/primitives"
},
"packageManager": "pnpm@11.18.0",
"engines": {
"node": ">=24.16.0"
},
"type": "module",
"sideEffects": false,
"files": [
"dist"
],
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./*": {
"import": {
"types": "./dist/*/index.d.mts",
"default": "./dist/*/index.mjs"
},
"require": {
"types": "./dist/*/index.d.cts",
"default": "./dist/*/index.cjs"
}
}
},
"scripts": {
"lint:check": "eslint .",
"lint:fix": "eslint . --fix",
"test": "vitest run",
"bench": "vitest bench",
"dev": "vitest dev",
"build": "tsdown"
},
"devDependencies": {
"@robonen/eslint": "workspace:*",
"@robonen/tsconfig": "workspace:*",
"@robonen/tsdown": "workspace:*",
"@vitest/browser": "catalog:",
"@vitest/browser-playwright": "^4.1.10",
"@vue/test-utils": "catalog:",
"axe-core": "^4.12.1",
"eslint": "catalog:",
"playwright": "^1.62.0",
"tsdown": "catalog:",
"unplugin-vue": "^7.2.0",
"vitest-browser-vue": "^2.1.0",
"vue": "catalog:",
"vue-tsc": "^3.3.8"
},
"dependencies": {
"@floating-ui/vue": "^2.0.1",
"@robonen/encoding": "workspace:*",
"@robonen/platform": "workspace:*",
"@robonen/stdlib": "workspace:*",
"@robonen/vue": "workspace:*",
"@vue/shared": "catalog:"
},
"peerDependencies": {
"vue": "^3.5"
}
}