fix(primitives): make v-model, native attributes and panel styling usable under strict TS
Publish to NPM / Check version changes and publish (push) Successful in 11m33s

Consuming the package under `verbatimModuleSyntax` + `strictTemplates` surfaced
four defects that forced workarounds downstream.

- Drop the deprecated `SelectValue` string alias. It collided with the
  `SelectValue` component exported from the same barrel, so the component
  resolved to the type meaning and could not be imported (TS1484).

- Narrow the select's model to `SelectModelValue<T, Multiple>` and make
  `TabsRoot` generic over its value, so a plain `v-model` on a `Ref<string>`
  type-checks. Both roots declare the value prop and emit explicitly instead of
  via `defineModel`, which would widen the payload with `| undefined` even
  though neither control ever clears its value.

- Stop declaring `defineModel` keys in `defineEmits` as well. The duplicate
  erased the payload type from the generated declarations, shipping
  `(...args: unknown[]) => any` for eleven components' model events.

- Let every part accept global DOM attributes (`id`, `role`, `aria-*`,
  `data-*`, ...) through `PrimitiveAttributes`. The heritage clause is marked
  `@vue-ignore`, so they stay out of the runtime props and keep falling through
  via `$attrs` exactly as before.

- Give `SelectContent` and `SelectViewport` a single styleable root: the
  content forwards `$attrs` onto the panel, and the viewport's scrollbar CSS
  moves to a reference-counted `<head>` style tag. A forwarded `class` was
  previously dropped, leaving the panel unstyled.

The tsconfig vue preset gains `htmlAttributes: ["aria-*", "data-*"]` so
hyphenated data attributes are not camelized before they reach those types.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 00:14:44 +07:00
parent e59e05ac05
commit 1d2130f279
24 changed files with 260 additions and 78 deletions
@@ -38,6 +38,11 @@ export interface NavigationMenuRootProps extends PrimitiveProps {
unmountOnHide?: boolean;
}
/**
* Emit contract for `NavigationMenuRoot`. The model events are declared by `defineModel`:
* passing a model key through `defineEmits` as well erases its payload type
* from the generated declarations, leaving consumers with `unknown`.
*/
export interface NavigationMenuRootEmits {
'update:modelValue': [value: string];
}
@@ -70,8 +75,6 @@ const {
as = 'nav',
} = defineProps<NavigationMenuRootProps>();
defineEmits<NavigationMenuRootEmits>();
defineSlots<{
default?: (props: { modelValue: string }) => unknown;
}>();