forked from mirrors/thatmattlove-hyperglass
parent
18e0b3e7e7
commit
ee7d8752f8
40 changed files with 260 additions and 331 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,5 +1,5 @@
|
|||
# Project
|
||||
hyperglass/hyperglass/static
|
||||
static/
|
||||
TODO*
|
||||
.env
|
||||
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ def init_ui_params(*, params: "Params", devices: "Devices") -> "UIParameters":
|
|||
_ui_params = params.frontend()
|
||||
_ui_params["web"]["logo"]["light_format"] = params.web.logo.light.suffix
|
||||
_ui_params["web"]["logo"]["dark_format"] = params.web.logo.dark.suffix
|
||||
_ui_params["web"]["logo"]["footer_format"] = params.web.logo.footer.suffix
|
||||
|
||||
return UIParameters(
|
||||
**_ui_params,
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ def migrate_images(app_path: Path, params: "UIParameters"):
|
|||
src_files = ()
|
||||
dst_files = ()
|
||||
|
||||
for image in ("light", "dark", "favicon"):
|
||||
for image in ("light", "dark", "favicon", "footer"):
|
||||
src: Path = getattr(params.web.logo, image)
|
||||
dst = images_dir / f"{image + src.suffix}"
|
||||
src_files += (src,)
|
||||
|
|
|
|||
|
|
@ -47,12 +47,12 @@ class ParamsPublic(HyperglassModel):
|
|||
description="Your network's primary ASN. This field is used to set some useful defaults such as the subtitle and PeeringDB URL.",
|
||||
)
|
||||
org_name: str = Field(
|
||||
"Beloved Hyperglass User",
|
||||
"Beloved Looking Glass User",
|
||||
title="Organization Name",
|
||||
description="Your organization's name. This field is used in the UI & API documentation to set fields such as `<meta/>` HTML tags for SEO and the terms & conditions footer component.",
|
||||
)
|
||||
site_title: str = Field(
|
||||
"hyperglass",
|
||||
"Looking Glass",
|
||||
title="Site Title",
|
||||
description="The name of your hyperglass site. This field is used in the UI & API documentation to set fields such as the `<title/>` HTML tag, and the terms & conditions footer component.",
|
||||
)
|
||||
|
|
@ -131,8 +131,8 @@ class Params(ParamsPublic, HyperglassModel):
|
|||
|
||||
for menu in web.menus:
|
||||
menu.content = menu.content.format(
|
||||
site_title=info.data.get("site_title", "hyperglass"),
|
||||
org_name=info.data.get("org_name", "hyperglass"),
|
||||
site_title=info.data.get("site_title", "Looking Glass"),
|
||||
org_name=info.data.get("org_name", "Looking Glass"),
|
||||
version=__version__,
|
||||
)
|
||||
return web
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ class Logo(HyperglassModel):
|
|||
light: FilePath = DEFAULT_IMAGES / "hyperglass-light.svg"
|
||||
dark: FilePath = DEFAULT_IMAGES / "hyperglass-dark.svg"
|
||||
favicon: FilePath = DEFAULT_IMAGES / "hyperglass-icon.svg"
|
||||
footer: FilePath = DEFAULT_IMAGES / "hyperglass-icon.svg"
|
||||
width: str = Field(default="50%", pattern=PERCENTAGE_PATTERN)
|
||||
height: t.Optional[str] = Field(default=None, pattern=PERCENTAGE_PATTERN)
|
||||
|
||||
|
|
@ -95,13 +96,13 @@ class LogoPublic(Logo):
|
|||
|
||||
light_format: str
|
||||
dark_format: str
|
||||
|
||||
footer_format: str
|
||||
|
||||
class Text(HyperglassModel):
|
||||
"""Validation model for params.branding.text."""
|
||||
|
||||
title_mode: TitleMode = "logo_only"
|
||||
title: str = Field(default="hyperglass", max_length=32)
|
||||
title: str = Field(default="Looking Glass", max_length=32)
|
||||
subtitle: str = Field(default="Network Looking Glass", max_length=32)
|
||||
query_location: str = "Location"
|
||||
query_type: str = "Query Type"
|
||||
|
|
@ -132,7 +133,7 @@ class Text(HyperglassModel):
|
|||
class ThemeColors(HyperglassModel):
|
||||
"""Validation model for theme colors."""
|
||||
|
||||
black: Color = "#000000"
|
||||
black: Color = "#2d3635"
|
||||
white: Color = "#ffffff"
|
||||
dark: Color = "#010101"
|
||||
light: Color = "#f5f6f7"
|
||||
|
|
@ -169,7 +170,7 @@ class ThemeColors(HyperglassModel):
|
|||
class ThemeFonts(HyperglassModel):
|
||||
"""Validation model for theme fonts."""
|
||||
|
||||
body: str = "Nunito"
|
||||
body: str = "Inter"
|
||||
mono: str = "Fira Code"
|
||||
|
||||
|
||||
|
|
@ -226,6 +227,7 @@ class HighlightPattern(HyperglassModel):
|
|||
class Web(HyperglassModel):
|
||||
"""Validation model for all web/browser-related configuration."""
|
||||
|
||||
copyright: t.Optional[str] = "All rights reserved"
|
||||
credit: Credit = Credit()
|
||||
dns_provider: DnsOverHttps = DnsOverHttps()
|
||||
links: t.Sequence[Link] = [
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ import { useMemo } from 'react';
|
|||
import { Button, Menu, MenuButton, MenuList } from '@chakra-ui/react';
|
||||
import { useConfig } from '~/context';
|
||||
import { Markdown } from '~/elements';
|
||||
import { useColorValue, useBreakpointValue, useOpposingColor, useStrf } from '~/hooks';
|
||||
import { useStrf } from '~/hooks';
|
||||
|
||||
import type { MenuListProps } from '@chakra-ui/react';
|
||||
import type { Config } from '~/types';
|
||||
|
||||
interface FooterButtonProps extends Omit<MenuListProps, 'title'> {
|
||||
side: 'left' | 'right';
|
||||
title?: MenuListProps['children'];
|
||||
content: string;
|
||||
}
|
||||
|
|
@ -27,26 +26,26 @@ function getConfigFmt(config: Config): Record<string, string> {
|
|||
}
|
||||
|
||||
export const FooterButton = (props: FooterButtonProps): JSX.Element => {
|
||||
const { content, title, side, ...rest } = props;
|
||||
const { content, title, ...rest } = props;
|
||||
|
||||
const config = useConfig();
|
||||
const strF = useStrf();
|
||||
const fmt = useMemo(() => getConfigFmt(config), [config]);
|
||||
const fmtContent = useMemo(() => strF(content, fmt), [fmt, content, strF]);
|
||||
|
||||
const placement = side === 'left' ? 'top' : side === 'right' ? 'top-end' : undefined;
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
const color = useOpposingColor(bg);
|
||||
const size = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
|
||||
return (
|
||||
<Menu placement={placement} preventOverflow isLazy>
|
||||
<Menu
|
||||
placement="top"
|
||||
preventOverflow
|
||||
isLazy
|
||||
>
|
||||
<MenuButton
|
||||
zIndex={2}
|
||||
py={1}
|
||||
fontWeight="normal"
|
||||
as={Button}
|
||||
size={size}
|
||||
variant="ghost"
|
||||
lineHeight={0}
|
||||
variant="link"
|
||||
colorScheme="transparent"
|
||||
aria-label={typeof title === 'string' ? title : undefined}
|
||||
>
|
||||
{title}
|
||||
|
|
@ -54,10 +53,10 @@ export const FooterButton = (props: FooterButtonProps): JSX.Element => {
|
|||
<MenuList
|
||||
px={6}
|
||||
py={4}
|
||||
bg={bg}
|
||||
bg="white"
|
||||
// Ensure the height doesn't overtake the viewport, especially on mobile. See overflow also.
|
||||
maxH="50vh"
|
||||
color={color}
|
||||
color="black"
|
||||
boxShadow="2xl"
|
||||
textAlign="left"
|
||||
overflowY="auto"
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
import { forwardRef } from 'react';
|
||||
import { Button, Tooltip } from '@chakra-ui/react';
|
||||
import { Switch, Case } from 'react-if';
|
||||
import { DynamicIcon } from '~/elements';
|
||||
import { useOpposingColor, useColorMode, useColorValue, useBreakpointValue } from '~/hooks';
|
||||
|
||||
import type { ButtonProps } from '@chakra-ui/react';
|
||||
|
||||
interface ColorModeToggleProps extends Omit<ButtonProps, 'size'> {
|
||||
size?: string | number;
|
||||
}
|
||||
|
||||
export const ColorModeToggle = forwardRef<HTMLButtonElement, ColorModeToggleProps>(
|
||||
(props: ColorModeToggleProps, 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}
|
||||
size={btnSize}
|
||||
title={label}
|
||||
variant="ghost"
|
||||
aria-label={label}
|
||||
_hover={{ color: bg }}
|
||||
color="currentColor"
|
||||
onClick={toggleColorMode}
|
||||
{...rest}
|
||||
>
|
||||
<Switch>
|
||||
<Case condition={colorMode === 'light'}>
|
||||
<DynamicIcon icon={{ hi: 'HiMoon' }} boxSize={size} />
|
||||
</Case>
|
||||
<Case condition={colorMode === 'dark'}>
|
||||
<DynamicIcon icon={{ hi: 'HiSun' }} boxSize={size} />
|
||||
</Case>
|
||||
</Switch>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
import { Flex, HStack, useToken } from '@chakra-ui/react';
|
||||
import { Box, Container, Flex, Grid, GridItem } from '@chakra-ui/react';
|
||||
import { useMemo } from 'react';
|
||||
import { useConfig } from '~/context';
|
||||
import { DynamicIcon } from '~/elements';
|
||||
import { useBreakpointValue, useColorValue, useMobile } from '~/hooks';
|
||||
import { DynamicIcon, Markdown } from '~/elements';
|
||||
import { useMobile } from '~/hooks';
|
||||
import { isLink, isMenu } from '~/types';
|
||||
import { FooterButton } from './button';
|
||||
import { ColorModeToggle } from './color-mode';
|
||||
import { FooterLink } from './link';
|
||||
|
||||
import type { ButtonProps, LinkProps } from '@chakra-ui/react';
|
||||
import type { Link, Menu } from '~/types';
|
||||
import { Logo } from './logo';
|
||||
|
||||
type MenuItems = (Link | Menu)[];
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ function buildItems(links: Link[], menus: Menu[]): [MenuItems, MenuItems] {
|
|||
return [left, right];
|
||||
}
|
||||
|
||||
const LinkOnSide = (props: { item: ArrayElement<MenuItems>; side: 'left' | 'right' }) => {
|
||||
const NavLink = (props: { item: ArrayElement<MenuItems> }) => {
|
||||
const { item, side } = props;
|
||||
if (isLink(item)) {
|
||||
const icon: Partial<ButtonProps & LinkProps> = {};
|
||||
|
|
@ -40,49 +40,55 @@ const LinkOnSide = (props: { item: ArrayElement<MenuItems>; side: 'left' | 'righ
|
|||
};
|
||||
|
||||
export const Footer = (): JSX.Element => {
|
||||
const { web, content } = useConfig();
|
||||
|
||||
const footerBg = useColorValue('blackAlpha.50', 'whiteAlpha.100');
|
||||
const footerColor = useColorValue('black', 'white');
|
||||
|
||||
const size = useBreakpointValue({ base: useToken('sizes', 4), lg: useToken('sizes', 6) });
|
||||
|
||||
const isMobile = useMobile();
|
||||
const { web } = useConfig();
|
||||
|
||||
const [left, right] = useMemo(() => buildItems(web.links, web.menus), [web.links, web.menus]);
|
||||
|
||||
return (
|
||||
<HStack
|
||||
<Box
|
||||
w="100%"
|
||||
bg="#005e8a"
|
||||
color="#ffffff"
|
||||
fontSize=".945rem"
|
||||
>
|
||||
<Container maxW="8xl">
|
||||
<Grid
|
||||
px={6}
|
||||
py={4}
|
||||
w="100%"
|
||||
zIndex={1}
|
||||
as="footer"
|
||||
bg={footerBg}
|
||||
whiteSpace="nowrap"
|
||||
color={footerColor}
|
||||
spacing={{ base: 8, lg: 6 }}
|
||||
display={{ base: 'inline-block', lg: 'flex' }}
|
||||
overflowY={{ base: 'auto', lg: 'unset' }}
|
||||
justifyContent={{ base: 'center', lg: 'space-between' }}
|
||||
templateColumns="repeat(4, 1fr)"
|
||||
gap={6}
|
||||
>
|
||||
{left.map(item => (
|
||||
<LinkOnSide key={item.title} item={item} side="left" />
|
||||
))}
|
||||
{!isMobile && <Flex p={0} flex="1 0 auto" maxWidth="100%" mr="auto" />}
|
||||
{right.map(item => (
|
||||
<LinkOnSide key={item.title} item={item} side="right" />
|
||||
))}
|
||||
{web.credit.enable && (
|
||||
<FooterButton
|
||||
<GridItem colSpan={{base: 4, md: 2}}>
|
||||
<Flex
|
||||
flex="1 0 auto"
|
||||
key="credit"
|
||||
side="right"
|
||||
content={content.credit}
|
||||
title={<DynamicIcon icon={{ fi: 'FiCode' }} boxSize={size} />}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ColorModeToggle size={size} />
|
||||
</HStack>
|
||||
side="left"
|
||||
maxW="50%"
|
||||
>
|
||||
<Box w="50px" maxW="15vw" my="2" mr="4">
|
||||
<Logo />
|
||||
</Box>
|
||||
<Markdown content={web.copyright} />
|
||||
</Flex>
|
||||
</GridItem>
|
||||
<GridItem colSpan={{base: 4, md: 1}}>
|
||||
<Flex flexDirection="column" alignItems="start">
|
||||
{left.map(item => (
|
||||
<NavLink key={item.title} item={item} />
|
||||
))}
|
||||
</Flex>
|
||||
</GridItem>
|
||||
<GridItem colSpan={{base: 4, md: 1}}>
|
||||
{right.map(item => (
|
||||
<NavLink key={item.title} item={item} />
|
||||
))}
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Button, Link } from '@chakra-ui/react';
|
||||
import { useBreakpointValue } from '~/hooks';
|
||||
|
||||
import type { ButtonProps, LinkProps } from '@chakra-ui/react';
|
||||
|
||||
|
|
@ -7,9 +6,17 @@ type FooterLinkProps = ButtonProps & LinkProps & { title: string };
|
|||
|
||||
export const FooterLink = (props: FooterLinkProps): JSX.Element => {
|
||||
const { title } = props;
|
||||
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
return (
|
||||
<Button as={Link} isExternal size={btnSize} variant="ghost" aria-label={title} {...props}>
|
||||
<Button
|
||||
as={Link}
|
||||
isExternal
|
||||
py={1}
|
||||
fontWeight="normal"
|
||||
variant="link"
|
||||
colorScheme="transparent"
|
||||
aria-label={title}
|
||||
{...props}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
|
|
|
|||
37
hyperglass/ui/components/footer/logo.tsx
Normal file
37
hyperglass/ui/components/footer/logo.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { Image, Skeleton } from '@chakra-ui/react';
|
||||
import { useConfig } from '~/context';
|
||||
|
||||
import type { ImageProps } from '@chakra-ui/react';
|
||||
|
||||
export const Logo = (props: ImageProps): JSX.Element => {
|
||||
const { web } = useConfig();
|
||||
const { footerFormat } = web.logo;
|
||||
|
||||
return (
|
||||
<Image
|
||||
src={`/images/footer${footerFormat}`}
|
||||
alt={web.text.title}
|
||||
width="100%"
|
||||
css={{
|
||||
objectFit: 'contain',
|
||||
objectPosition: 'top',
|
||||
userDrag: 'none',
|
||||
userSelect: 'none',
|
||||
msUserSelect: 'none',
|
||||
MozUserSelect: 'none',
|
||||
WebkitUserDrag: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
}}
|
||||
fallback={
|
||||
<Skeleton
|
||||
isLoaded={false}
|
||||
borderRadius="md"
|
||||
endColor="light.500"
|
||||
startColor="whiteSolid.100"
|
||||
width="100%"
|
||||
/>
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -2,7 +2,7 @@ import { Flex, FormControl, FormErrorMessage, FormLabel } from '@chakra-ui/react
|
|||
import { useMemo } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { If, Then } from 'react-if';
|
||||
import { useBooleanValue, useColorValue } from '~/hooks';
|
||||
import { useBooleanValue } from '~/hooks';
|
||||
|
||||
import type { FormControlProps } from '@chakra-ui/react';
|
||||
import type { FieldError } from 'react-hook-form';
|
||||
|
|
@ -18,8 +18,8 @@ interface FormFieldProps extends FormControlProps {
|
|||
|
||||
export const FormField = (props: FormFieldProps): JSX.Element => {
|
||||
const { name, label, children, labelAddOn, fieldAddOn, hiddenLabels = false, ...rest } = props;
|
||||
const labelColor = useColorValue('blackAlpha.700', 'whiteAlpha.700');
|
||||
const errorColor = useColorValue('red.500', 'red.300');
|
||||
const labelColor = 'blackAlpha.700';
|
||||
const errorColor = 'red.500';
|
||||
const opacity = useBooleanValue(hiddenLabels, 0, undefined);
|
||||
|
||||
const { formState } = useFormContext<FormData>();
|
||||
|
|
@ -47,7 +47,6 @@ export const FormField = (props: FormFieldProps): JSX.Element => {
|
|||
>
|
||||
<FormLabel
|
||||
pr={0}
|
||||
mb={{ lg: 4 }}
|
||||
htmlFor={name}
|
||||
display="flex"
|
||||
opacity={opacity}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
import { If, Then } from 'react-if';
|
||||
import { Markdown } from '~/elements';
|
||||
import { useConfig } from '~/context';
|
||||
import { useGreeting, useColorValue, useOpposingColor } from '~/hooks';
|
||||
import { useGreeting } from '~/hooks';
|
||||
|
||||
import type { ModalContentProps } from '@chakra-ui/react';
|
||||
|
||||
|
|
@ -20,9 +20,6 @@ export const Greeting = (props: ModalContentProps): JSX.Element => {
|
|||
const { web, content } = useConfig();
|
||||
const { isAck, isOpen, open, ack } = useGreeting();
|
||||
|
||||
const bg = useColorValue('white', 'gray.800');
|
||||
const color = useOpposingColor(bg);
|
||||
|
||||
useEffect(() => {
|
||||
if (!web.greeting.enable && !web.greeting.required) {
|
||||
ack(true, false);
|
||||
|
|
@ -44,8 +41,8 @@ export const Greeting = (props: ModalContentProps): JSX.Element => {
|
|||
<ModalOverlay />
|
||||
<ModalContent
|
||||
py={4}
|
||||
bg={bg}
|
||||
color={color}
|
||||
bg="white"
|
||||
color="black"
|
||||
borderRadius="md"
|
||||
maxW={{ base: '95%', md: '75%' }}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import { Flex, ScaleFade } from '@chakra-ui/react';
|
||||
import { Container, Flex, ScaleFade } from '@chakra-ui/react';
|
||||
import { motionChakra } from '~/elements';
|
||||
import { useBooleanValue, useBreakpointValue, useFormInteractive } from '~/hooks';
|
||||
import { Title } from './title';
|
||||
|
||||
const Wrapper = motionChakra('header', {
|
||||
baseStyle: { display: 'flex', px: 4, pt: 6, minH: 16, w: 'full', flex: '0 1 auto' },
|
||||
});
|
||||
const Wrapper = motionChakra('header');
|
||||
|
||||
export const Header = (): JSX.Element => {
|
||||
const formInteractive = useFormInteractive();
|
||||
|
|
@ -18,6 +16,14 @@ export const Header = (): JSX.Element => {
|
|||
|
||||
return (
|
||||
<Wrapper layout="position">
|
||||
<Container
|
||||
maxW="8xl"
|
||||
display="flex"
|
||||
px={4}
|
||||
pt={6}
|
||||
minH={16}
|
||||
flex="0 1 auto"
|
||||
>
|
||||
<ScaleFade in initialScale={0.5} style={{ width: '100%' }}>
|
||||
<Flex
|
||||
height="100%"
|
||||
|
|
@ -29,6 +35,7 @@ export const Header = (): JSX.Element => {
|
|||
<Title />
|
||||
</Flex>
|
||||
</ScaleFade>
|
||||
</Container>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,46 +1,17 @@
|
|||
import { Image, Skeleton } from '@chakra-ui/react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useConfig } from '~/context';
|
||||
import { useColorValue } from '~/hooks';
|
||||
|
||||
import type { ImageProps } from '@chakra-ui/react';
|
||||
|
||||
/**
|
||||
* Custom hook to handle loading the user's logo, errors loading the logo, and color mode changes.
|
||||
*/
|
||||
function useLogo(): [string, () => void] {
|
||||
const { web } = useConfig();
|
||||
const { darkFormat, lightFormat } = web.logo;
|
||||
|
||||
const src = useColorValue(`/images/light${darkFormat}`, `/images/dark${lightFormat}`);
|
||||
|
||||
// Use the hyperglass logo if the user's logo can't be loaded for whatever reason.
|
||||
const [fallback, setSource] = useState<string | null>(null);
|
||||
|
||||
// If the user image cannot be loaded, log an error to the console and set the fallback image.
|
||||
const setFallback = useCallback(() => {
|
||||
console.warn(`Error loading image from '${src}'`);
|
||||
setSource('https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-light.svg');
|
||||
}, [src]);
|
||||
|
||||
// Only return the fallback image if it's been set.
|
||||
return useMemo(() => [fallback ?? src, setFallback], [fallback, setFallback, src]);
|
||||
}
|
||||
|
||||
export const Logo = (props: ImageProps): JSX.Element => {
|
||||
const { web } = useConfig();
|
||||
const { width } = web.logo;
|
||||
|
||||
const skeletonA = useColorValue('whiteSolid.100', 'blackSolid.800');
|
||||
const skeletonB = useColorValue('light.500', 'dark.500');
|
||||
|
||||
const [source, setFallback] = useLogo();
|
||||
const { lightFormat, width } = web.logo;
|
||||
|
||||
return (
|
||||
<Image
|
||||
src={source}
|
||||
src={`/images/light${lightFormat}`}
|
||||
alt={web.text.title}
|
||||
onError={setFallback}
|
||||
maxW={{ base: '100%', md: width }}
|
||||
width="auto"
|
||||
css={{
|
||||
|
|
@ -55,8 +26,8 @@ export const Logo = (props: ImageProps): JSX.Element => {
|
|||
<Skeleton
|
||||
isLoaded={false}
|
||||
borderRadius="md"
|
||||
endColor={skeletonB}
|
||||
startColor={skeletonA}
|
||||
endColor="light.500"
|
||||
startColor="whiteSolid.100"
|
||||
width={{ base: 64, lg: 80 }}
|
||||
height={{ base: 12, lg: 16 }}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Flex } from '@chakra-ui/react';
|
||||
import { Container, Flex } from '@chakra-ui/react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { isSafari } from 'react-device-detect';
|
||||
import { If, Then } from 'react-if';
|
||||
|
|
@ -53,6 +53,7 @@ export const Layout = (props: FlexProps): JSX.Element => {
|
|||
minHeight={isSafari ? '-webkit-fill-available' : '100vh'}
|
||||
>
|
||||
<Header />
|
||||
<Container maxW="8xl" display="flex" flex="1 1 auto">
|
||||
<Main
|
||||
layout
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
|
|
@ -62,6 +63,7 @@ export const Layout = (props: FlexProps): JSX.Element => {
|
|||
>
|
||||
{props.children}
|
||||
</Main>
|
||||
</Container>
|
||||
<Footer />
|
||||
<If condition={developerMode}>
|
||||
<Then>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { useMemo, useState } from 'react';
|
||||
import { Flex, Avatar, chakra } from '@chakra-ui/react';
|
||||
import { motionChakra } from '~/elements';
|
||||
import { useColorValue, useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { SingleOption } from '~/types';
|
||||
import type { LocationOption } from './query-location';
|
||||
|
|
@ -43,11 +42,9 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => {
|
|||
}
|
||||
}
|
||||
|
||||
const bg = useColorValue('white', 'blackSolid.600');
|
||||
const imageBorder = useColorValue('gray.600', 'whiteAlpha.800');
|
||||
const fg = useOpposingColor(bg);
|
||||
const checkedBorder = useColorValue('blue.400', 'blue.300');
|
||||
const errorBorder = useColorValue('red.500', 'red.300');
|
||||
const imageBorder = 'gray.600';
|
||||
const checkedBorder = 'brand.500';
|
||||
const errorBorder = 'red.500';
|
||||
|
||||
const borderColor = useMemo(
|
||||
() =>
|
||||
|
|
@ -64,7 +61,7 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => {
|
|||
);
|
||||
return (
|
||||
<LocationCardWrapper
|
||||
bg={bg}
|
||||
bg="white"
|
||||
key={label}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
borderColor={borderColor}
|
||||
|
|
@ -76,7 +73,6 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => {
|
|||
<>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<chakra.h2
|
||||
color={fg}
|
||||
fontWeight="bold"
|
||||
mt={{ base: 2, md: 0 }}
|
||||
fontSize={{ base: 'lg', md: 'xl' }}
|
||||
|
|
@ -84,7 +80,7 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => {
|
|||
{label}
|
||||
</chakra.h2>
|
||||
<Avatar
|
||||
color={fg}
|
||||
color="black"
|
||||
name={label}
|
||||
boxSize={12}
|
||||
rounded="full"
|
||||
|
|
@ -97,7 +93,7 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => {
|
|||
</Flex>
|
||||
|
||||
{option?.data?.description && (
|
||||
<chakra.p mt={2} color={fg} opacity={0.6} fontSize="sm">
|
||||
<chakra.p mt={2} opacity={0.6} fontSize="sm">
|
||||
{option.data.description as string}
|
||||
</chakra.p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ export const LookingGlassForm = (): JSX.Element => {
|
|||
w="100%"
|
||||
mx="auto"
|
||||
textAlign="left"
|
||||
maxW={{ base: '100%', lg: '75%' }}
|
||||
maxW="100%"
|
||||
onSubmit={handleSubmit(submitHandler)}
|
||||
>
|
||||
<FormRow>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export const Meta = (): JSX.Element => {
|
|||
const [location, setLocation] = useState('/');
|
||||
|
||||
const {
|
||||
siteTitle: title = 'hyperglass',
|
||||
siteTitle: title = 'Looking Glass',
|
||||
siteDescription: description = 'Network Looking Glass',
|
||||
} = useConfig();
|
||||
|
||||
|
|
@ -23,16 +23,13 @@ export const Meta = (): JSX.Element => {
|
|||
<Head>
|
||||
<title key="title">{title}</title>
|
||||
<meta name="url" content={location} />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="og:title" content={title} />
|
||||
<meta name="og:url" content={location} />
|
||||
<meta name="description" content={description} />
|
||||
<meta property="og:image:alt" content={siteName} />
|
||||
<meta name="og:description" content={description} />
|
||||
<meta name="hyperglass-version" content={config.version} />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0"
|
||||
/>
|
||||
<meta name="lg-version" content={config.version} />
|
||||
</Head>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -147,8 +147,6 @@ export const ASPath = (props: ASPathProps): JSX.Element => {
|
|||
export const Communities = (props: CommunitiesProps): JSX.Element => {
|
||||
const { communities } = props;
|
||||
const { web } = useConfig();
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
const color = useOpposingColor(bg);
|
||||
return (
|
||||
<If condition={communities.length === 0}>
|
||||
<Then>
|
||||
|
|
@ -165,10 +163,10 @@ export const Communities = (props: CommunitiesProps): JSX.Element => {
|
|||
</MenuButton>
|
||||
<MenuList
|
||||
p={3}
|
||||
bg={bg}
|
||||
bg="white"
|
||||
minW={32}
|
||||
width="unset"
|
||||
color={color}
|
||||
color="black"
|
||||
boxShadow="2xl"
|
||||
textAlign="left"
|
||||
fontFamily="mono"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export const PathButton = (props: PathButtonProps): JSX.Element => {
|
|||
const { onOpen } = props;
|
||||
return (
|
||||
<Tooltip hasArrow label="View AS Path" placement="top">
|
||||
<Button as="a" mx={1} size="sm" variant="ghost" onClick={onOpen} colorScheme="secondary">
|
||||
<Button as="a" mx={1} size="sm" variant="ghost" onClick={onOpen} colorScheme="primary">
|
||||
<DynamicIcon icon={{ bi: 'BiNetworkChart' }} boxSize="16px" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -6,19 +6,17 @@ import {
|
|||
PopoverContent,
|
||||
PopoverCloseButton,
|
||||
} from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/hooks';
|
||||
|
||||
import type { PromptProps } from './types';
|
||||
|
||||
export const DesktopPrompt = (props: PromptProps): JSX.Element => {
|
||||
const { trigger, children, ...disclosure } = props;
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
|
||||
return (
|
||||
<Popover closeOnBlur={false} {...disclosure}>
|
||||
<PopoverTrigger>{trigger}</PopoverTrigger>
|
||||
<PopoverContent bg={bg}>
|
||||
<PopoverArrow bg={bg} />
|
||||
<PopoverContent bg="white">
|
||||
<PopoverArrow bg="white" />
|
||||
<PopoverCloseButton />
|
||||
<PopoverBody p={6}>{children}</PopoverBody>
|
||||
</PopoverContent>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import { Modal, ModalBody, ModalOverlay, ModalContent, ModalCloseButton } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/hooks';
|
||||
|
||||
import type { PromptProps } from './types';
|
||||
|
||||
export const MobilePrompt = (props: PromptProps): JSX.Element => {
|
||||
const { children, trigger, ...disclosure } = props;
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
return (
|
||||
<>
|
||||
{trigger}
|
||||
|
|
@ -18,7 +16,7 @@ export const MobilePrompt = (props: PromptProps): JSX.Element => {
|
|||
{...disclosure}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent bg={bg}>
|
||||
<ModalContent bg="white">
|
||||
<ModalCloseButton />
|
||||
<ModalBody px={4} py={10}>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -120,10 +120,10 @@ export const QueryLocation = (props: QueryLocationProps): JSX.Element => {
|
|||
<>
|
||||
{options.length === 1 ? (
|
||||
<Wrap
|
||||
p={{ lg: 4 }}
|
||||
p={{ lg: 2 }}
|
||||
align="flex-start"
|
||||
shouldWrapChildren
|
||||
spacing={{ base: 4, lg: 8 }}
|
||||
spacing={{ base: 4, lg: 6 }}
|
||||
justify={{ base: 'center', lg: 'center' }}
|
||||
>
|
||||
{options[0].options.map(opt => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useDirective, useFormState } from '~/hooks';
|
|||
import { isSelectDirective } from '~/types';
|
||||
import { UserIP } from './user-ip';
|
||||
|
||||
import { type UseFormRegister, useForm } from 'react-hook-form';
|
||||
import { type UseFormRegister } from 'react-hook-form';
|
||||
import type { GroupBase, OptionProps } from 'react-select';
|
||||
import type { SelectOnChange } from '~/components/select';
|
||||
import type { Directive, FormData, OnChangeArgs, SingleOption } from '~/types';
|
||||
|
|
@ -87,7 +87,7 @@ export const QueryTarget = (props: QueryTargetProps): JSX.Element => {
|
|||
<InputGroup size="lg">
|
||||
<Input
|
||||
bg="white"
|
||||
color="gray.400"
|
||||
color="black"
|
||||
borderRadius="md"
|
||||
borderColor="gray.100"
|
||||
value={displayTarget}
|
||||
|
|
@ -96,12 +96,6 @@ export const QueryTarget = (props: QueryTargetProps): JSX.Element => {
|
|||
name="queryTargetDisplay"
|
||||
onChange={handleInputChange}
|
||||
_placeholder={{ color: 'gray.600' }}
|
||||
_dark={{
|
||||
bg: 'blackSolid.800',
|
||||
color: 'whiteAlpha.800',
|
||||
borderColor: 'whiteAlpha.50',
|
||||
_placeholder: { color: 'whiteAlpha.700' },
|
||||
}}
|
||||
/>
|
||||
<InputRightElement w="max-content" pr={2}>
|
||||
<UserIP setTarget={handleUserIPChange} />
|
||||
|
|
|
|||
|
|
@ -13,18 +13,16 @@ interface ResetButtonProps extends FlexProps {
|
|||
export const ResetButton = (props: ResetButtonProps): JSX.Element => {
|
||||
const { developerMode, resetForm, ...rest } = props;
|
||||
const status = useFormState(s => s.status);
|
||||
const bg = useColorValue('primary.500', 'primary.300');
|
||||
const color = useOpposingColor(bg);
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{status === 'results' && (
|
||||
<AnimatedDiv
|
||||
bg={bg}
|
||||
bg="brand.500"
|
||||
left={0}
|
||||
zIndex={4}
|
||||
bottom={24}
|
||||
boxSize={12}
|
||||
color={color}
|
||||
color="white"
|
||||
position="fixed"
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: '-100%' }}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
|||
import { Button, Stack, Text, VStack } from '@chakra-ui/react';
|
||||
import { useConfig } from '~/context';
|
||||
import { DynamicIcon } from '~/elements';
|
||||
import { useStrf, useColorValue, useDNSQuery, useFormState } from '~/hooks';
|
||||
import { useStrf, useDNSQuery, useFormState } from '~/hooks';
|
||||
|
||||
import type { DnsOverHttps } from '~/types';
|
||||
|
||||
|
|
@ -28,8 +28,8 @@ export const ResolvedTarget = (props: ResolvedTargetProps): JSX.Element => {
|
|||
const displayTarget = useFormState(s => s.target.display);
|
||||
const setFormValue = useFormState(s => s.setFormValue);
|
||||
|
||||
const color = useColorValue('secondary.500', 'secondary.300');
|
||||
const errorColor = useColorValue('red.500', 'red.300');
|
||||
const color = 'secondary.500';
|
||||
const errorColor = 'red.500';
|
||||
|
||||
const tooltip4 = strF(web.text.fqdnTooltip, { protocol: 'IPv4' });
|
||||
const tooltip6 = strF(web.text.fqdnTooltip, { protocol: 'IPv6' });
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const CopyButton = (props: CopyButtonProps): JSX.Element => {
|
|||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={onCopy}
|
||||
colorScheme="secondary"
|
||||
colorScheme="primary"
|
||||
{...rest}
|
||||
>
|
||||
<DynamicIcon icon={{ fi: hasCopied ? 'FiCheck' : 'FiCopy' }} boxSize="16px" />
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { AccordionIcon, Box, HStack, Spinner, Text, Tooltip } from '@chakra-ui/r
|
|||
import { useMemo } from 'react';
|
||||
import { useConfig } from '~/context';
|
||||
import { DynamicIcon } from '~/elements';
|
||||
import { useColorValue, useOpposingColor, useStrf } from '~/hooks';
|
||||
import { useOpposingColor, useStrf } from '~/hooks';
|
||||
|
||||
import type { ErrorLevels } from '~/types';
|
||||
|
||||
|
|
@ -26,9 +26,9 @@ const runtimeText = (runtime: number, text: string): string => {
|
|||
export const ResultHeader = (props: ResultHeaderProps): JSX.Element => {
|
||||
const { title, loading, isError, errorMsg, errorLevel, runtime } = props;
|
||||
|
||||
const status = useColorValue('primary.500', 'primary.300');
|
||||
const warning = useColorValue(`${errorLevel}.500`, `${errorLevel}.300`);
|
||||
const defaultStatus = useColorValue('success.500', 'success.300');
|
||||
const status = 'primary.500';
|
||||
const warning = `${errorLevel}.500`;
|
||||
const defaultStatus = 'success.500';
|
||||
|
||||
const { web } = useConfig();
|
||||
const strF = useStrf();
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ const AccordionHeaderWrapper = chakra('div', {
|
|||
baseStyle: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
bg: 'white',
|
||||
_hover: { bg: 'blackAlpha.50' },
|
||||
_focus: { boxShadow: 'outline' },
|
||||
},
|
||||
|
|
@ -223,6 +224,7 @@ const _Result: React.ForwardRefRenderFunction<HTMLDivElement, ResultProps> = (
|
|||
</AccordionHeaderWrapper>
|
||||
<AccordionPanel
|
||||
pb={4}
|
||||
bg="white"
|
||||
overflowX="auto"
|
||||
css={{
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, RequeryB
|
|||
zIndex="1"
|
||||
variant="ghost"
|
||||
onClick={requery as Get<RequeryButtonProps, 'onClick'>}
|
||||
colorScheme="secondary"
|
||||
colorScheme="primary"
|
||||
{...rest}
|
||||
>
|
||||
<DynamicIcon icon={{ fi: 'FiRepeat' }} boxSize="16px" />
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const Option = <Opt extends SingleOption, IsMulti extends boolean>(
|
|||
display={{ base: 'flex', lg: 'inline-flex' }}
|
||||
>
|
||||
{tags.map(tag => (
|
||||
<Badge fontSize="xs" variant="subtle" key={tag} colorScheme="gray" textTransform="none">
|
||||
<Badge fontSize="xs" variant="subtle" key={tag} colorScheme="primary" textTransform="none">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import { mergeWith } from '@chakra-ui/utils';
|
|||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useCallback } from 'react';
|
||||
import {
|
||||
useColorToken,
|
||||
useColorValue,
|
||||
useMobile,
|
||||
useOpposingColor,
|
||||
useOpposingColorCallback,
|
||||
|
|
@ -30,26 +28,20 @@ export const useControlStyle = <Opt extends SingleOption, IsMulti extends boolea
|
|||
|
||||
const { isError } = useSelectContext();
|
||||
|
||||
const minHeight = useToken('space', 12);
|
||||
const borderRadius = useToken('radii', 'md');
|
||||
const color = useColorToken('colors', 'black', 'whiteAlpha.800');
|
||||
const focusBorder = useColorToken('colors', 'blue.500', 'blue.300');
|
||||
const invalidBorder = useColorToken('colors', 'red.500', 'red.300');
|
||||
// const borderColor = useColorToken('colors', 'gray.200', 'whiteAlpha.300');
|
||||
const borderColor = useColorToken('colors', 'gray.100', 'whiteAlpha.50');
|
||||
const borderHover = useColorToken('colors', 'gray.300', 'whiteAlpha.400');
|
||||
const backgroundColor = useColorToken('colors', 'white', 'blackSolid.800');
|
||||
const focusBorder = useToken('colors', 'brand.500');
|
||||
const invalidBorder = useToken('colors', 'red.500');
|
||||
const borderHover = useToken('colors', 'gray.300');
|
||||
|
||||
return useCallback(
|
||||
(base, state) => {
|
||||
const { isFocused } = state;
|
||||
const styles = {
|
||||
backgroundColor,
|
||||
borderRadius,
|
||||
color,
|
||||
minHeight,
|
||||
backgroundColor: useToken('colors', 'white'),
|
||||
borderRadius: useToken('radii', 'md'),
|
||||
color: useToken('colors', 'black'),
|
||||
minHeight: useToken('space', 12),
|
||||
transition: 'all 0.2s',
|
||||
borderColor: isError ? invalidBorder : isFocused ? focusBorder : borderColor,
|
||||
borderColor: isError ? invalidBorder : isFocused ? focusBorder : useToken('gray.100'),
|
||||
boxShadow: isError
|
||||
? `0 0 0 1px ${invalidBorder}`
|
||||
: isFocused
|
||||
|
|
@ -72,9 +64,7 @@ export const useMenuStyle = <Opt extends SingleOption, IsMulti extends boolean>(
|
|||
const { colorMode } = props;
|
||||
|
||||
const { isOpen } = useSelectContext();
|
||||
|
||||
const backgroundColor = useColorToken('colors', 'white', 'blackSolid.700');
|
||||
const styles = { backgroundColor, zIndex: 1500 };
|
||||
const styles = { backgroundColor: useToken('white'), zIndex: 1500 };
|
||||
|
||||
return useCallback(base => mergeWith({}, base, styles), [colorMode, isOpen]);
|
||||
};
|
||||
|
|
@ -86,14 +76,12 @@ export const useMenuListStyle = <Opt extends SingleOption, IsMulti extends boole
|
|||
|
||||
const { isOpen } = useSelectContext();
|
||||
|
||||
const borderRadius = useToken('radii', 'md');
|
||||
const backgroundColor = useColorToken('colors', 'white', 'blackSolid.700');
|
||||
const scrollbarTrack = useColorToken('colors', 'blackAlpha.50', 'whiteAlpha.50');
|
||||
const scrollbarThumb = useColorToken('colors', 'blackAlpha.300', 'whiteAlpha.300');
|
||||
const scrollbarThumbHover = useColorToken('colors', 'blackAlpha.400', 'whiteAlpha.400');
|
||||
const scrollbarTrack = useToken('colors', 'blackAlpha.50');
|
||||
const scrollbarThumb = useToken('colors', 'blackAlpha.300');
|
||||
const scrollbarThumbHover = useToken('colors', 'blackAlpha.400');
|
||||
const styles = {
|
||||
borderRadius,
|
||||
backgroundColor,
|
||||
borderRadius: useToken('radii', 'md'),
|
||||
backgroundColor: useToken('colors', 'white'),
|
||||
'&::-webkit-scrollbar': { width: '5px' },
|
||||
'&::-webkit-scrollbar-track': { backgroundColor: scrollbarTrack },
|
||||
'&::-webkit-scrollbar-thumb': { backgroundColor: scrollbarThumb },
|
||||
|
|
@ -113,12 +101,11 @@ export const useOptionStyle = <Opt extends SingleOption, IsMulti extends boolean
|
|||
|
||||
const fontSize = useToken('fontSizes', 'lg');
|
||||
const disabled = useToken('colors', 'whiteAlpha.400');
|
||||
const active = useColorToken('colors', 'primary.600', 'primary.400');
|
||||
const focused = useColorToken('colors', 'primary.500', 'primary.300');
|
||||
const selected = useColorToken('colors', 'blackAlpha.400', 'whiteAlpha.400');
|
||||
const active = useToken('colors', 'brand.600');
|
||||
const focused = useToken('colors', 'brand.500');
|
||||
const selected = useToken('colors', 'brand.600');
|
||||
|
||||
const activeColor = useOpposingColor(active);
|
||||
const getColor = useOpposingColorCallback();
|
||||
const color = useToken('colors', 'white');
|
||||
|
||||
return useCallback(
|
||||
(base, state) => {
|
||||
|
|
@ -136,12 +123,11 @@ export const useOptionStyle = <Opt extends SingleOption, IsMulti extends boolean
|
|||
backgroundColor = focused;
|
||||
break;
|
||||
}
|
||||
const color = getColor(backgroundColor);
|
||||
|
||||
const styles = {
|
||||
color: backgroundColor === 'transparent' ? 'currentColor' : color,
|
||||
'&:active': { backgroundColor: active, color: activeColor },
|
||||
'&:focus': { backgroundColor: active, color: activeColor },
|
||||
'&:active': { backgroundColor: active, color: color },
|
||||
'&:focus': { backgroundColor: active, color: color },
|
||||
backgroundColor,
|
||||
fontSize,
|
||||
};
|
||||
|
|
@ -156,8 +142,7 @@ export const useIndicatorSeparatorStyle = <Opt extends SingleOption, IsMulti ext
|
|||
props: RSStyleCallbackProps,
|
||||
): RSStyleFunction<'indicatorSeparator', Opt, IsMulti> => {
|
||||
const { colorMode } = props;
|
||||
const backgroundColor = useColorToken('colors', 'gray.200', 'whiteAlpha.300');
|
||||
const styles = { backgroundColor };
|
||||
const styles = { backgroundColor: useToken('colors', 'gray.200') };
|
||||
|
||||
return useCallback(base => mergeWith({}, base, styles), [colorMode]);
|
||||
};
|
||||
|
|
@ -167,10 +152,12 @@ export const usePlaceholderStyle = <Opt extends SingleOption, IsMulti extends bo
|
|||
): RSStyleFunction<'placeholder', Opt, IsMulti> => {
|
||||
const { colorMode } = props;
|
||||
|
||||
const color = useColorToken('colors', 'gray.600', 'whiteAlpha.700');
|
||||
const fontSize = useToken('fontSizes', 'lg');
|
||||
const styles = {
|
||||
color: useToken('colors', 'gray.600'),
|
||||
fontSize: useToken('fontSizes', 'lg')
|
||||
}
|
||||
|
||||
return useCallback(base => mergeWith({}, base, { color, fontSize }), [colorMode]);
|
||||
return useCallback(base => mergeWith({}, base, styles), [colorMode]);
|
||||
};
|
||||
|
||||
export const useSingleValueStyle = <Opt extends SingleOption, IsMulti extends boolean>(
|
||||
|
|
@ -178,9 +165,11 @@ export const useSingleValueStyle = <Opt extends SingleOption, IsMulti extends bo
|
|||
): RSStyleFunction<'singleValue', Opt, IsMulti> => {
|
||||
const { colorMode } = props;
|
||||
|
||||
const color = useColorValue('black', 'whiteAlpha.800');
|
||||
const fontSize = useToken('fontSizes', 'lg');
|
||||
const styles = { color, fontSize };
|
||||
const color = useToken('colors', 'black');
|
||||
const styles = {
|
||||
color,
|
||||
fontSize: useToken('fontSizes', 'lg')
|
||||
};
|
||||
|
||||
return useCallback(base => mergeWith({}, base, styles), [color, colorMode]);
|
||||
};
|
||||
|
|
@ -190,10 +179,12 @@ export const useMultiValueStyle = <Opt extends SingleOption, IsMulti extends boo
|
|||
): RSStyleFunction<'multiValue', Opt, IsMulti> => {
|
||||
const { colorMode } = props;
|
||||
|
||||
const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300');
|
||||
const color = useOpposingColor(backgroundColor);
|
||||
const borderRadius = useToken('radii', 'md');
|
||||
const styles = { backgroundColor, color, borderRadius };
|
||||
const backgroundColor = useToken('colors', 'brand.500');
|
||||
const styles = {
|
||||
backgroundColor: backgroundColor,
|
||||
color: useToken('colors', 'white'),
|
||||
borderRadius: useToken('radii', 'md')
|
||||
};
|
||||
|
||||
return useCallback(base => mergeWith({}, base, styles), [backgroundColor, colorMode]);
|
||||
};
|
||||
|
|
@ -203,9 +194,7 @@ export const useMultiValueLabelStyle = <Opt extends SingleOption, IsMulti extend
|
|||
): RSStyleFunction<'multiValueLabel', Opt, IsMulti> => {
|
||||
const { colorMode } = props;
|
||||
|
||||
const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300');
|
||||
const color = useOpposingColor(backgroundColor);
|
||||
const styles = { color };
|
||||
const styles = { color: useToken('colors', 'white') };
|
||||
|
||||
return useCallback(base => mergeWith({}, base, styles), [colorMode]);
|
||||
};
|
||||
|
|
@ -215,10 +204,9 @@ export const useMultiValueRemoveStyle = <Opt extends SingleOption, IsMulti exten
|
|||
): RSStyleFunction<'multiValueRemove', Opt, IsMulti> => {
|
||||
const { colorMode } = props;
|
||||
|
||||
const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300');
|
||||
const color = useOpposingColor(backgroundColor);
|
||||
const color = useToken('colors', 'white');
|
||||
const styles = {
|
||||
color,
|
||||
color: color,
|
||||
'&:hover': { backgroundColor: 'transparent', color, opacity: 0.8 },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { useFormContext } from 'react-hook-form';
|
|||
import { Else, If, Then } from 'react-if';
|
||||
import { ResolvedTarget } from '~/components';
|
||||
import { DynamicIcon } from '~/elements';
|
||||
import { useColorValue, useFormState, useMobile } from '~/hooks';
|
||||
import { useFormState, useMobile } from '~/hooks';
|
||||
|
||||
import type { IconButtonProps } from '@chakra-ui/react';
|
||||
|
||||
|
|
@ -42,7 +42,8 @@ const _SubmitIcon: React.ForwardRefRenderFunction<
|
|||
type="submit"
|
||||
icon={<DynamicIcon icon={{ fi: 'FiSearch' }} />}
|
||||
title="Submit Query"
|
||||
colorScheme="primary"
|
||||
variant="solid"
|
||||
colorScheme="brand"
|
||||
isLoading={isLoading}
|
||||
aria-label="Submit Query"
|
||||
{...rest}
|
||||
|
|
@ -56,7 +57,6 @@ const SubmitIcon = forwardRef<HTMLButtonElement, Omit<IconButtonProps, 'aria-lab
|
|||
*/
|
||||
const MSubmitButton = (props: ResponsiveSubmitButtonProps): JSX.Element => {
|
||||
const { children, isOpen, onClose } = props;
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
|
|
@ -70,7 +70,7 @@ const MSubmitButton = (props: ResponsiveSubmitButtonProps): JSX.Element => {
|
|||
motionPreset="slideInBottom"
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent bg={bg}>
|
||||
<ModalContent bg="white">
|
||||
<ModalCloseButton />
|
||||
<ModalBody px={4} py={10}>
|
||||
{isOpen && <ResolvedTarget errorClose={onClose} />}
|
||||
|
|
@ -86,12 +86,11 @@ const MSubmitButton = (props: ResponsiveSubmitButtonProps): JSX.Element => {
|
|||
*/
|
||||
const DSubmitButton = (props: ResponsiveSubmitButtonProps): JSX.Element => {
|
||||
const { children, isOpen, onClose } = props;
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
return (
|
||||
<Popover isOpen={isOpen} onClose={onClose} closeOnBlur={false}>
|
||||
<PopoverTrigger>{children}</PopoverTrigger>
|
||||
<PopoverContent bg={bg}>
|
||||
<PopoverArrow bg={bg} />
|
||||
<PopoverContent bg="white">
|
||||
<PopoverArrow bg="white" />
|
||||
<PopoverCloseButton />
|
||||
<PopoverBody p={6}>{isOpen && <ResolvedTarget errorClose={onClose} />}</PopoverBody>
|
||||
</PopoverContent>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export const UserIP = (props: UserIPProps): JSX.Element => {
|
|||
return (
|
||||
<Prompt
|
||||
trigger={
|
||||
<Button size="sm" onClick={handleOpen}>
|
||||
<Button size="sm" onClick={handleOpen} colorScheme="brand">
|
||||
{web.text.ipButton}
|
||||
</Button>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Flex } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/hooks';
|
||||
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
|
|
@ -9,14 +8,12 @@ interface CardBodyProps extends Omit<FlexProps, 'onClick'> {
|
|||
|
||||
export const CardBody = (props: CardBodyProps): JSX.Element => {
|
||||
const { onClick, ...rest } = props;
|
||||
const bg = useColorValue('white', 'dark.500');
|
||||
const color = useColorValue('dark.500', 'white');
|
||||
return (
|
||||
<Flex
|
||||
bg={bg}
|
||||
bg="white"
|
||||
w="100%"
|
||||
rounded="md"
|
||||
color={color}
|
||||
color="dark.500"
|
||||
onClick={onClick}
|
||||
overflow="hidden"
|
||||
borderWidth="1px"
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import { Box } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/hooks';
|
||||
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export const CodeBlock = (props: BoxProps): JSX.Element => {
|
||||
const bg = useColorValue('blackAlpha.100', 'gray.800');
|
||||
const color = useColorValue('black', 'white');
|
||||
return (
|
||||
<Box
|
||||
p={3}
|
||||
mt={5}
|
||||
bg={bg}
|
||||
bg="blackAlpha.100"
|
||||
as="pre"
|
||||
border="1px"
|
||||
rounded="md"
|
||||
color={color}
|
||||
color="black"
|
||||
fontSize="sm"
|
||||
fontFamily="mono"
|
||||
borderColor="inherit"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ const Renderer = (props: RendererProps): JSX.Element => {
|
|||
const time = [zeroPad(seconds)];
|
||||
minutes !== 0 && time.unshift(zeroPad(minutes));
|
||||
hours !== 0 && time.unshift(zeroPad(hours));
|
||||
const bg = useColorValue('black', 'white');
|
||||
return (
|
||||
<If condition={completed}>
|
||||
<Then>
|
||||
|
|
@ -28,7 +27,7 @@ const Renderer = (props: RendererProps): JSX.Element => {
|
|||
<Else>
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
{text}
|
||||
<chakra.span fontSize="xs" color={bg}>
|
||||
<chakra.span fontSize="xs">
|
||||
{time.join(':')}
|
||||
</chakra.span>
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import fs from 'fs';
|
||||
import { ColorModeScript } from '@chakra-ui/react';
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { CustomHtml, CustomJavascript, Favicon } from '~/elements';
|
||||
import { googleFontUrl } from '~/util';
|
||||
|
|
@ -42,7 +41,7 @@ class MyDocument extends Document<DocumentExtra> {
|
|||
// } = await getHyperglassConfig(hyperglassUrl);
|
||||
|
||||
fonts = {
|
||||
body: googleFontUrl(config.web.theme.fonts.body),
|
||||
body: "https://assets.witine.com/fonts/inter.css",
|
||||
mono: googleFontUrl(config.web.theme.fonts.mono),
|
||||
};
|
||||
defaultColorMode = config.web.theme.defaultColorMode;
|
||||
|
|
@ -61,9 +60,6 @@ class MyDocument extends Document<DocumentExtra> {
|
|||
<meta name="og:image" content="/images/opengraph.jpg" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<link rel="dns-prefetch" href="//fonts.gstatic.com" />
|
||||
<link rel="dns-prefetch" href="//fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
|
||||
<link href={this.props.fonts.mono} rel="stylesheet" />
|
||||
<link href={this.props.fonts.body} rel="stylesheet" />
|
||||
{favicons.map(favicon => (
|
||||
|
|
@ -72,7 +68,6 @@ class MyDocument extends Document<DocumentExtra> {
|
|||
<CustomJavascript>{this.props.customJs}</CustomJavascript>
|
||||
</Head>
|
||||
<body>
|
||||
<ColorModeScript initialColorMode={this.props.defaultColorMode ?? 'system'} />
|
||||
<Main />
|
||||
<CustomHtml>{this.props.customHtml}</CustomHtml>
|
||||
<NextScript />
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ interface _Logo {
|
|||
height: string | null;
|
||||
light_format: string;
|
||||
dark_format: string;
|
||||
footer_format: string;
|
||||
}
|
||||
|
||||
interface _Link {
|
||||
|
|
@ -87,6 +88,7 @@ interface _Highlight {
|
|||
}
|
||||
|
||||
interface _Web {
|
||||
copyright: string;
|
||||
credit: _Credit;
|
||||
dns_provider: { name: string; url: string };
|
||||
links: _Link[];
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ function importFonts(userFonts: Theme.Fonts): ChakraTheme['fonts'] {
|
|||
|
||||
function importColors(userColors: ThemeConfig['colors']): Theme.Colors {
|
||||
const initial: Pick<Theme.Colors, 'blackSolid' | 'whiteSolid' | 'transparent' | 'current'> = {
|
||||
brand: generatePalette('#00BCA9'),
|
||||
blackSolid: {
|
||||
50: '#444444',
|
||||
100: '#3c3c3c',
|
||||
|
|
@ -92,24 +93,14 @@ export function makeTheme(
|
|||
colors,
|
||||
config,
|
||||
fontWeights,
|
||||
semanticTokens: {
|
||||
colors: {
|
||||
'body-bg': {
|
||||
default: 'light.500',
|
||||
_dark: 'dark.500',
|
||||
},
|
||||
'body-fg': {
|
||||
default: 'dark.500',
|
||||
_dark: 'light.500',
|
||||
},
|
||||
},
|
||||
},
|
||||
styles: {
|
||||
global: {
|
||||
html: { scrollBehavior: 'smooth', height: '-webkit-fill-available' },
|
||||
body: {
|
||||
background: 'body-bg',
|
||||
color: 'body-fg',
|
||||
background: 'repeating-linear-gradient(225deg,#f5fffe,#effdff,#eff9ff,#f5f4ff,#ffeefe,#ffeefe)',
|
||||
color: '#2D3635',
|
||||
fontSize: '1.05rem',
|
||||
lineHeight: '1.5',
|
||||
overflowX: 'hidden',
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue