mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Add clickable to app card.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m11.003 6.276-5.267 5.267a.667.667 0 0 1-.943-.943l5.266-5.267H5.67A.667.667 0 1 1 5.67 4h6.667v6.667a.667.667 0 0 1-1.333 0V6.276Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 246 B |
+65
-10
@@ -1,20 +1,40 @@
|
||||
import { Box, Heading, Image, Text, useColorModeValue } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { Box, Heading, Icon, Image, Link, LinkBox, LinkOverlay, Text, useColorModeValue } from '@chakra-ui/react';
|
||||
import type { MouseEvent } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import type { AppItemPreview } from 'types/client/apps';
|
||||
|
||||
const AppCard = ({ title, logo, shortDescription, categories }: AppItemPreview) => {
|
||||
import northEastIcon from 'icons/arrows/north-east.svg';
|
||||
|
||||
interface Props extends AppItemPreview {
|
||||
onInfoClick: (id: string) => void;
|
||||
}
|
||||
|
||||
const AppCard = ({ id, title, logo, shortDescription, categories, onInfoClick }: Props) => {
|
||||
const categoriesLabel = categories.map(c => c.name).join(', ');
|
||||
|
||||
const handleInfoClick = useCallback((event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
onInfoClick(id);
|
||||
}, [ onInfoClick, id ]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
borderRadius={{ base: 'none', sm: 'md' }}
|
||||
<LinkBox
|
||||
borderRadius="md"
|
||||
height="100%"
|
||||
padding={{ base: '16px', sm: '20px' }}
|
||||
padding={{ base: 3, sm: '20px' }}
|
||||
boxShadow={ `0 0 0 1px ${ useColorModeValue('var(--chakra-colors-gray-200)', 'var(--chakra-colors-gray-600)') }` }
|
||||
>
|
||||
<Box overflow="hidden" height="100%">
|
||||
<Box
|
||||
display={{ base: 'grid', sm: 'block' }}
|
||||
gridTemplateColumns={{ base: '64px 1fr', sm: '1fr' }}
|
||||
gridTemplateRows={{ base: '20px 20px auto', sm: 'none' }}
|
||||
gridRowGap={{ base: 2, sm: 'none' }}
|
||||
gridColumnGap={{ base: 4, sm: 'none' }}
|
||||
height="100%"
|
||||
>
|
||||
<Box
|
||||
gridRow={{ base: '1 / 4', sm: 'auto' }}
|
||||
marginBottom={ 4 }
|
||||
w={{ base: '64px', sm: '96px' }}
|
||||
h={{ base: '64px', sm: '96px' }}
|
||||
@@ -26,12 +46,20 @@ const AppCard = ({ title, logo, shortDescription, categories }: AppItemPreview)
|
||||
</Box>
|
||||
|
||||
<Heading
|
||||
gridColumn={{ base: 2, sm: 'auto' }}
|
||||
as="h3"
|
||||
marginBottom={ 2 }
|
||||
fontSize={{ base: 'sm', sm: 'lg' }}
|
||||
fontWeight="semibold"
|
||||
>
|
||||
{ title }
|
||||
<LinkOverlay
|
||||
href="#"
|
||||
_hover={{
|
||||
textDecoration: 'underline',
|
||||
}}
|
||||
>
|
||||
{ title }
|
||||
</LinkOverlay>
|
||||
</Heading>
|
||||
|
||||
<Text
|
||||
@@ -49,9 +77,36 @@ const AppCard = ({ title, logo, shortDescription, categories }: AppItemPreview)
|
||||
>
|
||||
{ shortDescription }
|
||||
</Text>
|
||||
|
||||
<Box
|
||||
position="absolute"
|
||||
right={{ base: 3, sm: '20px' }}
|
||||
bottom={{ base: 3, sm: '20px' }}
|
||||
paddingTop={ 1 }
|
||||
paddingLeft={ 8 }
|
||||
bgGradient={ `linear(to-r, transparent, ${ useColorModeValue('white', 'black') } 20%)` }
|
||||
>
|
||||
<Link
|
||||
fontSize={{ base: 'xs', sm: 'sm' }}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
paddingRight={{ sm: 2 }}
|
||||
maxW="100%"
|
||||
overflow="hidden"
|
||||
href="#"
|
||||
onClick={ handleInfoClick }
|
||||
>
|
||||
More
|
||||
|
||||
<Icon
|
||||
as={ northEastIcon }
|
||||
marginLeft={ 1 }
|
||||
/>
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</LinkBox>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppCard;
|
||||
export default React.memo(AppCard);
|
||||
|
||||
+6
-5
@@ -9,9 +9,10 @@ import EmptySearchResult from 'ui/apps/EmptySearchResult';
|
||||
|
||||
type Props = {
|
||||
apps: Array<AppItemPreview>;
|
||||
onAppClick: (id: string) => void;
|
||||
}
|
||||
|
||||
const AppList = ({ apps }: Props) => {
|
||||
const AppList = ({ apps, onAppClick }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<VisuallyHidden>
|
||||
@@ -21,18 +22,18 @@ const AppList = ({ apps }: Props) => {
|
||||
{ apps.length > 0 ? (
|
||||
<Grid
|
||||
templateColumns={{
|
||||
base: 'repeat(auto-fill, minmax(160px, 1fr))',
|
||||
sm: 'repeat(auto-fill, minmax(200px, 1fr))',
|
||||
sm: 'repeat(auto-fill, minmax(170px, 1fr))',
|
||||
lg: 'repeat(auto-fill, minmax(260px, 1fr))',
|
||||
}}
|
||||
autoRows="1fr"
|
||||
gap={{ base: '1px', sm: '24px' }}
|
||||
gap={{ base: '16px', sm: '24px' }}
|
||||
>
|
||||
{ apps.map((app) => (
|
||||
<GridItem
|
||||
key={ app.id }
|
||||
>
|
||||
<AppCard
|
||||
onInfoClick={ onAppClick }
|
||||
id={ app.id }
|
||||
title={ app.title }
|
||||
logo={ app.logo }
|
||||
@@ -49,4 +50,4 @@ const AppList = ({ apps }: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AppList;
|
||||
export default React.memo(AppList);
|
||||
|
||||
+6
-2
@@ -13,7 +13,11 @@ const defaultDisplayedApps = [ ...TEMPORARY_DEMO_APPS ]
|
||||
|
||||
const Apps = () => {
|
||||
const [ displayedApps, setDisplayedApps ] = useState<Array<AppItemOverview>>(defaultDisplayedApps);
|
||||
const [ displayedAppId, setDisplayedAppId ] = useState<string | null>('component');
|
||||
const [ displayedAppId, setDisplayedAppId ] = useState<string | null>(null);
|
||||
|
||||
const showAppInfo = useCallback((id: string) => {
|
||||
setDisplayedAppId(id);
|
||||
}, []);
|
||||
|
||||
const filterApps = (q: string) => {
|
||||
const apps = displayedApps
|
||||
@@ -30,7 +34,7 @@ const Apps = () => {
|
||||
return (
|
||||
<>
|
||||
<FilterInput onChange={ debounceFilterApps } marginBottom={{ base: '4', lg: '6' }} placeholder="Find app"/>
|
||||
<AppList apps={ displayedApps }/>
|
||||
<AppList apps={ displayedApps } onAppClick={ showAppInfo }/>
|
||||
<AppModal
|
||||
id={ displayedAppId }
|
||||
onClose={ clearDisplayedAppId }
|
||||
|
||||
Reference in New Issue
Block a user