mirror of
https://github.com/labring/sealos.git
synced 2026-09-24 15:46:19 +08:00
feat(desktop): desktop design catchup (#5703)
* feat(desktop): match app grid and folder design with design Signed-off-by: Nixieboluo <me@sagirii.me> * feat(desktop): make folder draw area larger Signed-off-by: Nixieboluo <me@sagirii.me> * refactor(desktop): calculate app height using the height of 2 lines app name Signed-off-by: Nixieboluo <me@sagirii.me> * feat(desktop): add menu for secondary links on narrow screens Signed-off-by: Nixieboluo <me@sagirii.me> * chore(desktop): clean up Signed-off-by: Nixieboluo <me@sagirii.me> * feat(desktop): tweak pin input corner radius to match the design Signed-off-by: Nixieboluo <me@sagirii.me> * fix(desktop): incorrect desktop folder paging calculation Signed-off-by: Nixieboluo <me@sagirii.me> * fix(desktop): navbar overflows on very narrow screens Signed-off-by: Nixieboluo <me@sagirii.me> --------- Signed-off-by: Nixieboluo <me@sagirii.me>
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
import { useConfigStore } from '@/stores/config';
|
||||
import useAppStore from '@/stores/app';
|
||||
import { useGuideModalStore } from '@/stores/guideModal';
|
||||
import {
|
||||
Box,
|
||||
Center,
|
||||
Divider,
|
||||
Flex,
|
||||
Text,
|
||||
useBreakpointValue,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem
|
||||
} from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { getAmount } from '@/api/auth';
|
||||
import Decimal from 'decimal.js';
|
||||
import { CurrencySymbol } from '@sealos/ui';
|
||||
import { formatMoney } from '@/utils/format';
|
||||
import useSessionStore from '@/stores/session';
|
||||
import { MoreHorizontal } from 'lucide-react';
|
||||
|
||||
const baseItemStyle = {
|
||||
minW: '36px',
|
||||
h: '40px',
|
||||
fontSize: '14px',
|
||||
fontWeight: '500',
|
||||
color: 'primary',
|
||||
_hover: {
|
||||
background: 'secondary'
|
||||
}
|
||||
};
|
||||
|
||||
export default function SecondaryLinks() {
|
||||
const { layoutConfig } = useConfigStore();
|
||||
const { t } = useTranslation();
|
||||
const { openGuideModal, setInitGuide } = useGuideModalStore();
|
||||
const { openDesktopApp } = useAppStore();
|
||||
const { session } = useSessionStore();
|
||||
const user = session?.user;
|
||||
|
||||
const isCollapsed = useBreakpointValue({
|
||||
base: true,
|
||||
lg: false
|
||||
});
|
||||
|
||||
const openWorkOrderApp = () => {
|
||||
openDesktopApp({
|
||||
appKey: 'system-workorder',
|
||||
pathname: '/'
|
||||
});
|
||||
};
|
||||
|
||||
const openCostCenterApp = () => {
|
||||
openDesktopApp({
|
||||
appKey: 'system-costcenter',
|
||||
pathname: '/'
|
||||
});
|
||||
};
|
||||
|
||||
const { data } = useQuery({
|
||||
queryKey: ['getAmount', { userId: user?.userCrUid }],
|
||||
queryFn: getAmount,
|
||||
enabled: !!user,
|
||||
staleTime: 60 * 1000
|
||||
});
|
||||
|
||||
const balance = useMemo(() => {
|
||||
let realBalance = new Decimal(data?.data?.balance || 0);
|
||||
if (data?.data?.deductionBalance) {
|
||||
realBalance = realBalance.minus(new Decimal(data.data.deductionBalance));
|
||||
}
|
||||
return realBalance.toNumber();
|
||||
}, [data]);
|
||||
|
||||
const handleGuideClick = () => {
|
||||
openGuideModal();
|
||||
setInitGuide(false);
|
||||
};
|
||||
|
||||
const handleDocsClick = () => {
|
||||
if (layoutConfig?.common.docsUrl) {
|
||||
window.open(layoutConfig.common.docsUrl);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isCollapsed) {
|
||||
return (
|
||||
<Flex gap={'4px'} ml={'auto'}>
|
||||
{layoutConfig?.version === 'cn' && (
|
||||
<Center
|
||||
mr={'12px'}
|
||||
borderRadius={'8px'}
|
||||
bg={
|
||||
'linear-gradient(90deg, rgba(129, 203, 252, 0.12) 0%, rgba(81, 159, 245, 0.12) 100%)'
|
||||
}
|
||||
h={'36px'}
|
||||
px={'12px'}
|
||||
py={'8px'}
|
||||
color="#2563EB"
|
||||
fontSize={'14px'}
|
||||
fontWeight={'500'}
|
||||
cursor={'pointer'}
|
||||
onClick={openCostCenterApp}
|
||||
>
|
||||
<Text>{t('common:balance')}</Text>
|
||||
<Divider orientation="vertical" mx={'12px'} />
|
||||
<CurrencySymbol />
|
||||
<Text ml={'4px'}>{formatMoney(balance).toFixed(2)}</Text>
|
||||
</Center>
|
||||
)}
|
||||
|
||||
<Center
|
||||
className="guide-button"
|
||||
cursor={'pointer'}
|
||||
{...baseItemStyle}
|
||||
px={'8px'}
|
||||
borderRadius={'8px'}
|
||||
onClick={handleGuideClick}
|
||||
>
|
||||
{t('common:guide')}
|
||||
</Center>
|
||||
|
||||
{layoutConfig?.common.docsUrl && (
|
||||
<Center
|
||||
{...baseItemStyle}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'8px'}
|
||||
px={'8px'}
|
||||
onClick={handleDocsClick}
|
||||
>
|
||||
{t('common:doc')}
|
||||
</Center>
|
||||
)}
|
||||
|
||||
{layoutConfig?.version === 'cn' && (
|
||||
<Center
|
||||
cursor={'pointer'}
|
||||
{...baseItemStyle}
|
||||
px={'8px'}
|
||||
borderRadius={'8px'}
|
||||
border={'1px solid transparent'}
|
||||
onClick={openWorkOrderApp}
|
||||
>
|
||||
{t('v2:support')}
|
||||
</Center>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={Center}
|
||||
{...baseItemStyle}
|
||||
tabIndex={0}
|
||||
boxSize={'36px'}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'full'}
|
||||
border={'1px solid rgba(0, 0, 0, 0.05)'}
|
||||
px={'8px'}
|
||||
>
|
||||
<MoreHorizontal size={16} />
|
||||
</MenuButton>
|
||||
<MenuList
|
||||
p="8px"
|
||||
borderRadius="12px"
|
||||
boxShadow="0 4px 20px rgba(0, 0, 0, 0.15)"
|
||||
border="1px solid rgba(0, 0, 0, 0.05)"
|
||||
minW="200px"
|
||||
>
|
||||
{layoutConfig?.version === 'cn' && (
|
||||
<>
|
||||
<MenuItem
|
||||
borderRadius="8px"
|
||||
_hover={{ bg: 'rgba(129, 203, 252, 0.12)' }}
|
||||
onClick={openCostCenterApp}
|
||||
bg={
|
||||
'linear-gradient(90deg, rgba(129, 203, 252, 0.12) 0%, rgba(81, 159, 245, 0.12) 100%)'
|
||||
}
|
||||
color="#2563EB"
|
||||
fontSize="14px"
|
||||
fontWeight="500"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Flex height={'16px'} alignItems="center" my="8px" px="12px">
|
||||
<Text>{t('common:balance')}</Text>
|
||||
<Divider orientation="vertical" mx="8px" />
|
||||
<CurrencySymbol />
|
||||
<Text ml="4px">{formatMoney(balance).toFixed(2)}</Text>
|
||||
</Flex>
|
||||
</MenuItem>
|
||||
|
||||
<Divider my="8px" mx="-8px" w="calc(100% + 16px)" bg="rgba(0, 0, 0, 0.1)" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<MenuItem
|
||||
py="8px"
|
||||
px="12px"
|
||||
borderRadius="8px"
|
||||
_hover={{ bg: 'rgba(0, 0, 0, 0.05)' }}
|
||||
onClick={handleGuideClick}
|
||||
fontSize="14px"
|
||||
fontWeight="500"
|
||||
>
|
||||
{t('common:guide')}
|
||||
</MenuItem>
|
||||
|
||||
{layoutConfig?.common.docsUrl && (
|
||||
<MenuItem
|
||||
py="8px"
|
||||
px="12px"
|
||||
borderRadius="8px"
|
||||
_hover={{ bg: 'rgba(0, 0, 0, 0.05)' }}
|
||||
onClick={handleDocsClick}
|
||||
fontSize="14px"
|
||||
fontWeight="500"
|
||||
>
|
||||
{t('common:doc')}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{layoutConfig?.version === 'cn' && (
|
||||
<MenuItem
|
||||
py="8px"
|
||||
px="12px"
|
||||
borderRadius="8px"
|
||||
_hover={{ bg: 'rgba(0, 0, 0, 0.05)' }}
|
||||
onClick={openWorkOrderApp}
|
||||
fontSize="14px"
|
||||
fontWeight="500"
|
||||
>
|
||||
{t('v2:support')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@@ -18,10 +18,10 @@ import {
|
||||
useColorMode,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import RegionToggle from '../region/RegionToggle';
|
||||
import WorkspaceToggle from '../team/WorkspaceToggle';
|
||||
import useAppStore from '@/stores/app';
|
||||
@@ -38,11 +38,9 @@ import {
|
||||
} from 'lucide-react';
|
||||
import AccountCenter from './AccountCenter';
|
||||
import { useLanguageSwitcher } from '@/hooks/useLanguageSwitcher';
|
||||
import { getAmount } from '@/api/auth';
|
||||
import Decimal from 'decimal.js';
|
||||
import { CopyIcon, CurrencySymbol } from '@sealos/ui';
|
||||
import { CopyIcon } from '@sealos/ui';
|
||||
import { useGuideModalStore } from '@/stores/guideModal';
|
||||
import { formatMoney } from '@/utils/format';
|
||||
import SecondaryLinks from '../SecondaryLinks';
|
||||
|
||||
const baseItemStyle = {
|
||||
minW: '36px',
|
||||
@@ -72,10 +70,6 @@ export default function Account() {
|
||||
const { toggleLanguage, currentLanguage } = useLanguageSwitcher();
|
||||
const onAmount = useCallback((amount: number) => setNotificationAmount(amount), []);
|
||||
const [showNsId, setShowNsId] = useState(false);
|
||||
const linksCollapsed = useBreakpointValue({
|
||||
base: true,
|
||||
md: false
|
||||
});
|
||||
|
||||
const logout = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
@@ -85,19 +79,6 @@ export default function Account() {
|
||||
setToken('');
|
||||
};
|
||||
|
||||
const openWorkOrderApp = () => {
|
||||
const workorder = installedApps.find((t) => t.key === 'system-workorder');
|
||||
if (!workorder) return;
|
||||
openApp(workorder);
|
||||
};
|
||||
|
||||
const openCostCenterApp = () => {
|
||||
openDesktopApp({
|
||||
appKey: 'system-costcenter',
|
||||
pathname: '/'
|
||||
});
|
||||
};
|
||||
|
||||
const openReferralApp = () => {
|
||||
openDesktopApp({
|
||||
appKey: 'system-invite',
|
||||
@@ -105,21 +86,6 @@ export default function Account() {
|
||||
});
|
||||
};
|
||||
|
||||
const { data } = useQuery({
|
||||
queryKey: ['getAmount', { userId: user?.userCrUid }],
|
||||
queryFn: getAmount,
|
||||
enabled: !!user,
|
||||
staleTime: 60 * 1000
|
||||
});
|
||||
|
||||
const balance = useMemo(() => {
|
||||
let realBalance = new Decimal(data?.data?.balance || 0);
|
||||
if (data?.data?.deductionBalance) {
|
||||
realBalance = realBalance.minus(new Decimal(data.data.deductionBalance));
|
||||
}
|
||||
return realBalance.toNumber();
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (initGuide) {
|
||||
openGuideModal();
|
||||
@@ -127,9 +93,9 @@ export default function Account() {
|
||||
}, [initGuide, openGuideModal]);
|
||||
|
||||
return (
|
||||
<Box position={'relative'} flex={1}>
|
||||
<Flex alignItems={'center'} height={'100%'} zIndex={3}>
|
||||
<Flex alignItems={'center'} flexGrow={1}>
|
||||
<Box position={'relative'} flex={1} w={'full'}>
|
||||
<Flex alignItems={'center'} height={'100%'} zIndex={3} w={'full'}>
|
||||
<Flex alignItems={'center'} flex={'1 1 auto'} minW={0}>
|
||||
<Center
|
||||
display={{
|
||||
base: 'none',
|
||||
@@ -168,72 +134,6 @@ export default function Account() {
|
||||
<WorkspaceToggle />
|
||||
</Flex>
|
||||
|
||||
{!linksCollapsed && (
|
||||
<Flex gap={'4px'} ml={'auto'}>
|
||||
{layoutConfig?.version === 'cn' && (
|
||||
<Center
|
||||
mr={'12px'}
|
||||
borderRadius={'8px'}
|
||||
bg={
|
||||
'linear-gradient(90deg, rgba(129, 203, 252, 0.12) 0%, rgba(81, 159, 245, 0.12) 100%)'
|
||||
}
|
||||
h={'36px'}
|
||||
px={'12px'}
|
||||
py={'8px'}
|
||||
color="#2563EB"
|
||||
fontSize={'14px'}
|
||||
fontWeight={'500'}
|
||||
cursor={'pointer'}
|
||||
onClick={openCostCenterApp}
|
||||
>
|
||||
<Text>{t('common:balance')}</Text>
|
||||
<Divider orientation="vertical" mx={'12px'} />
|
||||
<CurrencySymbol />
|
||||
<Text ml={'4px'}>{formatMoney(balance).toFixed(2)}</Text>
|
||||
</Center>
|
||||
)}
|
||||
|
||||
<Center
|
||||
className="guide-button"
|
||||
cursor={'pointer'}
|
||||
{...baseItemStyle}
|
||||
px={'8px'}
|
||||
borderRadius={'8px'}
|
||||
onClick={() => {
|
||||
openGuideModal();
|
||||
setInitGuide(false);
|
||||
}}
|
||||
>
|
||||
{t('common:guide')}
|
||||
</Center>
|
||||
|
||||
{layoutConfig?.common.docsUrl && (
|
||||
<Center
|
||||
{...baseItemStyle}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'8px'}
|
||||
px={'8px'}
|
||||
onClick={() => window.open(layoutConfig?.common?.docsUrl)}
|
||||
>
|
||||
{t('common:doc')}
|
||||
</Center>
|
||||
)}
|
||||
|
||||
{layoutConfig?.version === 'cn' && (
|
||||
<Center
|
||||
cursor={'pointer'}
|
||||
{...baseItemStyle}
|
||||
px={'8px'}
|
||||
borderRadius={'8px'}
|
||||
border={'1px solid transparent'}
|
||||
onClick={openWorkOrderApp}
|
||||
>
|
||||
{t('v2:support')}
|
||||
</Center>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
<Flex
|
||||
ml={'20px'}
|
||||
gap={'12px'}
|
||||
@@ -258,6 +158,8 @@ export default function Account() {
|
||||
|
||||
{/* <ThemeToggle /> */}
|
||||
|
||||
<SecondaryLinks />
|
||||
|
||||
<Notification key={'notification'} disclosure={showDisclosure} onAmount={onAmount}>
|
||||
<Center
|
||||
{...baseItemStyle}
|
||||
@@ -375,23 +277,7 @@ export default function Account() {
|
||||
</Text>
|
||||
</Flex>
|
||||
</MenuItem> */}
|
||||
<MenuItem
|
||||
_focus={{ bg: 'transparent' }}
|
||||
py="6px"
|
||||
px="8px"
|
||||
borderRadius="8px"
|
||||
_hover={{ bg: '#F4F4F5' }}
|
||||
onClick={openCostCenterApp}
|
||||
>
|
||||
<Flex alignItems="center" gap="8px">
|
||||
<Center w="20px" h="20px">
|
||||
<CreditCard size={16} color="#737373" />
|
||||
</Center>
|
||||
<Text fontSize="14px" fontWeight="400">
|
||||
{t('common:billing')}
|
||||
</Text>
|
||||
</Flex>
|
||||
</MenuItem>
|
||||
|
||||
{layoutConfig?.version === 'cn' && (
|
||||
<MenuItem
|
||||
mt="0px"
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
ModalBody,
|
||||
Center,
|
||||
useBreakpointValue,
|
||||
GridProps
|
||||
GridProps,
|
||||
FlexProps
|
||||
} from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
@@ -57,6 +58,9 @@ const AppItem = ({
|
||||
justifyContent={'flex-start'}
|
||||
alignItems={'center'}
|
||||
className={app.key}
|
||||
w="fit-content"
|
||||
h="fit-content"
|
||||
maxW="100%"
|
||||
>
|
||||
<Center
|
||||
w={{ base: '64px', sm: '78px' }}
|
||||
@@ -68,6 +72,7 @@ const AppItem = ({
|
||||
overflow={'hidden'}
|
||||
_hover={{ transform: 'scale(1.05)' }}
|
||||
cursor={'pointer'}
|
||||
flexShrink={0}
|
||||
style={{
|
||||
touchAction: 'none',
|
||||
userSelect: 'none'
|
||||
@@ -97,7 +102,11 @@ const AppItem = ({
|
||||
fontSize={'14px'}
|
||||
fontWeight={500}
|
||||
textAlign={'center'}
|
||||
lineHeight={'normal'}
|
||||
lineHeight={'18px'}
|
||||
flexShrink={0}
|
||||
maxW="100%"
|
||||
wordBreak="break-word"
|
||||
noOfLines={2}
|
||||
style={{
|
||||
touchAction: 'none',
|
||||
userSelect: 'none'
|
||||
@@ -112,12 +121,16 @@ const AppItem = ({
|
||||
const AppGrid = ({
|
||||
children,
|
||||
gridGap,
|
||||
rows,
|
||||
appHeight,
|
||||
columns,
|
||||
gridProps
|
||||
}: {
|
||||
children: ReactNode;
|
||||
gridGap?: number;
|
||||
appHeight?: number;
|
||||
rows: number;
|
||||
appHeight: number;
|
||||
columns: number;
|
||||
gridProps?: Omit<
|
||||
GridProps,
|
||||
'templateColumns' | 'templateRows' | 'gap' | 'flexGrow' | 'flexShrink' | 'flexBasis'
|
||||
@@ -129,13 +142,12 @@ const AppGrid = ({
|
||||
flexShrink={0}
|
||||
flexGrow={0}
|
||||
flexBasis={'100%'}
|
||||
gap={`${gridGap}px`}
|
||||
templateColumns={{
|
||||
base: 'repeat(2, 1fr)',
|
||||
sm: 'repeat(3, 1fr)',
|
||||
lg: 'repeat(5, 1fr)'
|
||||
}}
|
||||
templateRows={`repeat(auto-fill, ${appHeight}px)`}
|
||||
gap={`${gridGap ?? 0}px`}
|
||||
templateColumns={`repeat(${columns}, 1fr)`}
|
||||
templateRows={`repeat(${rows}, minmax(${appHeight}px, auto))`}
|
||||
justifyItems={'center'}
|
||||
alignContent={'space-between'}
|
||||
justifyContent={'space-between'}
|
||||
className="apps-container"
|
||||
>
|
||||
{children}
|
||||
@@ -147,29 +159,27 @@ const AppGridPagingContainer = ({
|
||||
children,
|
||||
gridGap,
|
||||
appHeight,
|
||||
columns,
|
||||
totalPages,
|
||||
currentPage,
|
||||
handleNavigation,
|
||||
pageGap,
|
||||
onChange
|
||||
onChange,
|
||||
dragContainerProps
|
||||
}: {
|
||||
children: ReactNode;
|
||||
dragContainerProps?: FlexProps;
|
||||
gridGap: number;
|
||||
appHeight: number;
|
||||
columns: number;
|
||||
totalPages: number;
|
||||
currentPage: number;
|
||||
handleNavigation: boolean;
|
||||
pageGap: number;
|
||||
onChange: (currentPage: number, pageSize: number) => void;
|
||||
}) => {
|
||||
const gridWrapperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const columns =
|
||||
useBreakpointValue({
|
||||
base: 2,
|
||||
sm: 3,
|
||||
lg: 5
|
||||
}) ?? 2;
|
||||
const dragContainerRef = useRef<HTMLDivElement>(null);
|
||||
const gridContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [pageWidth, setPageWidth] = useState(0);
|
||||
const [itemsPerPage, setItemsPerPage] = useState(8);
|
||||
@@ -185,18 +195,19 @@ const AppGridPagingContainer = ({
|
||||
: (pageWidth + pageGap) * clampedCurrentPage - dragDelta;
|
||||
|
||||
const calculateItemsPerPage = useCallback(() => {
|
||||
if (!gridWrapperRef.current) return 8;
|
||||
if (!gridContainerRef.current) return 8;
|
||||
|
||||
const height = gridWrapperRef.current.clientHeight;
|
||||
const rows = Math.floor((height + gridGap) / (appHeight + gridGap));
|
||||
const height = gridContainerRef.current.clientHeight;
|
||||
// At least 1 row in a grid.
|
||||
const rows = Math.max(1, Math.floor((height + gridGap) / (appHeight + gridGap)));
|
||||
|
||||
return rows * columns;
|
||||
}, [columns, gridGap, appHeight]);
|
||||
|
||||
const calculatePageWidth = useCallback(() => {
|
||||
if (!gridWrapperRef.current) return 0;
|
||||
if (!gridContainerRef.current) return 0;
|
||||
|
||||
return (gridWrapperRef.current.scrollWidth + pageGap) / totalPages - pageGap;
|
||||
return (gridContainerRef.current.scrollWidth + pageGap) / totalPages - pageGap;
|
||||
}, [totalPages, pageGap]);
|
||||
|
||||
// Calculate items per page and scroll position on initial render and on screen size changes
|
||||
@@ -266,11 +277,11 @@ const AppGridPagingContainer = ({
|
||||
|
||||
// Pointer drag pagination
|
||||
useEffect(() => {
|
||||
const wrapper = gridWrapperRef.current;
|
||||
const wrapper = dragContainerRef.current;
|
||||
if (!wrapper) return;
|
||||
|
||||
const handlePointerDown = (e: PointerEvent) => {
|
||||
if (!gridWrapperRef.current) return;
|
||||
if (!dragContainerRef.current) return;
|
||||
|
||||
setDragStartX(e.screenX);
|
||||
setDragStartY(e.screenY);
|
||||
@@ -332,17 +343,19 @@ const AppGridPagingContainer = ({
|
||||
]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
ref={gridWrapperRef}
|
||||
h="full"
|
||||
w="full"
|
||||
transition="transform 0.2s ease-out"
|
||||
gap={`${pageGap}px`}
|
||||
style={{
|
||||
transform: `translateX(-${scrollPosition}px)`
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<Flex ref={dragContainerRef} w="full" h="full" {...dragContainerProps}>
|
||||
<Flex
|
||||
ref={gridContainerRef}
|
||||
h="full"
|
||||
w="full"
|
||||
transition="transform 0.2s ease-out"
|
||||
gap={`${pageGap}px`}
|
||||
style={{
|
||||
transform: `translateX(-${scrollPosition}px)`
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -477,12 +490,12 @@ const PageSwitcher = ({
|
||||
onChange: (target: number) => void;
|
||||
}) => {
|
||||
return (
|
||||
<Flex justifyContent="center" alignItems="center" gap="8px">
|
||||
<Flex justifyContent="center" alignItems="center" gap="11px">
|
||||
{Array.from({ length: pages }).map((_, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
w="12px"
|
||||
h="12px"
|
||||
w="10px"
|
||||
h="10px"
|
||||
borderRadius="50%"
|
||||
bg={index === current ? 'gray.400' : 'gray.200'}
|
||||
cursor="pointer"
|
||||
@@ -507,11 +520,19 @@ export default function Apps() {
|
||||
const folderRef = useRef<HTMLDivElement>(null);
|
||||
const modalContentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// min = 1 line app name
|
||||
// max = 2 lines app name
|
||||
const appHeight =
|
||||
useBreakpointValue({
|
||||
base: 114,
|
||||
md: 146
|
||||
}) ?? 114;
|
||||
base: 112,
|
||||
md: 126
|
||||
}) ?? 112;
|
||||
const columns =
|
||||
useBreakpointValue({
|
||||
base: 2,
|
||||
sm: 3,
|
||||
lg: 5
|
||||
}) ?? 2;
|
||||
const gridGap = 10;
|
||||
|
||||
const [itemsPerPageInGrid, setItemsPerPageInGrid] = useState(0);
|
||||
@@ -801,6 +822,7 @@ export default function Apps() {
|
||||
<AppGridPagingContainer
|
||||
gridGap={gridGap}
|
||||
appHeight={appHeight}
|
||||
columns={columns}
|
||||
// One page desktop, other apps are in folder
|
||||
totalPages={1}
|
||||
currentPage={currentPageInGrid}
|
||||
@@ -812,7 +834,13 @@ export default function Apps() {
|
||||
}}
|
||||
>
|
||||
{desktopPages.map((page, pageIndex) => (
|
||||
<AppGrid key={pageIndex} gridGap={gridGap} appHeight={appHeight}>
|
||||
<AppGrid
|
||||
key={pageIndex}
|
||||
gridGap={gridGap}
|
||||
rows={itemsPerPageInGrid / columns}
|
||||
appHeight={appHeight}
|
||||
columns={columns}
|
||||
>
|
||||
{page.map((app, index) => (
|
||||
<AppItem
|
||||
key={index}
|
||||
@@ -839,34 +867,46 @@ export default function Apps() {
|
||||
<ModalContent
|
||||
ref={modalContentRef}
|
||||
maxH={'90vh'}
|
||||
maxW={'90vw'}
|
||||
height={'720px'}
|
||||
borderRadius="16px"
|
||||
maxW={'min(90vw, 1240px)'}
|
||||
height={'min(calc(100vh - 80px), 720px)'}
|
||||
borderRadius="32px"
|
||||
boxShadow="0 4px 20px rgba(0, 0, 0, 0.15)"
|
||||
border="1px solid rgba(0, 0, 0, 0.05)"
|
||||
p="0"
|
||||
>
|
||||
<ModalCloseButton
|
||||
top="16px"
|
||||
right="24px"
|
||||
top="40px"
|
||||
right="40px"
|
||||
padding={'12.5px'}
|
||||
size={'20px'}
|
||||
border={'1.25px solid #E4E4E7'}
|
||||
borderRadius={'full'}
|
||||
color="#18181B"
|
||||
_hover={{ bg: 'rgba(0, 0, 0, 0.05)' }}
|
||||
/>
|
||||
<ModalBody
|
||||
py={{
|
||||
base: '36px',
|
||||
xl: '100px'
|
||||
}}
|
||||
px={{
|
||||
base: '12px',
|
||||
xl: '100px'
|
||||
}}
|
||||
overflow="hidden"
|
||||
className="folder-modal-body"
|
||||
>
|
||||
<ModalBody p="0" maxH={'full'}>
|
||||
<AppGridPagingContainer
|
||||
dragContainerProps={{
|
||||
pt: {
|
||||
base: '112.5px',
|
||||
sm: '124.5px'
|
||||
},
|
||||
pb: {
|
||||
base: '112.5px',
|
||||
sm: '144.5px'
|
||||
},
|
||||
px: {
|
||||
base: '31.5px',
|
||||
sm: '60px',
|
||||
md: '64px',
|
||||
lg: '82px',
|
||||
xl: '90px'
|
||||
},
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
gridGap={gridGap}
|
||||
appHeight={appHeight!}
|
||||
appHeight={appHeight}
|
||||
columns={columns}
|
||||
totalPages={totalPagesInFolder}
|
||||
currentPage={currentPageInFolder}
|
||||
handleNavigation={isFolderOpen}
|
||||
@@ -874,14 +914,16 @@ export default function Apps() {
|
||||
onChange={(currentPage, pageSize) => {
|
||||
setCurrentPageInFolder(currentPage);
|
||||
setItemsPerPageInFolder(pageSize);
|
||||
setTotalPagesInFolder(Math.ceil(moreApps.length / pageSize));
|
||||
setTotalPagesInFolder(Math.ceil(folderApps.length / pageSize));
|
||||
}}
|
||||
>
|
||||
{folderPages.map((page, pageIndex) => (
|
||||
<AppGrid
|
||||
key={pageIndex}
|
||||
gridGap={gridGap}
|
||||
rows={itemsPerPageInFolder / columns}
|
||||
appHeight={appHeight}
|
||||
columns={columns}
|
||||
gridProps={{
|
||||
alignContent: 'center'
|
||||
}}
|
||||
@@ -899,7 +941,14 @@ export default function Apps() {
|
||||
))}
|
||||
</AppGridPagingContainer>
|
||||
|
||||
<Box position="absolute" bottom="16px" left="0" right="0">
|
||||
<Box
|
||||
position="absolute"
|
||||
w="fit-content"
|
||||
bottom="32px"
|
||||
left="0"
|
||||
right="0"
|
||||
marginInline={'auto'}
|
||||
>
|
||||
<PageSwitcher
|
||||
pages={totalPagesInFolder}
|
||||
current={currentPageInFolder}
|
||||
|
||||
@@ -81,10 +81,13 @@ export default function WorkspaceToggle() {
|
||||
pl={'12px'}
|
||||
pr={'8px'}
|
||||
bg={isOpen ? 'secondary' : ''}
|
||||
minW={'0'}
|
||||
flex={'0 1 auto'}
|
||||
tabIndex={0}
|
||||
>
|
||||
{namespace?.id && (
|
||||
<Box
|
||||
flex={'0 0 auto'}
|
||||
boxSize={{
|
||||
base: '20px',
|
||||
sm: '24px'
|
||||
@@ -102,11 +105,9 @@ export default function WorkspaceToggle() {
|
||||
fontSize={'14px'}
|
||||
fontWeight={'500'}
|
||||
textTransform={'capitalize'}
|
||||
maxW={{
|
||||
base: '30px',
|
||||
xs: '80px',
|
||||
sm: 'fit-content'
|
||||
}}
|
||||
flex={'0 1 auto'}
|
||||
wordBreak={'break-all'}
|
||||
minW={'2ch'}
|
||||
overflow={'hidden'}
|
||||
textOverflow={'ellipsis'}
|
||||
whiteSpace={'nowrap'}
|
||||
|
||||
@@ -226,7 +226,7 @@ export default function EmailCheckComponent() {
|
||||
mr={{ base: '4px', lg: '8px' }}
|
||||
boxSize={{ base: '40px', lg: '56px' }}
|
||||
fontSize={{ base: '16px', lg: '20px' }}
|
||||
borderRadius={{ base: '8px', lg: '12px' }}
|
||||
borderRadius={'12px'}
|
||||
/>
|
||||
))}
|
||||
</PinInput>
|
||||
|
||||
@@ -289,7 +289,7 @@ export default function PhoneCheckComponent() {
|
||||
mr={{ base: '4px', lg: '8px' }}
|
||||
boxSize={{ base: '40px', lg: '56px' }}
|
||||
fontSize={{ base: '16px', lg: '20px' }}
|
||||
borderRadius={{ base: '8px', lg: '12px' }}
|
||||
borderRadius={'12px'}
|
||||
/>
|
||||
))}
|
||||
</PinInput>
|
||||
|
||||
Reference in New Issue
Block a user