mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-06 00:32:02 +02:00
refactor useNavLayout
This commit is contained in:
@@ -7,10 +7,15 @@ import {
|
||||
DASHBOARD_NAV_KEY_PREFIX,
|
||||
navDefinitions
|
||||
} from '../../../shared/constants';
|
||||
import { triggerNavEntryAction } from '../navActionUtils';
|
||||
import {
|
||||
buildMenuItems,
|
||||
collectLayoutKeys,
|
||||
findFirstNavEntry,
|
||||
findFirstNavKey
|
||||
} from '../navLayoutHelpers';
|
||||
import { normalizeHiddenKeys, sanitizeLayout } from '../navMenuUtils';
|
||||
|
||||
const DEFAULT_FOLDER_ICON = 'ri-folder-line';
|
||||
|
||||
export function useNavLayout({
|
||||
t,
|
||||
locale,
|
||||
@@ -72,82 +77,16 @@ export function useNavLayout({
|
||||
{ type: 'item', key: 'direct-access' }
|
||||
];
|
||||
|
||||
const menuItems = computed(() => {
|
||||
const items = [];
|
||||
navLayout.value.forEach((entry) => {
|
||||
if (entry.type === 'item') {
|
||||
const definition = navDefinitionMap.value.get(entry.key);
|
||||
if (!definition) {
|
||||
return;
|
||||
}
|
||||
items.push({
|
||||
...definition,
|
||||
index: definition.key,
|
||||
title: definition.tooltip || definition.labelKey,
|
||||
titleIsCustom: Boolean(definition.isDashboard)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.type === 'folder') {
|
||||
const folderDefinitions = (entry.items || [])
|
||||
.map((key) => navDefinitionMap.value.get(key))
|
||||
.filter(Boolean);
|
||||
if (folderDefinitions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const folderEntries = folderDefinitions.map((definition) => ({
|
||||
label: definition.labelKey,
|
||||
routeName: definition.routeName,
|
||||
routeParams: definition.routeParams,
|
||||
index: definition.key,
|
||||
icon: definition.icon,
|
||||
action: definition.action,
|
||||
titleIsCustom: Boolean(definition.isDashboard)
|
||||
}));
|
||||
|
||||
items.push({
|
||||
index: entry.id,
|
||||
icon: entry.icon || DEFAULT_FOLDER_ICON,
|
||||
title:
|
||||
entry.name?.trim() ||
|
||||
t('nav_menu.custom_nav.folder_name_placeholder'),
|
||||
titleIsCustom: true,
|
||||
children: folderEntries
|
||||
});
|
||||
}
|
||||
});
|
||||
return items;
|
||||
});
|
||||
const menuItems = computed(() =>
|
||||
buildMenuItems(navLayout.value, navDefinitionMap.value, t)
|
||||
);
|
||||
|
||||
const getFirstNavEntryLocal = (layout) => {
|
||||
for (const entry of layout) {
|
||||
if (entry.type === 'item') {
|
||||
const definition = navDefinitionMap.value.get(entry.key);
|
||||
if (
|
||||
definition?.routeName ||
|
||||
definition?.action ||
|
||||
definition?.path
|
||||
) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
if (entry.type === 'folder' && entry.items?.length) {
|
||||
const definition = entry.items
|
||||
.map((key) => navDefinitionMap.value.get(key))
|
||||
.find((def) => def?.routeName || def?.action || def?.path);
|
||||
if (definition) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return findFirstNavEntry(layout, navDefinitionMap.value);
|
||||
};
|
||||
|
||||
const getFirstNavKeyLocal = (layout) => {
|
||||
const entry = getFirstNavEntryLocal(layout);
|
||||
return entry?.key || null;
|
||||
return findFirstNavKey(layout, navDefinitionMap.value);
|
||||
};
|
||||
|
||||
const activeMenuIndex = computed(() => {
|
||||
@@ -182,27 +121,6 @@ export function useNavLayout({
|
||||
return `nav-folder-${dayjs().toISOString()}-${Math.random().toString().slice(2, 4)}`;
|
||||
};
|
||||
|
||||
const collectLayoutKeys = (layout) => {
|
||||
const keys = new Set();
|
||||
if (!Array.isArray(layout)) {
|
||||
return keys;
|
||||
}
|
||||
layout.forEach((entry) => {
|
||||
if (entry?.type === 'item' && entry.key) {
|
||||
keys.add(entry.key);
|
||||
return;
|
||||
}
|
||||
if (entry?.type === 'folder' && Array.isArray(entry.items)) {
|
||||
entry.items.forEach((key) => {
|
||||
if (key) {
|
||||
keys.add(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
};
|
||||
|
||||
const getAppendDefinitions = (layout, hiddenKeys = []) => {
|
||||
const keysInLayout = collectLayoutKeys(layout);
|
||||
const hiddenSet = new Set(Array.isArray(hiddenKeys) ? hiddenKeys : []);
|
||||
@@ -246,35 +164,8 @@ export function useNavLayout({
|
||||
return sanitizeLayoutLocal(base, []);
|
||||
});
|
||||
|
||||
const handleRouteChange = (routeName, routeParams = undefined) => {
|
||||
if (!routeName) {
|
||||
return;
|
||||
}
|
||||
if (routeParams) {
|
||||
router.push({ name: routeName, params: routeParams });
|
||||
return;
|
||||
}
|
||||
router.push({ name: routeName });
|
||||
};
|
||||
|
||||
const triggerNavAction = (entry) => {
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.action === 'direct-access') {
|
||||
directAccessPaste();
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.routeName) {
|
||||
handleRouteChange(entry.routeName, entry.routeParams);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.path) {
|
||||
router.push(entry.path);
|
||||
}
|
||||
triggerNavEntryAction(entry, { router, directAccessPaste });
|
||||
};
|
||||
|
||||
const saveNavLayout = async (layout, hiddenKeys = []) => {
|
||||
|
||||
51
src/components/nav-menu/navActionUtils.js
Normal file
51
src/components/nav-menu/navActionUtils.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @param {object | null | undefined} entry
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isNavEntryActionable(entry) {
|
||||
return Boolean(entry?.routeName || entry?.action || entry?.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} router
|
||||
* @param {string} routeName
|
||||
* @param {object | undefined} routeParams
|
||||
*/
|
||||
export function navigateToRoute(router, routeName, routeParams = undefined) {
|
||||
if (!routeName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (routeParams) {
|
||||
router.push({ name: routeName, params: routeParams });
|
||||
return;
|
||||
}
|
||||
|
||||
router.push({ name: routeName });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object | null | undefined} entry
|
||||
* @param {object} deps
|
||||
* @param {object} deps.router
|
||||
* @param {Function} deps.directAccessPaste
|
||||
*/
|
||||
export function triggerNavEntryAction(entry, { router, directAccessPaste }) {
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.action === 'direct-access') {
|
||||
directAccessPaste();
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.routeName) {
|
||||
navigateToRoute(router, entry.routeName, entry.routeParams);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.path) {
|
||||
router.push(entry.path);
|
||||
}
|
||||
}
|
||||
125
src/components/nav-menu/navLayoutHelpers.js
Normal file
125
src/components/nav-menu/navLayoutHelpers.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import { isNavEntryActionable } from './navActionUtils';
|
||||
|
||||
const DEFAULT_FOLDER_ICON = 'ri-folder-line';
|
||||
|
||||
/**
|
||||
* @param {Array} layout
|
||||
* @returns {Set<string>}
|
||||
*/
|
||||
export function collectLayoutKeys(layout) {
|
||||
const keys = new Set();
|
||||
if (!Array.isArray(layout)) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
layout.forEach((entry) => {
|
||||
if (entry?.type === 'item' && entry.key) {
|
||||
keys.add(entry.key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry?.type === 'folder' && Array.isArray(entry.items)) {
|
||||
entry.items.forEach((key) => {
|
||||
if (key) {
|
||||
keys.add(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} layout
|
||||
* @param {Map<string, any>} navDefinitionMap
|
||||
* @param {Function} t
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function buildMenuItems(layout, navDefinitionMap, t) {
|
||||
const items = [];
|
||||
|
||||
layout.forEach((entry) => {
|
||||
if (entry.type === 'item') {
|
||||
const definition = navDefinitionMap.get(entry.key);
|
||||
if (!definition) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.push({
|
||||
...definition,
|
||||
index: definition.key,
|
||||
title: definition.tooltip || definition.labelKey,
|
||||
titleIsCustom: Boolean(definition.isDashboard)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.type === 'folder') {
|
||||
const folderDefinitions = (entry.items || [])
|
||||
.map((key) => navDefinitionMap.get(key))
|
||||
.filter(Boolean);
|
||||
if (folderDefinitions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.push({
|
||||
index: entry.id,
|
||||
icon: entry.icon || DEFAULT_FOLDER_ICON,
|
||||
title:
|
||||
entry.name?.trim() ||
|
||||
t('nav_menu.custom_nav.folder_name_placeholder'),
|
||||
titleIsCustom: true,
|
||||
children: folderDefinitions.map((definition) => ({
|
||||
label: definition.labelKey,
|
||||
routeName: definition.routeName,
|
||||
routeParams: definition.routeParams,
|
||||
index: definition.key,
|
||||
icon: definition.icon,
|
||||
action: definition.action,
|
||||
path: definition.path,
|
||||
titleIsCustom: Boolean(definition.isDashboard)
|
||||
}))
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} layout
|
||||
* @param {Map<string, any>} navDefinitionMap
|
||||
* @returns {object | null}
|
||||
*/
|
||||
export function findFirstNavEntry(layout, navDefinitionMap) {
|
||||
for (const entry of layout) {
|
||||
if (entry.type === 'item') {
|
||||
const definition = navDefinitionMap.get(entry.key);
|
||||
if (isNavEntryActionable(definition)) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.type === 'folder' && entry.items?.length) {
|
||||
const definition = entry.items
|
||||
.map((key) => navDefinitionMap.get(key))
|
||||
.find((def) => isNavEntryActionable(def));
|
||||
if (definition) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} layout
|
||||
* @param {Map<string, any>} navDefinitionMap
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export function findFirstNavKey(layout, navDefinitionMap) {
|
||||
const entry = findFirstNavEntry(layout, navDefinitionMap);
|
||||
return entry?.key || null;
|
||||
}
|
||||
Reference in New Issue
Block a user