routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
||||
@layer vuetify-components {
|
||||
.v-menu > .v-overlay__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.v-menu > .v-overlay__content {
|
||||
border-radius: 4px;
|
||||
}
|
||||
.v-menu > .v-overlay__content > .v-card,
|
||||
.v-menu > .v-overlay__content > .v-sheet,
|
||||
.v-menu > .v-overlay__content > .v-list {
|
||||
background: rgb(var(--v-theme-surface));
|
||||
border-radius: inherit;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
.v-menu > .v-overlay__content > .v-card,
|
||||
.v-menu > .v-overlay__content > .v-sheet,
|
||||
.v-menu > .v-overlay__content > .v-list {
|
||||
box-shadow: 0px 1px 3px 0px rgba(var(--v-shadow-color), var(--v-shadow-key-opacity, 0.3)), 0px 4px 8px 3px rgba(var(--v-shadow-color), var(--v-shadow-ambient-opacity, 0.15));
|
||||
--v-elevation-overlay: color-mix(in srgb, var(--v-elevation-overlay-color) 6%, transparent);
|
||||
}
|
||||
}
|
||||
+3480
File diff suppressed because it is too large
Load Diff
+161
@@ -0,0 +1,161 @@
|
||||
import { createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
|
||||
// Styles
|
||||
import "./VMenu.css";
|
||||
|
||||
// Components
|
||||
import { VDialogTransition } from "../transitions/index.js";
|
||||
import { VDefaultsProvider } from "../VDefaultsProvider/index.js";
|
||||
import { VOverlay } from "../VOverlay/index.js";
|
||||
import { makeVOverlayProps } from "../VOverlay/VOverlay.js"; // Composables
|
||||
import { forwardRefs } from "../../composables/forwardRefs.js";
|
||||
import { useRtl } from "../../composables/locale.js";
|
||||
import { useProxiedModel } from "../../composables/proxiedModel.js";
|
||||
import { useScopeId } from "../../composables/scopeId.js"; // Utilities
|
||||
import { computed, inject, mergeProps, onBeforeUnmount, onDeactivated, provide, ref, shallowRef, toRef, useId, watch } from 'vue';
|
||||
import { VMenuSymbol } from "./shared.js";
|
||||
import { focusableChildren, focusChild, genericComponent, getNextElement, isClickInsideElement, omit, propsFactory, useRender } from "../../util/index.js"; // Types
|
||||
export const makeVMenuProps = propsFactory({
|
||||
// TODO
|
||||
// disableKeys: Boolean,
|
||||
id: String,
|
||||
submenu: Boolean,
|
||||
...omit(makeVOverlayProps({
|
||||
captureFocus: true,
|
||||
closeDelay: 250,
|
||||
closeOnContentClick: true,
|
||||
locationStrategy: 'connected',
|
||||
location: undefined,
|
||||
openDelay: 300,
|
||||
scrim: false,
|
||||
scrollStrategy: 'reposition',
|
||||
transition: {
|
||||
component: VDialogTransition
|
||||
}
|
||||
}), ['absolute'])
|
||||
}, 'VMenu');
|
||||
export const VMenu = genericComponent()({
|
||||
name: 'VMenu',
|
||||
props: makeVMenuProps(),
|
||||
emits: {
|
||||
'update:modelValue': value => true
|
||||
},
|
||||
setup(props, {
|
||||
slots
|
||||
}) {
|
||||
const isActive = useProxiedModel(props, 'modelValue');
|
||||
const {
|
||||
scopeId
|
||||
} = useScopeId();
|
||||
const {
|
||||
isRtl
|
||||
} = useRtl();
|
||||
const uid = useId();
|
||||
const id = toRef(() => props.id || `v-menu-${uid}`);
|
||||
const overlay = ref();
|
||||
const parent = inject(VMenuSymbol, null);
|
||||
const openChildren = shallowRef(new Set());
|
||||
provide(VMenuSymbol, {
|
||||
register() {
|
||||
openChildren.value.add(uid);
|
||||
},
|
||||
unregister() {
|
||||
openChildren.value.delete(uid);
|
||||
},
|
||||
closeParents(e) {
|
||||
setTimeout(() => {
|
||||
if (!openChildren.value.size && !props.persistent && (e == null || overlay.value?.contentEl && !isClickInsideElement(e, overlay.value.contentEl))) {
|
||||
isActive.value = false;
|
||||
parent?.closeParents();
|
||||
}
|
||||
}, 40);
|
||||
}
|
||||
});
|
||||
onBeforeUnmount(() => parent?.unregister());
|
||||
onDeactivated(() => isActive.value = false);
|
||||
watch(isActive, val => {
|
||||
val ? parent?.register() : parent?.unregister();
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
function onClickOutside(e) {
|
||||
parent?.closeParents(e);
|
||||
}
|
||||
function onKeydown(e) {
|
||||
if (props.disabled) return;
|
||||
if (e.key === 'Tab' || e.key === 'Enter' && !props.closeOnContentClick) {
|
||||
if (e.key === 'Enter' && (e.target instanceof HTMLTextAreaElement || e.target instanceof HTMLInputElement && !!e.target.closest('form'))) return;
|
||||
if (e.key === 'Enter') e.preventDefault();
|
||||
const nextElement = getNextElement(focusableChildren(overlay.value?.contentEl, false), e.shiftKey ? 'prev' : 'next', el => el.tabIndex >= 0);
|
||||
if (!nextElement && !props.retainFocus) {
|
||||
isActive.value = false;
|
||||
overlay.value?.activatorEl?.focus();
|
||||
}
|
||||
} else if (props.submenu && e.key === (isRtl.value ? 'ArrowRight' : 'ArrowLeft')) {
|
||||
isActive.value = false;
|
||||
overlay.value?.activatorEl?.focus();
|
||||
}
|
||||
}
|
||||
function onActivatorKeydown(e) {
|
||||
if (props.disabled) return;
|
||||
const el = overlay.value?.contentEl;
|
||||
if (el && isActive.value) {
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
focusChild(el, 'next');
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
focusChild(el, 'prev');
|
||||
} else if (props.submenu) {
|
||||
if (e.key === (isRtl.value ? 'ArrowRight' : 'ArrowLeft')) {
|
||||
isActive.value = false;
|
||||
} else if (e.key === (isRtl.value ? 'ArrowLeft' : 'ArrowRight')) {
|
||||
e.preventDefault();
|
||||
focusChild(el, 'first');
|
||||
}
|
||||
}
|
||||
} else if (props.submenu ? e.key === (isRtl.value ? 'ArrowLeft' : 'ArrowRight') : ['ArrowDown', 'ArrowUp'].includes(e.key)) {
|
||||
isActive.value = true;
|
||||
e.preventDefault();
|
||||
setTimeout(() => setTimeout(() => onActivatorKeydown(e)));
|
||||
}
|
||||
}
|
||||
const activatorProps = computed(() => mergeProps({
|
||||
'aria-haspopup': 'menu',
|
||||
'aria-expanded': String(isActive.value),
|
||||
'aria-controls': id.value,
|
||||
'aria-owns': id.value,
|
||||
onKeydown: onActivatorKeydown
|
||||
}, props.activatorProps));
|
||||
useRender(() => {
|
||||
const overlayProps = VOverlay.filterProps(props);
|
||||
return _createVNode(VOverlay, _mergeProps({
|
||||
"ref": overlay,
|
||||
"id": id.value,
|
||||
"class": ['v-menu', props.class],
|
||||
"style": props.style
|
||||
}, overlayProps, {
|
||||
"modelValue": isActive.value,
|
||||
"onUpdate:modelValue": $event => isActive.value = $event,
|
||||
"absolute": true,
|
||||
"activatorProps": activatorProps.value,
|
||||
"location": props.location ?? (props.submenu ? 'end' : 'bottom'),
|
||||
"onClick:outside": onClickOutside,
|
||||
"onKeydown": onKeydown
|
||||
}, scopeId), {
|
||||
activator: slots.activator,
|
||||
default: (...args) => _createVNode(VDefaultsProvider, {
|
||||
"root": "VMenu"
|
||||
}, {
|
||||
default: () => [slots.default?.(...args)]
|
||||
})
|
||||
});
|
||||
});
|
||||
return forwardRefs({
|
||||
id,
|
||||
ΨopenChildren: openChildren
|
||||
}, overlay);
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=VMenu.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+19
@@ -0,0 +1,19 @@
|
||||
@use '../../styles/tools'
|
||||
@use './variables' as *
|
||||
|
||||
@include tools.layer('components')
|
||||
.v-menu
|
||||
> .v-overlay__content
|
||||
display: flex
|
||||
flex-direction: column
|
||||
@include tools.rounded($menu-content-border-radius)
|
||||
|
||||
> .v-card,
|
||||
> .v-sheet,
|
||||
> .v-list
|
||||
background: rgb(var(--v-theme-surface))
|
||||
border-radius: inherit
|
||||
overflow: auto
|
||||
height: 100%
|
||||
|
||||
@include tools.elevation($menu-elevation)
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@use '../../styles/settings';
|
||||
|
||||
$menu-elevation: 3 !default;
|
||||
$menu-content-border-radius: settings.$border-radius-root !default;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { VMenu } from './VMenu.js';
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export { VMenu } from "./VMenu.js";
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","names":["VMenu"],"sources":["../../../src/components/VMenu/index.ts"],"sourcesContent":["export { VMenu } from './VMenu'\n"],"mappings":"SAASA,KAAK","ignoreList":[]}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import type { InjectionKey } from 'vue';
|
||||
interface MenuProvide {
|
||||
register(): void;
|
||||
unregister(): void;
|
||||
closeParents(e?: MouseEvent): void;
|
||||
}
|
||||
export declare const VMenuSymbol: InjectionKey<MenuProvide>;
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// Types
|
||||
|
||||
export const VMenuSymbol = Symbol.for('vuetify:v-menu');
|
||||
//# sourceMappingURL=shared.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"shared.js","names":["VMenuSymbol","Symbol","for"],"sources":["../../../src/components/VMenu/shared.ts"],"sourcesContent":["// Types\nimport type { InjectionKey } from 'vue'\n\ninterface MenuProvide {\n register (): void\n unregister (): void\n closeParents (e?: MouseEvent): void\n}\n\nexport const VMenuSymbol: InjectionKey<MenuProvide> = Symbol.for('vuetify:v-menu')\n"],"mappings":"AAAA;;AASA,OAAO,MAAMA,WAAsC,GAAGC,MAAM,CAACC,GAAG,CAAC,gBAAgB,CAAC","ignoreList":[]}
|
||||
Reference in New Issue
Block a user