68 lines
2.3 KiB
Vue
68 lines
2.3 KiB
Vue
<script lang="ts">
|
|
import type { MenuItemImplEmits, MenuItemImplProps } from './MenuItemImpl.vue';
|
|
import type { CheckedState } from './types';
|
|
|
|
/**
|
|
* A menu item that toggles a boolean (or indeterminate) state when selected,
|
|
* rendering with `role="menuitemcheckbox"`. Pair it with MenuItemIndicator to
|
|
* show a check mark. Bind `v-model:checked` to control its state, or leave it
|
|
* uncontrolled with `defaultChecked`.
|
|
*/
|
|
export interface MenuCheckboxItemProps extends MenuItemImplProps {
|
|
/** The controlled checked state. Use together with `update:checked`; may be `'indeterminate'`. */
|
|
checked?: CheckedState;
|
|
/** The checked state when uncontrolled. */
|
|
defaultChecked?: CheckedState;
|
|
}
|
|
export interface MenuCheckboxItemEmits extends MenuItemImplEmits {
|
|
'update:checked': [value: CheckedState];
|
|
}
|
|
</script>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref } from 'vue';
|
|
|
|
import { provideMenuItemIndicatorContext, useMenuRootContext } from './context';
|
|
import MenuItemImpl from './MenuItemImpl.vue';
|
|
import { ITEM_SELECT, getCheckedState, isIndeterminate } from './utils';
|
|
|
|
const {
|
|
checked: checkedProp,
|
|
defaultChecked = false,
|
|
...itemProps
|
|
} = defineProps<MenuCheckboxItemProps>();
|
|
|
|
const emit = defineEmits<MenuCheckboxItemEmits>();
|
|
const rootCtx = useMenuRootContext();
|
|
|
|
const local = ref<CheckedState>(defaultChecked);
|
|
const checkedState = computed<CheckedState>(() => checkedProp !== undefined ? checkedProp : local.value);
|
|
|
|
provideMenuItemIndicatorContext({ checkedState });
|
|
|
|
function handleSelect(event: Event) {
|
|
const next: CheckedState = isIndeterminate(checkedState.value) ? true : !checkedState.value;
|
|
local.value = next;
|
|
emit('update:checked', next);
|
|
|
|
const target = event.currentTarget as HTMLElement;
|
|
const selectEvent = new CustomEvent(ITEM_SELECT, { bubbles: true, cancelable: true });
|
|
// Emit the cancelable ITEM_SELECT event so `@select` preventDefault works.
|
|
target.addEventListener(ITEM_SELECT, e => emit('select', e), { once: true });
|
|
target.dispatchEvent(selectEvent);
|
|
if (!selectEvent.defaultPrevented) rootCtx.onClose();
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<MenuItemImpl
|
|
v-bind="itemProps"
|
|
role="menuitemcheckbox"
|
|
:aria-checked="isIndeterminate(checkedState) ? 'mixed' : checkedState"
|
|
:data-state="getCheckedState(checkedState)"
|
|
@select="handleSelect"
|
|
>
|
|
<slot />
|
|
</MenuItemImpl>
|
|
</template>
|