From 8eca1698cbee35b0fdb232af4c79410a072a181c Mon Sep 17 00:00:00 2001 From: renzuzu <82306584+renzuzu@users.noreply.github.com> Date: Sun, 19 Feb 2023 20:53:48 +0800 Subject: [PATCH] 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> --- package/client/resource/interface/context.ts | 2 +- package/client/resource/interface/menu.ts | 2 +- .../menu/context/components/ContextButton.tsx | 16 +++++++++++++++- web/src/features/menu/list/ListItem.tsx | 15 +++++++++++++-- web/src/typings/context.ts | 2 +- web/src/typings/menu.ts | 3 ++- web/src/utils/isIconUrl.ts | 1 + 7 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 web/src/utils/isIconUrl.ts diff --git a/package/client/resource/interface/context.ts b/package/client/resource/interface/context.ts index 64bb48e..897b312 100644 --- a/package/client/resource/interface/context.ts +++ b/package/client/resource/interface/context.ts @@ -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; diff --git a/package/client/resource/interface/menu.ts b/package/client/resource/interface/menu.ts index e0c0c35..2b22a62 100644 --- a/package/client/resource/interface/menu.ts +++ b/package/client/resource/interface/menu.ts @@ -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; description?: string; diff --git a/web/src/features/menu/context/components/ContextButton.tsx b/web/src/features/menu/context/components/ContextButton.tsx index 3dd25d6..64ddd72 100644 --- a/web/src/features/menu/context/components/ContextButton.tsx +++ b/web/src/features/menu/context/components/ContextButton.tsx @@ -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('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<{ {button?.icon && ( - + {typeof button.icon === 'string' && isIconUrl(button.icon) ? ( + Missing img + ) : ( + + )} )} diff --git a/web/src/features/menu/list/ListItem.tsx b/web/src/features/menu/list/ListItem.tsx index 373630a..1730a0b 100644 --- a/web/src/features/menu/list/ListItem.tsx +++ b/web/src/features/menu/list/ListItem.tsx @@ -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, Props>(({ item, index, {item.icon && ( - + {typeof item.icon === 'string' && isIconUrl(item.icon) ? ( + Missing image + ) : ( + + )} )} {Array.isArray(item.values) ? ( diff --git a/web/src/typings/context.ts b/web/src/typings/context.ts index aadec20..275581f 100644 --- a/web/src/typings/context.ts +++ b/web/src/typings/context.ts @@ -6,7 +6,7 @@ export interface Option { description?: string; arrow?: boolean; image?: string; - icon?: IconProp; + icon?: IconProp | string; iconColor?: string; progress?: number; colorScheme?: string; diff --git a/web/src/typings/menu.ts b/web/src/typings/menu.ts index c76f70e..802bd02 100644 --- a/web/src/typings/menu.ts +++ b/web/src/typings/menu.ts @@ -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; description?: string; - icon?: IconProp; + icon?: IconProp | string; iconColor?: string; defaultIndex?: number; close?: boolean; diff --git a/web/src/utils/isIconUrl.ts b/web/src/utils/isIconUrl.ts new file mode 100644 index 0000000..cc8c3d0 --- /dev/null +++ b/web/src/utils/isIconUrl.ts @@ -0,0 +1 @@ +export const isIconUrl = (icon: string) => icon.includes('://') || icon.includes('.png') || icon.includes('.webp');