mirror of https://github.com/vizality/vizality
improve and modularize SettingsContextMenu component
- Moved the ContextMenu out of the settings builtin and renamed it to `SettingsContextMenu`. It can now be accessed with `@vizality/components/vizality` - Changed the format of the addon items - Added quick toggle feature to addon submenus, which can be activated by clicking the button or holding shiftpull/67/head
parent
7d09c1a03c
commit
47660b84c4
@ -1,148 +0,0 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import { ContextMenu, LazyImage } from '@vizality/components';
|
||||
import { toPlural } from '@vizality/util/string';
|
||||
import { useForceUpdate } from '@vizality/hooks';
|
||||
import { Messages } from '@vizality/i18n';
|
||||
|
||||
export default memo(() => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const plugins =
|
||||
vizality.manager.plugins.keys
|
||||
.sort((a, b) => a - b)
|
||||
.map(plugin => vizality.manager.plugins.get(plugin));
|
||||
|
||||
const themes =
|
||||
vizality.manager.themes.keys
|
||||
.sort((a, b) => a - b)
|
||||
.map(theme => vizality.manager.themes.get(theme));
|
||||
|
||||
const renderContextItem = (item, type) => {
|
||||
return (
|
||||
<ContextMenu.CheckboxItem
|
||||
vz-addon-icon={item.manifest.icon}
|
||||
id={item.addonId}
|
||||
label={item.manifest.name}
|
||||
checked={vizality.manager[toPlural(type)].isEnabled(item.addonId)}
|
||||
action={async () => {
|
||||
vizality.manager[toPlural(type)].isEnabled(item.addonId)
|
||||
? await vizality.manager[toPlural(type)].disable(item.addonId)
|
||||
: await vizality.manager[toPlural(type)].enable(item.addonId);
|
||||
forceUpdate();
|
||||
}}
|
||||
>
|
||||
<LazyImage src={item.manifest.icon} />
|
||||
</ContextMenu.CheckboxItem>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContextMenu.Separator />
|
||||
<ContextMenu.Item
|
||||
id='vizality'
|
||||
label='Vizality'
|
||||
action={() => vizality.api.routes.navigateTo('dashboard')}
|
||||
>
|
||||
<ContextMenu.Item
|
||||
id='settings'
|
||||
label='Settings'
|
||||
action={() => vizality.api.routes.navigateTo('settings')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='plugins'
|
||||
label='Plugins'
|
||||
action={() => vizality.api.routes.navigateTo('plugins')}
|
||||
>
|
||||
{plugins.length && plugins.map(plugin => renderContextItem(plugin, 'plugins'))}
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
id='themes'
|
||||
label='Themes'
|
||||
action={() => vizality.api.routes.navigateTo('themes')}
|
||||
>
|
||||
{themes.length && themes.map(theme => renderContextItem(theme, 'themes'))}
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
id='snippets'
|
||||
label='Snippets'
|
||||
disabled={true}
|
||||
action={() => vizality.api.routes.navigateTo('snippets')}
|
||||
>
|
||||
</ContextMenu.Item>
|
||||
{vizality.manager.builtins.isEnabled('quick-code') && (
|
||||
<ContextMenu.Item
|
||||
id='quick-code'
|
||||
label='Quick Code'
|
||||
action={() => vizality.api.routes.navigateTo('quick-code')}
|
||||
/>
|
||||
)}
|
||||
<ContextMenu.Separator/>
|
||||
<ContextMenu.Item
|
||||
id='development'
|
||||
label='Development'
|
||||
action={() => vizality.api.routes.navigateTo('development')}
|
||||
>
|
||||
<ContextMenu.Item
|
||||
id='documentation'
|
||||
label='Documentation'
|
||||
action={() => vizality.api.routes.navigateTo('docs')}
|
||||
>
|
||||
<ContextMenu.Item
|
||||
id='getting-started'
|
||||
label='Getting Started'
|
||||
action={() => vizality.api.routes.navigateTo('docs/getting-started')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='plugins'
|
||||
label='Plugins'
|
||||
action={() => vizality.api.routes.navigateTo('docs/plugins')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='themes'
|
||||
label='Themes'
|
||||
action={() => vizality.api.routes.navigateTo('docs/themes')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='screenshots'
|
||||
label='Screenshots'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/screenshots')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='icons'
|
||||
label='Components'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/icons')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='markdown'
|
||||
label='Markdown'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/markdown')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='error-test'
|
||||
label='Error Test'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/error-test')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='test'
|
||||
label='Test'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/test')}
|
||||
/>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Separator/>
|
||||
<ContextMenu.Item
|
||||
id='updater'
|
||||
label='Updater'
|
||||
action={() => vizality.api.routes.navigateTo('updater')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='changelog'
|
||||
label='Changelog'
|
||||
action={() => vizality.api.routes.navigateTo('changelog')}
|
||||
/>
|
||||
</ContextMenu.Item>
|
||||
</>
|
||||
);
|
||||
});
|
@ -0,0 +1,333 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import { toPlural } from '@vizality/util/string';
|
||||
import { useForceUpdate } from '@vizality/hooks';
|
||||
import { contextMenu } from '@vizality/webpack';
|
||||
import { error } from '@vizality/util/logger';
|
||||
import { Messages } from '@vizality/i18n';
|
||||
|
||||
import { ContextMenu, LazyImage, Button, Tooltip } from '..';
|
||||
import { AddonContextMenu } from '../addon';
|
||||
|
||||
const { closeContextMenu } = contextMenu;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
const _labels = [ 'Component', 'Vizality', 'SettingsContextMenu' ];
|
||||
const _error = (...message) => error({ labels: _labels, message });
|
||||
|
||||
/**
|
||||
* Vizality dashboard settings context menu that is like a mini-dashboard. You're able
|
||||
* to quickly access the Vizality dashboard and many of the pages that are
|
||||
* accessible on the Vizality dashboard.
|
||||
*
|
||||
* By default, this component is implemented and can be accessed in various places around
|
||||
* the app, such as the right click the gear on the user account panel which opens the
|
||||
* Discord settings context menu. It can also be accessed by right clicking on the
|
||||
* private channels Dashboard item.
|
||||
* @component
|
||||
* @returns {React.MemoExoticComponent<function(): React.ReactElement>}
|
||||
*/
|
||||
export default memo(() => {
|
||||
const [ quickToggle, setQuickToggle ] = useState(false);
|
||||
const [ quickToggleKeybind, setQuickToggleKeybind ] = useState(false);
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
/**
|
||||
* Add some key event listeners on mount to allow users to hold shift to enter
|
||||
* quick toggle mode on addon context items.
|
||||
*/
|
||||
useEffect(() => {
|
||||
/**
|
||||
* Handles keydown events.
|
||||
* @param {document#event:keydown} evt Keydown event
|
||||
*/
|
||||
const keyDownHandler = evt => {
|
||||
/**
|
||||
* Check for the shift key.
|
||||
*/
|
||||
if (evt.keyCode === 16) {
|
||||
setQuickToggle(true);
|
||||
setQuickToggleKeybind(true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles keyup events.
|
||||
* @param {document#event:keyup} evt Keyup event
|
||||
*/
|
||||
const keyUpHandler = evt => {
|
||||
/**
|
||||
* Check for the shift key.
|
||||
*/
|
||||
if (evt.keyCode === 16) {
|
||||
setQuickToggle(false);
|
||||
setQuickToggleKeybind(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', keyDownHandler);
|
||||
document.addEventListener('keyup', keyUpHandler);
|
||||
|
||||
/**
|
||||
* Remove the event listeners on dismount.
|
||||
*/
|
||||
return () => {
|
||||
document.removeEventListener('keydown', keyDownHandler);
|
||||
document.removeEventListener('keyup', keyUpHandler);
|
||||
};
|
||||
}, [ quickToggle ]);
|
||||
|
||||
/**
|
||||
* Creates a sorted array containing all installed plugins.
|
||||
*/
|
||||
const plugins =
|
||||
vizality.manager.plugins.keys
|
||||
.sort((a, b) => a - b)
|
||||
.map(plugin => vizality.manager.plugins.get(plugin));
|
||||
|
||||
/**
|
||||
* Creates a sorted array containing all installed themes.
|
||||
*/
|
||||
const themes =
|
||||
vizality.manager.themes.keys
|
||||
.sort((a, b) => a - b)
|
||||
.map(theme => vizality.manager.themes.get(theme));
|
||||
|
||||
/**
|
||||
* Renders a context menu item for an addon.
|
||||
* @param {Addon#addonId} addonId Addon ID
|
||||
* @param {AddonManifest#name} name Addon name
|
||||
* @param {AddonManifest#icon} icon Addon icon
|
||||
* @param {AddonType} type Addon type
|
||||
*/
|
||||
const renderContextItem = (addonId, name, icon, type) => {
|
||||
const _AddonContextMenu = AddonContextMenu.type({ addonId, type }).props.children;
|
||||
return (
|
||||
quickToggle
|
||||
? (
|
||||
<ContextMenu.CheckboxItem
|
||||
vz-addon-icon={icon}
|
||||
id={addonId}
|
||||
label={name}
|
||||
checked={vizality.manager[toPlural(type)].isEnabled(addonId)}
|
||||
action={async () => {
|
||||
try {
|
||||
vizality.manager[toPlural(type)].isEnabled(addonId)
|
||||
? await vizality.manager[toPlural(type)].disable(addonId)
|
||||
: await vizality.manager[toPlural(type)].enable(addonId);
|
||||
forceUpdate();
|
||||
} catch (err) {
|
||||
_error(err);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LazyImage src={icon} />
|
||||
</ContextMenu.CheckboxItem>
|
||||
)
|
||||
: (
|
||||
<ContextMenu.Item
|
||||
vz-addon-icon={icon}
|
||||
id={addonId}
|
||||
label={name}
|
||||
action={() => {
|
||||
try {
|
||||
if (vizality.manager[toPlural(type)].isCommunity(addonId)) {
|
||||
return vizality.api.routes.navigateTo(`${toPlural(type)}/${addonId}`);
|
||||
}
|
||||
return vizality.api.routes.navigateTo(`${toPlural(type)}/local/${addonId}`);
|
||||
} catch (err) {
|
||||
_error(err);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{_AddonContextMenu}
|
||||
</ContextMenu.Item>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders an array of addon context items.
|
||||
* @param {Array<Addon>} addons Addons
|
||||
* @param {AddonType} type Addon type
|
||||
* @returns {Array<React.Component>}
|
||||
*/
|
||||
const renderAddonItems = (addons, type) => {
|
||||
return addons.map(addon => renderContextItem(addon.addonId, addon.manifest.name, addon.manifest.icon, type));
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a group of addon context items.
|
||||
* @param {Array<Addon>} addons Addons
|
||||
* @param {AddonType} type Addon type
|
||||
* @returns {React.Component}
|
||||
*/
|
||||
const renderAddonGroup = (addons, type) => (
|
||||
<ContextMenu.Group
|
||||
id='quick-toggle'
|
||||
className='vz-settings-context-menu-quick-toggle'
|
||||
label={
|
||||
<Tooltip text='Or you can just hold shift!'>
|
||||
<Button
|
||||
disabled={quickToggleKeybind}
|
||||
onClick={() => setQuickToggle(!quickToggle)}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={quickToggle && !quickToggleKeybind ? Button.Colors.RED : Button.Colors.BRAND}
|
||||
>
|
||||
{quickToggle && !quickToggleKeybind ? 'Disable' : 'Enable'} Quick Toggle Mode
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
{renderAddonItems(addons, type)}
|
||||
</ContextMenu.Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<ContextMenu.Menu navId='vizality-dashboard' onClose={closeContextMenu}>
|
||||
<ContextMenu.Item
|
||||
id='settings'
|
||||
label='Settings'
|
||||
action={() => vizality.api.routes.navigateTo('settings')}
|
||||
>
|
||||
<ContextMenu.Item
|
||||
id='general'
|
||||
label='General'
|
||||
action={() => vizality.api.routes.navigateTo('settings/general')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='account'
|
||||
label='Account'
|
||||
action={() => vizality.api.routes.navigateTo('settings/account')}
|
||||
disabled
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='appearance'
|
||||
label='Appearance'
|
||||
action={() => vizality.api.routes.navigateTo('settings/appearance')}
|
||||
disabled
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='notifications'
|
||||
label='Notifications'
|
||||
action={() => vizality.api.routes.navigateTo('settings/notifications')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='keybinds'
|
||||
label='Keybinds'
|
||||
action={() => vizality.api.routes.navigateTo('settings/keybinds')}
|
||||
disabled
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='commands'
|
||||
label='Commands'
|
||||
action={() => vizality.api.routes.navigateTo('settings/commands')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='developer'
|
||||
label='Developer'
|
||||
action={() => vizality.api.routes.navigateTo('settings/developer')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='advanced'
|
||||
label='Advanced'
|
||||
action={() => vizality.api.routes.navigateTo('settings/advanced')}
|
||||
/>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
id='plugins'
|
||||
label='Plugins'
|
||||
action={() => vizality.api.routes.navigateTo('plugins')}
|
||||
>
|
||||
{plugins.length && renderAddonGroup(plugins, 'plugin')}
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
id='themes'
|
||||
label='Themes'
|
||||
action={() => vizality.api.routes.navigateTo('themes')}
|
||||
>
|
||||
{themes.length && renderAddonGroup(themes, 'theme')}
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
id='snippets'
|
||||
label='Snippets'
|
||||
action={() => vizality.api.routes.navigateTo('snippets')}
|
||||
disabled
|
||||
>
|
||||
</ContextMenu.Item>
|
||||
{vizality.manager.builtins.isEnabled('quick-code') && (
|
||||
<ContextMenu.Item
|
||||
id='quick-code'
|
||||
label='Quick Code'
|
||||
action={() => vizality.api.routes.navigateTo('quick-code')}
|
||||
/>
|
||||
)}
|
||||
<ContextMenu.Separator/>
|
||||
<ContextMenu.Item
|
||||
id='development'
|
||||
label='Development'
|
||||
action={() => vizality.api.routes.navigateTo('development')}
|
||||
>
|
||||
<ContextMenu.Item
|
||||
id='documentation'
|
||||
label='Documentation'
|
||||
action={() => vizality.api.routes.navigateTo('docs')}
|
||||
>
|
||||
<ContextMenu.Item
|
||||
id='getting-started'
|
||||
label='Getting Started'
|
||||
action={() => vizality.api.routes.navigateTo('docs/getting-started')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='plugins'
|
||||
label='Plugins'
|
||||
action={() => vizality.api.routes.navigateTo('docs/plugins')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='themes'
|
||||
label='Themes'
|
||||
action={() => vizality.api.routes.navigateTo('docs/themes')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='screenshots'
|
||||
label='Screenshots'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/screenshots')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='icons'
|
||||
label='Components'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/icons')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='markdown'
|
||||
label='Markdown'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/markdown')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='error-test'
|
||||
label='Error Test'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/error-test')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='test'
|
||||
label='Test'
|
||||
action={() => vizality.api.routes.navigateTo('docs/components/test')}
|
||||
/>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Separator/>
|
||||
<ContextMenu.Item
|
||||
id='updater'
|
||||
label='Updater'
|
||||
action={() => vizality.api.routes.navigateTo('updater')}
|
||||
/>
|
||||
<ContextMenu.Item
|
||||
id='changelog'
|
||||
label='Changelog'
|
||||
action={() => vizality.api.routes.navigateTo('changelog')}
|
||||
/>
|
||||
</ContextMenu.Menu>
|
||||
);
|
||||
});
|
@ -0,0 +1 @@
|
||||
export { default as SettingsContextMenu } from './SettingsContextMenu';
|
Loading…
Reference in new issue