mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
improve footer layout [skip ci]
This commit is contained in:
@@ -1,24 +1,32 @@
|
||||
import { Button, Menu, MenuButton, MenuList } from '@chakra-ui/react';
|
||||
import { Markdown } from '~/components';
|
||||
import { useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TFooterButton } from './types';
|
||||
|
||||
export const FooterButton = (props: TFooterButton) => {
|
||||
const { content, title, side, ...rest } = props;
|
||||
const placement = side === 'left' ? 'top-end' : side === 'right' ? 'top-start' : undefined;
|
||||
const placement = side === 'left' ? 'top' : side === 'right' ? 'top-start' : undefined;
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
const color = useOpposingColor(bg);
|
||||
const size = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
return (
|
||||
<Menu placement={placement}>
|
||||
<MenuButton
|
||||
as={Button}
|
||||
size="xs"
|
||||
size={size}
|
||||
variant="ghost"
|
||||
aria-label={typeof title === 'string' ? title : undefined}>
|
||||
{title}
|
||||
</MenuButton>
|
||||
<MenuList
|
||||
bg={bg}
|
||||
boxShadow="2xl"
|
||||
color={color}
|
||||
px={6}
|
||||
py={4}
|
||||
textAlign={side}
|
||||
textAlign="left"
|
||||
mx={{ base: 1, lg: 2 }}
|
||||
maxW={{ base: '100vw', lg: '50vw' }}
|
||||
{...rest}>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, Flex, Link } from '@chakra-ui/react';
|
||||
import { If } from '~/components';
|
||||
import { useConfig, useColorValue } from '~/context';
|
||||
import { Button, Flex, Link, HStack, useToken } from '@chakra-ui/react';
|
||||
import { If, ColorModeToggle } from '~/components';
|
||||
import { useConfig, useMobile, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useStrf } from '~/hooks';
|
||||
import { FooterButton } from './button';
|
||||
|
||||
@@ -16,41 +16,45 @@ export const Footer = () => {
|
||||
|
||||
const extUrl = useStrf(web.external_link.url, { primary_asn }) ?? '/';
|
||||
|
||||
const size = useBreakpointValue({ base: useToken('sizes', 4), lg: useToken('sizes', 6) });
|
||||
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
|
||||
const isMobile = useMobile();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
<HStack
|
||||
px={6}
|
||||
py={4}
|
||||
w="100%"
|
||||
zIndex={1}
|
||||
as="footer"
|
||||
bg={footerBg}
|
||||
flexWrap="wrap"
|
||||
textAlign="center"
|
||||
alignItems="center"
|
||||
color={footerColor}
|
||||
py={{ base: 4, lg: 2 }}
|
||||
justifyContent="space-between">
|
||||
spacing={{ base: 8, lg: 6 }}
|
||||
justifyContent={{ base: 'center', lg: 'space-between' }}>
|
||||
<If c={web.terms.enable}>
|
||||
<FooterButton side="left" content={content.terms} title={web.terms.title} />
|
||||
</If>
|
||||
<If c={web.help_menu.enable}>
|
||||
<FooterButton side="left" content={content.help_menu} title={web.help_menu.title} />
|
||||
</If>
|
||||
<Flex p={0} flexGrow={0} flexShrink={0} maxWidth="100%" flexBasis="auto" marginRight="auto" />
|
||||
<If c={web.credit.enable}>
|
||||
<FooterButton side="right" content={content.credit} title={<CodeIcon />} />
|
||||
</If>
|
||||
<If c={web.external_link.enable}>
|
||||
<Button
|
||||
size="xs"
|
||||
as={Link}
|
||||
isExternal
|
||||
href={extUrl}
|
||||
size={btnSize}
|
||||
variant="ghost"
|
||||
rightIcon={<ExtIcon />}
|
||||
aria-label={web.external_link.title}>
|
||||
{web.external_link.title}
|
||||
</Button>
|
||||
</If>
|
||||
</Flex>
|
||||
{!isMobile && <Flex p={0} flex="0 0 auto" maxWidth="100%" mr="auto" />}
|
||||
<If c={web.credit.enable}>
|
||||
<FooterButton side="right" content={content.credit} title={<CodeIcon />} />
|
||||
</If>
|
||||
<ColorModeToggle size={size} />
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
@@ -1,37 +1,35 @@
|
||||
import { forwardRef } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, Icon } from '@chakra-ui/react';
|
||||
import { Button, Icon, Tooltip } from '@chakra-ui/react';
|
||||
import { If } from '~/components';
|
||||
import { useColorMode, useColorValue } from '~/context';
|
||||
import { useColorMode, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TColorModeToggle } from './types';
|
||||
|
||||
const Sun = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiSun));
|
||||
const Moon = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiMoon));
|
||||
|
||||
const outlineColor = { dark: 'primary.300', light: 'primary.600' };
|
||||
|
||||
export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>((props, ref) => {
|
||||
const { size = '1.5rem', ...rest } = props;
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
|
||||
const bg = useColorValue('primary.500', 'yellow.300');
|
||||
const color = useOpposingColor(bg);
|
||||
const label = useColorValue('Switch to dark mode', 'Switch to light mode');
|
||||
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
|
||||
return (
|
||||
<Tooltip hasArrow placement="top-end" label={label} bg={bg} color={color}>
|
||||
<Button
|
||||
ref={ref}
|
||||
aria-label={label}
|
||||
size={btnSize}
|
||||
title={label}
|
||||
onClick={toggleColorMode}
|
||||
variant="ghost"
|
||||
borderWidth="1px"
|
||||
borderColor="transparent"
|
||||
_hover={{
|
||||
backgroundColor: 'unset',
|
||||
borderColor: outlineColor[colorMode],
|
||||
}}
|
||||
aria-label={label}
|
||||
_hover={{ color: bg }}
|
||||
color="currentColor"
|
||||
px={4}
|
||||
onClick={toggleColorMode}
|
||||
{...rest}>
|
||||
<If c={colorMode === 'light'}>
|
||||
<Icon as={Moon} boxSize={size} />
|
||||
@@ -40,5 +38,6 @@ export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>((
|
||||
<Icon as={Sun} boxSize={size} />
|
||||
</If>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
@@ -1,35 +1,34 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import {
|
||||
IconButton,
|
||||
Modal,
|
||||
ScaleFade,
|
||||
ModalBody,
|
||||
IconButton,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
useDisclosure,
|
||||
ModalCloseButton,
|
||||
} from '@chakra-ui/react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Markdown } from '~/components';
|
||||
import { useColorValue } from '~/context';
|
||||
import { isQueryContent } from '~/types';
|
||||
|
||||
import type { THelpModal } from './types';
|
||||
|
||||
const Info = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiInfo));
|
||||
|
||||
export const HelpModal = (props: THelpModal) => {
|
||||
const { item, name, ...rest } = props;
|
||||
const { visible, item, name, ...rest } = props;
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const bg = useColorValue('whiteFaded.50', 'blackFaded.800');
|
||||
const color = useColorValue('black', 'white');
|
||||
if (!isQueryContent(item)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
transition={{ duration: 0.2 }}
|
||||
exit={{ opacity: 0, scale: 0.3 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
initial={{ opacity: 0, scale: 0.3 }}>
|
||||
<ScaleFade in={visible} unmountOnExit>
|
||||
<IconButton
|
||||
mb={1}
|
||||
ml={1}
|
||||
@@ -42,9 +41,8 @@ export const HelpModal = (props: THelpModal) => {
|
||||
colorScheme="blue"
|
||||
aria-label={`${name}_help`}
|
||||
/>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
<Modal isOpen={isOpen} onClose={onClose} size="xl">
|
||||
</ScaleFade>
|
||||
<Modal isOpen={isOpen} onClose={onClose} size="xl" motionPreset="slideInRight">
|
||||
<ModalOverlay />
|
||||
<ModalContent bg={bg} color={color} py={4} borderRadius="md" {...rest}>
|
||||
<ModalHeader>{item.params.title}</ModalHeader>
|
||||
|
@@ -2,6 +2,7 @@ import type { ModalContentProps } from '@chakra-ui/react';
|
||||
import type { TQueryContent, TQueryFields } from '~/types';
|
||||
|
||||
export interface THelpModal extends ModalContentProps {
|
||||
item: TQueryContent;
|
||||
item: TQueryContent | null;
|
||||
name: TQueryFields;
|
||||
visible: boolean;
|
||||
}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { useRef } from 'react';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useConfig, useColorValue } from '~/context';
|
||||
import { If, Debugger, Greeting, Footer, Header } from '~/components';
|
||||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useLGState } from '~/hooks';
|
||||
import { ResetButton } from './resetButton';
|
||||
|
||||
import type { TFrame } from './types';
|
||||
|
||||
@@ -23,10 +24,15 @@ export const Frame = (props: TFrame) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex bg={bg} w="100%" color={color} flexDir="column" minHeight="100vh" ref={containerRef}>
|
||||
<Flex px={2} flex="0 1 auto" flexDirection="column">
|
||||
<Flex
|
||||
bg={bg}
|
||||
w="100%"
|
||||
id="__hyperglass"
|
||||
color={color}
|
||||
flexDir="column"
|
||||
minHeight="100vh"
|
||||
ref={containerRef}>
|
||||
<Header resetForm={handleReset} />
|
||||
</Flex>
|
||||
<Flex
|
||||
px={2}
|
||||
py={0}
|
||||
@@ -43,6 +49,7 @@ export const Frame = (props: TFrame) => {
|
||||
<If c={developer_mode}>
|
||||
<Debugger />
|
||||
</If>
|
||||
<ResetButton developerMode={developer_mode} resetForm={handleReset} />
|
||||
</Flex>
|
||||
<Greeting />
|
||||
</>
|
||||
|
39
hyperglass/ui/components/layout/resetButton.tsx
Normal file
39
hyperglass/ui/components/layout/resetButton.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Box, Flex, Icon, IconButton, Slide } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
import { useLGState, useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TResetButton } from './types';
|
||||
|
||||
const LeftArrow = dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaAngleLeft));
|
||||
|
||||
export const ResetButton = (props: TResetButton) => {
|
||||
const { developerMode, resetForm, ...rest } = props;
|
||||
const { isSubmitting } = useLGState();
|
||||
const bg = useColorValue('primary.500', 'primary.300');
|
||||
const color = useOpposingColor(bg);
|
||||
return (
|
||||
<Slide direction="left" in={isSubmitting.value} unmountOnExit style={{ width: 'auto' }}>
|
||||
<Box
|
||||
bg={bg}
|
||||
left={0}
|
||||
zIndex={4}
|
||||
bottom={24}
|
||||
boxSize={12}
|
||||
color={color}
|
||||
position="fixed"
|
||||
borderRightRadius="md"
|
||||
mb={developerMode ? 14 : undefined}>
|
||||
<Flex boxSize="100%" justifyContent="center" alignItems="center" {...rest}>
|
||||
<IconButton
|
||||
color="current"
|
||||
variant="ghost"
|
||||
aria-label="Reset"
|
||||
onClick={resetForm}
|
||||
icon={<Icon as={LeftArrow} boxSize={8} />}
|
||||
/>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Slide>
|
||||
);
|
||||
};
|
@@ -1,3 +1,8 @@
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export interface TFrame extends FlexProps {}
|
||||
|
||||
export interface TResetButton extends FlexProps {
|
||||
developerMode: boolean;
|
||||
resetForm(): void;
|
||||
}
|
||||
|
Reference in New Issue
Block a user