feat(web/meu): support images as icons in context and list menu (#230)

* feat: Support image Tag in buttons

in my server. ox_lib is heavily used.
most of sample use cases for this is.
Food image,
Ingredients,
Vehicle Parts,
etc..

i believe font awesome is not enough for some use cases.

i did suggest this on v3 pending PR.
but it seems gets ignored.

here i am gonna try to push this PR.

to use this.
icon must be not existing in the options.

if both icon and image is in options, nothing will appear.

image in metadata will still appear normaly.

* tweak(web/menu): resize render image comparable to FA

* refactor(web/context): use icon prop for images

* refactor(web/list): use icon prop for images

---------

Co-authored-by: Luke <39926192+LukeWasTakenn@users.noreply.github.com>
This commit is contained in:
renzuzu
2023-02-19 20:53:48 +08:00
committed by GitHub
parent 672b638ba7
commit 8eca1698cb
7 changed files with 34 additions and 7 deletions

View File

@@ -6,7 +6,7 @@ interface ContextMenuItem {
description?: string;
arrow?: boolean;
image?: string;
icon?: IconName | [IconPrefix, IconName];
icon?: IconName | [IconPrefix, IconName] | string;
iconColor?: string;
progress?: number;
colorScheme?: string;

View File

@@ -5,7 +5,7 @@ type ChangeFunction = (selected: number, scrollIndex?: number, args?: any, check
interface MenuOptions {
label: string;
icon?: IconName | [IconPrefix, IconName];
icon?: IconName | [IconPrefix, IconName] | string;
checked?: boolean;
values?: Array<string | { label: string; description: string }>;
description?: string;

View File

@@ -3,6 +3,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import ReactMarkdown from 'react-markdown';
import { Option, ContextMenuProps } from '../../../../typings';
import { fetchNui } from '../../../../utils/fetchNui';
import { isIconUrl } from '../../../../utils/isIconUrl';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
const openMenu = (id: string | undefined) => {
fetchNui<ContextMenuProps>('openContext', { id: id, back: false });
@@ -21,6 +23,9 @@ const useStyles = createStyles((theme, params: { disabled?: boolean }) => ({
color: params.disabled ? theme.colors.dark[3] : theme.colors.dark[0],
whiteSpace: 'pre-wrap',
},
iconImage: {
maxWidth: '25px',
},
description: {
color: params.disabled ? theme.colors.dark[3] : theme.colors.dark[2],
},
@@ -63,7 +68,16 @@ const ContextButton: React.FC<{
<Group spacing={8} noWrap>
{button?.icon && (
<Stack w={25} h={25} justify="center" align="center">
<FontAwesomeIcon icon={button.icon} fixedWidth size="lg" style={{ color: button.iconColor }} />
{typeof button.icon === 'string' && isIconUrl(button.icon) ? (
<img src={button.icon} className={classes.iconImage} alt="Missing img" />
) : (
<FontAwesomeIcon
icon={button.icon as IconProp}
fixedWidth
size="lg"
style={{ color: button.iconColor }}
/>
)}
</Stack>
)}
<Text sx={{ overflowWrap: 'break-word' }}>

View File

@@ -1,9 +1,11 @@
import { Box, Group, Stack, Text, Progress } from '@mantine/core';
import { Box, Group, Stack, Text, Progress, Image } from '@mantine/core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React, { forwardRef } from 'react';
import CustomCheckbox from './CustomCheckbox';
import type { MenuItem } from '../../../typings';
import { createStyles } from '@mantine/core';
import { isIconUrl } from '../../../utils/isIconUrl';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
interface Props {
item: MenuItem;
@@ -24,6 +26,9 @@ const useStyles = createStyles((theme, params: { iconColor?: string }) => ({
outline: 'none',
},
},
iconImage: {
maxWidth: 32,
},
buttonWrapper: {
paddingLeft: 5,
paddingRight: 12,
@@ -32,6 +37,8 @@ const useStyles = createStyles((theme, params: { iconColor?: string }) => ({
iconContainer: {
display: 'flex',
alignItems: 'center',
width: 32,
height: 32,
},
icon: {
fontSize: 24,
@@ -79,7 +86,11 @@ const ListItem = forwardRef<Array<HTMLDivElement | null>, Props>(({ item, index,
<Group spacing={15} noWrap className={classes.buttonWrapper}>
{item.icon && (
<Box className={classes.iconContainer}>
<FontAwesomeIcon icon={item.icon} className={classes.icon} fixedWidth />
{typeof item.icon === 'string' && isIconUrl(item.icon) ? (
<img src={item.icon} alt="Missing image" className={classes.iconImage} />
) : (
<FontAwesomeIcon icon={item.icon as IconProp} className={classes.icon} fixedWidth />
)}
</Box>
)}
{Array.isArray(item.values) ? (

View File

@@ -6,7 +6,7 @@ export interface Option {
description?: string;
arrow?: boolean;
image?: string;
icon?: IconProp;
icon?: IconProp | string;
iconColor?: string;
progress?: number;
colorScheme?: string;

View File

@@ -3,13 +3,14 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
export type MenuPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
export interface MenuItem {
image?: string;
label: string;
progress?: number;
colorScheme?: string;
checked?: boolean;
values?: Array<string | { label: string; description: string }>;
description?: string;
icon?: IconProp;
icon?: IconProp | string;
iconColor?: string;
defaultIndex?: number;
close?: boolean;

View File

@@ -0,0 +1 @@
export const isIconUrl = (icon: string) => icon.includes('://') || icon.includes('.png') || icon.includes('.webp');