diff --git a/vue/primitives/.vitest-attachments/12dbc42b176f989336a161e4f129aeb48944d8d4.png b/vue/primitives/.vitest-attachments/12dbc42b176f989336a161e4f129aeb48944d8d4.png deleted file mode 100644 index d1c5f08..0000000 Binary files a/vue/primitives/.vitest-attachments/12dbc42b176f989336a161e4f129aeb48944d8d4.png and /dev/null differ diff --git a/vue/primitives/src/avatar/AvatarImage.vue b/vue/primitives/src/avatar/AvatarImage.vue index ce262b9..3f8b0f7 100644 --- a/vue/primitives/src/avatar/AvatarImage.vue +++ b/vue/primitives/src/avatar/AvatarImage.vue @@ -43,7 +43,7 @@ function load(nextSrc: string | undefined) { setStatus('error'); return; } - if (typeof globalThis.window === 'undefined') { + if (globalThis.window === undefined) { setStatus('loading'); return; } diff --git a/vue/primitives/src/combobox/ComboboxRoot.vue b/vue/primitives/src/combobox/ComboboxRoot.vue index 59d0eb3..5b45c9b 100644 --- a/vue/primitives/src/combobox/ComboboxRoot.vue +++ b/vue/primitives/src/combobox/ComboboxRoot.vue @@ -7,8 +7,6 @@ export interface ComboboxRootProps modelValue?: T | T[]; /** Uncontrolled initial value. */ defaultValue?: T | T[]; - /** Controlled open state. Use `v-model:open`. */ - open?: boolean; /** Uncontrolled default open state. */ defaultOpen?: boolean; /** Allow selecting multiple values. */ @@ -77,6 +75,7 @@ const config = useConfig(); const direction = computed(() => dir ?? config.dir.value); const localOpen = ref(defaultOpen); +/** Controlled open state. Use `v-model:open`. */ const open = defineModel('open', { default: undefined, get: v => v ?? localOpen.value, diff --git a/vue/primitives/src/command/CommandRoot.vue b/vue/primitives/src/command/CommandRoot.vue index 8df58c2..ad41aac 100644 --- a/vue/primitives/src/command/CommandRoot.vue +++ b/vue/primitives/src/command/CommandRoot.vue @@ -138,7 +138,7 @@ const filteredItems = computed>(() => { function escapeAttr(v: string): string { if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') return CSS.escape(v); - return v.replace(/["\\]/g, '\\$&'); + return v.replaceAll(/["\\]/g, '\\$&'); } function getSelectableItems(): string[] { diff --git a/vue/primitives/src/env.d.ts b/vue/primitives/src/env.d.ts index b9d14ca..71a152f 100644 --- a/vue/primitives/src/env.d.ts +++ b/vue/primitives/src/env.d.ts @@ -5,13 +5,9 @@ declare global { } declare module 'vue' { - interface ComponentCustomProps { - [key: `data${string}`]: unknown; - } + type ComponentCustomProps = Record<`data${string}`, unknown>; } declare module 'vue' { - interface HTMLAttributes { - [key: `data-${string}`]: unknown; - } + type HTMLAttributes = Record<`data-${string}`, unknown>; } diff --git a/vue/primitives/src/hover-card/HoverCardRoot.vue b/vue/primitives/src/hover-card/HoverCardRoot.vue index 09feb72..402c063 100644 --- a/vue/primitives/src/hover-card/HoverCardRoot.vue +++ b/vue/primitives/src/hover-card/HoverCardRoot.vue @@ -21,6 +21,9 @@ const { openDelay = 700, closeDelay = 300, defaultOpen = false } = defineProps('open', { default: undefined }); const uncontrolled = ref(defaultOpen); +// `open` intentionally shares the model name: it's a local read-only computed that +// resolves the controlled model against the uncontrolled fallback. Safe in script-setup. +// eslint-disable-next-line vue/no-dupe-keys const open = computed(() => openModel.value ?? uncontrolled.value); function setOpen(value: boolean) { diff --git a/vue/primitives/src/navigation-menu/NavigationMenuList.vue b/vue/primitives/src/navigation-menu/NavigationMenuList.vue index 8b36fc2..381d372 100644 --- a/vue/primitives/src/navigation-menu/NavigationMenuList.vue +++ b/vue/primitives/src/navigation-menu/NavigationMenuList.vue @@ -1,4 +1,7 @@ diff --git a/vue/primitives/src/navigation-menu/NavigationMenuRoot.vue b/vue/primitives/src/navigation-menu/NavigationMenuRoot.vue index 727d140..c4f56b4 100644 --- a/vue/primitives/src/navigation-menu/NavigationMenuRoot.vue +++ b/vue/primitives/src/navigation-menu/NavigationMenuRoot.vue @@ -3,8 +3,6 @@ import type { Direction } from '../config-provider'; import type { Orientation } from '../roving-focus'; export interface NavigationMenuRootProps { - /** Controlled active item value. Use `v-model`. */ - modelValue?: string; /** Uncontrolled initial value. */ defaultValue?: string; /** Reading direction. Falls back to `ConfigProvider`. */ @@ -72,6 +70,7 @@ const config = useConfig(); const dirRef = computed(() => dir ?? config.dir.value); const localValue = ref(defaultValue ?? ''); +/** Controlled active item value. Use `v-model`. */ const modelValue = defineModel({ default: undefined, get: v => v ?? localValue.value, @@ -212,7 +211,7 @@ provideNavigationMenuContext({ (); const localValue = ref(defaultValue ?? ''); +/** Controlled active value of the submenu. Use `v-model`. */ const modelValue = defineModel({ default: undefined, get: v => v ?? localValue.value, diff --git a/vue/primitives/src/pin-input/PinInputInput.vue b/vue/primitives/src/pin-input/PinInputInput.vue index fa0cc5d..814d8a5 100644 --- a/vue/primitives/src/pin-input/PinInputInput.vue +++ b/vue/primitives/src/pin-input/PinInputInput.vue @@ -102,7 +102,7 @@ function onPaste(e: ClipboardEvent): void { return; e.preventDefault(); const chars = ctx.type.value === 'number' - ? data.replace(NON_DIGIT_G, '').split('') + ? data.replaceAll(NON_DIGIT_G, '').split('') : data.split(''); let idx = props.index; for (const ch of chars) { diff --git a/vue/primitives/src/tags-input/TagsInputItemDelete.vue b/vue/primitives/src/tags-input/TagsInputItemDelete.vue index 2b9b350..ee5b05d 100644 --- a/vue/primitives/src/tags-input/TagsInputItemDelete.vue +++ b/vue/primitives/src/tags-input/TagsInputItemDelete.vue @@ -17,7 +17,7 @@ const { forwardRef } = useForwardExpose(); function onClick(): void { if (item.disabled.value) return; - const idx = ctx.modelValue.value.findIndex(v => v === item.value.value); + const idx = ctx.modelValue.value.indexOf(item.value.value); if (idx !== -1) ctx.onRemoveValue(idx); } diff --git a/vue/primitives/src/tags-input/__test__/TagsInput.test.ts b/vue/primitives/src/tags-input/__test__/TagsInput.test.ts index 5d66983..d53918c 100644 --- a/vue/primitives/src/tags-input/__test__/TagsInput.test.ts +++ b/vue/primitives/src/tags-input/__test__/TagsInput.test.ts @@ -120,8 +120,8 @@ describe('TagsInput', () => { it('delete trigger removes its tag', async () => { const w = createTagsInput({ defaultValue: ['a', 'b'] }); - const deleteBtns = w.findAll('button').filter(b => b.text() === '×'); - await deleteBtns[0]!.trigger('click'); + const deleteBtn = w.findAll('button').find(b => b.text() === '×'); + await deleteBtn!.trigger('click'); await nextTick(); const items = w.findAllComponents(TagsInputItem as Component); expect(items).toHaveLength(1);