forked from mirrors/thatmattlove-hyperglass
refactor, restructure typescript types
This commit is contained in:
parent
47711fb075
commit
93adc09e63
95 changed files with 634 additions and 767 deletions
|
|
@ -1,9 +1,13 @@
|
|||
import { Flex } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { TCardBody } from './types';
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export const CardBody = (props: TCardBody): JSX.Element => {
|
||||
interface CardBodyProps extends Omit<FlexProps, 'onClick'> {
|
||||
onClick?: () => boolean;
|
||||
}
|
||||
|
||||
export const CardBody = (props: CardBodyProps): JSX.Element => {
|
||||
const { onClick, ...rest } = props;
|
||||
const bg = useColorValue('white', 'dark.500');
|
||||
const color = useColorValue('dark.500', 'white');
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
import type { TCardFooter } from './types';
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export const CardFooter = (props: TCardFooter): JSX.Element => (
|
||||
export const CardFooter = (props: FlexProps): JSX.Element => (
|
||||
<Flex
|
||||
p={4}
|
||||
direction="column"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { TCardHeader } from './types';
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export const CardHeader = (props: TCardHeader): JSX.Element => {
|
||||
export const CardHeader = (props: FlexProps): JSX.Element => {
|
||||
const { children, ...rest } = props;
|
||||
const bg = useColorValue('blackAlpha.50', 'whiteAlpha.100');
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export interface TCardBody extends Omit<FlexProps, 'onClick'> {
|
||||
onClick?: () => boolean;
|
||||
}
|
||||
|
||||
export interface TCardFooter extends FlexProps {}
|
||||
|
||||
export interface TCardHeader extends FlexProps {}
|
||||
|
|
@ -3,9 +3,18 @@ import ReactCountdown, { zeroPad } from 'react-countdown';
|
|||
import { If, Then, Else } from 'react-if';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { TCountdown, TRenderer } from './types';
|
||||
import type { CountdownRenderProps } from 'react-countdown';
|
||||
|
||||
const Renderer = (props: TRenderer): JSX.Element => {
|
||||
interface RendererProps extends CountdownRenderProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface CountdownProps {
|
||||
timeout: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const Renderer = (props: RendererProps): JSX.Element => {
|
||||
const { hours, minutes, seconds, completed, text } = props;
|
||||
const time = [zeroPad(seconds)];
|
||||
minutes !== 0 && time.unshift(zeroPad(minutes));
|
||||
|
|
@ -28,7 +37,7 @@ const Renderer = (props: TRenderer): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
export const Countdown = (props: TCountdown): JSX.Element => {
|
||||
export const Countdown = (props: CountdownProps): JSX.Element => {
|
||||
const { timeout, text } = props;
|
||||
const then = timeout * 1000;
|
||||
return (
|
||||
|
|
@ -1 +0,0 @@
|
|||
export * from './countdown';
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import type { CountdownRenderProps } from 'react-countdown';
|
||||
|
||||
export interface TRenderer extends CountdownRenderProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface TCountdown {
|
||||
timeout: number;
|
||||
text: string;
|
||||
}
|
||||
|
|
@ -4,8 +4,14 @@ import { Markdown } from '~/components';
|
|||
import { useColorValue, useBreakpointValue, useConfig } from '~/context';
|
||||
import { useOpposingColor, useStrf } from '~/hooks';
|
||||
|
||||
import type { MenuListProps } from '@chakra-ui/react';
|
||||
import type { Config } from '~/types';
|
||||
import type { TFooterButton } from './types';
|
||||
|
||||
interface FooterButtonProps extends Omit<MenuListProps, 'title'> {
|
||||
side: 'left' | 'right';
|
||||
title?: MenuListProps['children'];
|
||||
content: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the configuration object based on values that are strings for formatting.
|
||||
|
|
@ -20,7 +26,7 @@ function getConfigFmt(config: Config): Record<string, string> {
|
|||
return fmt;
|
||||
}
|
||||
|
||||
export const FooterButton = (props: TFooterButton): JSX.Element => {
|
||||
export const FooterButton = (props: FooterButtonProps): JSX.Element => {
|
||||
const { content, title, side, ...rest } = props;
|
||||
|
||||
const config = useConfig();
|
||||
|
|
|
|||
|
|
@ -5,10 +5,14 @@ import { DynamicIcon } from '~/components';
|
|||
import { useColorMode, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TColorModeToggle } from './types';
|
||||
import type { ButtonProps } from '@chakra-ui/react';
|
||||
|
||||
export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>(
|
||||
(props: TColorModeToggle, ref) => {
|
||||
interface ColorModeToggleProps extends ButtonProps {
|
||||
size?: string;
|
||||
}
|
||||
|
||||
export const ColorModeToggle = forwardRef<HTMLButtonElement, ColorModeToggleProps>(
|
||||
(props: ColorModeToggleProps, ref) => {
|
||||
const { size = '1.5rem', ...rest } = props;
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
|
||||
|
|
@ -5,9 +5,9 @@ import { DynamicIcon } from '~/components';
|
|||
import { useConfig, useMobile, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useStrf } from '~/hooks';
|
||||
import { FooterButton } from './button';
|
||||
import { ColorModeToggle } from './colorMode';
|
||||
import { ColorModeToggle } from './color-mode';
|
||||
import { FooterLink } from './link';
|
||||
import { isLink, isMenu } from './types';
|
||||
import { isLink, isMenu } from '~/types';
|
||||
|
||||
import type { ButtonProps, LinkProps } from '@chakra-ui/react';
|
||||
import type { Link, Menu } from '~/types';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { Button, Link, useBreakpointValue } from '@chakra-ui/react';
|
||||
|
||||
import type { TFooterLink } from './types';
|
||||
import type { ButtonProps, LinkProps } from '@chakra-ui/react';
|
||||
|
||||
export const FooterLink = (props: TFooterLink): JSX.Element => {
|
||||
type FooterLinkProps = ButtonProps & LinkProps & { title: string };
|
||||
|
||||
export const FooterLink = (props: FooterLinkProps): JSX.Element => {
|
||||
const { title } = props;
|
||||
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import type { ButtonProps, LinkProps, MenuListProps } from '@chakra-ui/react';
|
||||
import type { Link, Menu } from '~/types';
|
||||
|
||||
type TFooterSide = 'left' | 'right';
|
||||
|
||||
export interface TFooterButton extends Omit<MenuListProps, 'title'> {
|
||||
side: TFooterSide;
|
||||
title?: MenuListProps['children'];
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type TFooterLink = ButtonProps & LinkProps & { title: string };
|
||||
|
||||
export type TFooterItems = 'help' | 'credit' | 'terms';
|
||||
|
||||
export interface TColorModeToggle extends ButtonProps {
|
||||
size?: string;
|
||||
}
|
||||
|
||||
export function isLink(item: Link | Menu): item is Link {
|
||||
return 'url' in item;
|
||||
}
|
||||
|
||||
export function isMenu(item: Link | Menu): item is Menu {
|
||||
return 'content' in item;
|
||||
}
|
||||
|
|
@ -5,11 +5,19 @@ import { If, Then } from 'react-if';
|
|||
import { useColorValue } from '~/context';
|
||||
import { useBooleanValue } from '~/hooks';
|
||||
|
||||
import type { FormControlProps } from '@chakra-ui/react';
|
||||
import type { FieldError } from 'react-hook-form';
|
||||
import type { FormData } from '~/types';
|
||||
import type { TField } from './types';
|
||||
|
||||
export const FormField = (props: TField): JSX.Element => {
|
||||
interface FormFieldProps extends FormControlProps {
|
||||
name: string;
|
||||
label: string;
|
||||
hiddenLabels?: boolean;
|
||||
labelAddOn?: React.ReactNode;
|
||||
fieldAddOn?: React.ReactNode;
|
||||
}
|
||||
|
||||
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');
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export * from './row';
|
||||
export * from './field';
|
||||
export * from './queryType';
|
||||
export * from './queryTarget';
|
||||
export * from './queryLocation';
|
||||
export * from './resolvedTarget';
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
import type { FormControlProps } from '@chakra-ui/react';
|
||||
import type { UseFormRegister } from 'react-hook-form';
|
||||
import type { OnChangeArgs, FormData, SingleOption } from '~/types';
|
||||
|
||||
export interface TField extends FormControlProps {
|
||||
name: string;
|
||||
label: string;
|
||||
hiddenLabels?: boolean;
|
||||
labelAddOn?: React.ReactNode;
|
||||
fieldAddOn?: React.ReactNode;
|
||||
}
|
||||
|
||||
export type OnChange = (f: OnChangeArgs) => void;
|
||||
|
||||
export interface TQuerySelectField {
|
||||
onChange: OnChange;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface TQueryTarget {
|
||||
name: string;
|
||||
placeholder: string;
|
||||
register: UseFormRegister<FormData>;
|
||||
onChange(e: OnChangeArgs): void;
|
||||
}
|
||||
|
||||
export interface ResolvedTargetProps {
|
||||
errorClose(): void;
|
||||
}
|
||||
|
||||
export interface LocationCardProps {
|
||||
option: SingleOption;
|
||||
defaultChecked: boolean;
|
||||
onChange(a: 'add' | 'remove', v: SingleOption): void;
|
||||
hasError: boolean;
|
||||
}
|
||||
|
||||
export interface UserIPProps {
|
||||
setTarget(target: string): void;
|
||||
}
|
||||
|
|
@ -14,9 +14,9 @@ import { Markdown } from '~/components';
|
|||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useGreeting, useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TGreeting } from './types';
|
||||
import type { ModalContentProps } from '@chakra-ui/react';
|
||||
|
||||
export const Greeting = (props: TGreeting): JSX.Element => {
|
||||
export const Greeting = (props: ModalContentProps): JSX.Element => {
|
||||
const { web, content } = useConfig();
|
||||
const { isAck, isOpen, open, ack } = useGreeting();
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
export * from './greeting';
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export interface TGreeting extends BoxProps {}
|
||||
|
|
@ -2,7 +2,7 @@ import { useCallback, useMemo, useState } from 'react';
|
|||
import { Image, Skeleton } from '@chakra-ui/react';
|
||||
import { useColorValue, useConfig } from '~/context';
|
||||
|
||||
import type { TLogo } from './types';
|
||||
import type { ImageProps } from '@chakra-ui/react';
|
||||
|
||||
/**
|
||||
* Custom hook to handle loading the user's logo, errors loading the logo, and color mode changes.
|
||||
|
|
@ -31,7 +31,7 @@ function useLogo(): [string, () => void] {
|
|||
return useMemo(() => [fallback ?? src, setFallback], [fallback, setFallback, src]);
|
||||
}
|
||||
|
||||
export const Logo = (props: TLogo): JSX.Element => {
|
||||
export const Logo = (props: ImageProps): JSX.Element => {
|
||||
const { web } = useConfig();
|
||||
const { width } = web.logo;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,16 @@ import { isSafari } from 'react-device-detect';
|
|||
import { Switch, Case } from 'react-if';
|
||||
import { useConfig, useMobile } from '~/context';
|
||||
import { useFormState, useFormInteractive } from '~/hooks';
|
||||
import { SubtitleOnly } from './subtitleOnly';
|
||||
import { TitleOnly } from './titleOnly';
|
||||
import { Logo } from './logo';
|
||||
import { SubtitleOnly } from './subtitle-only';
|
||||
import { TitleOnly } from './title-only';
|
||||
|
||||
import type { TTitle, TTitleWrapper, TDWrapper, TMWrapper } from './types';
|
||||
import type { FlexProps, StackProps } from '@chakra-ui/react';
|
||||
import type { MotionProps } from 'framer-motion';
|
||||
|
||||
type DWrapperProps = Omit<StackProps, 'transition'> & MotionProps;
|
||||
type MWrapperProps = Omit<StackProps, 'transition'> & MotionProps;
|
||||
type TextOnlyProps = Partial<MotionProps & Omit<StackProps, 'transition'>>;
|
||||
|
||||
const AnimatedVStack = motion(VStack);
|
||||
const AnimatedFlex = motion(Flex);
|
||||
|
|
@ -16,7 +21,7 @@ const AnimatedFlex = motion(Flex);
|
|||
/**
|
||||
* Title wrapper for mobile devices, breakpoints sm & md.
|
||||
*/
|
||||
const MWrapper = (props: TMWrapper): JSX.Element => {
|
||||
const MWrapper = (props: MWrapperProps): JSX.Element => {
|
||||
const formInteractive = useFormInteractive();
|
||||
return (
|
||||
<AnimatedVStack
|
||||
|
|
@ -31,7 +36,7 @@ const MWrapper = (props: TMWrapper): JSX.Element => {
|
|||
/**
|
||||
* Title wrapper for desktop devices, breakpoints lg & xl.
|
||||
*/
|
||||
const DWrapper = (props: TDWrapper): JSX.Element => {
|
||||
const DWrapper = (props: DWrapperProps): JSX.Element => {
|
||||
const formInteractive = useFormInteractive();
|
||||
return (
|
||||
<AnimatedVStack
|
||||
|
|
@ -50,11 +55,15 @@ const DWrapper = (props: TDWrapper): JSX.Element => {
|
|||
* Universal wrapper for title sub-components, which will be different depending on the
|
||||
* `title_mode` configuration variable.
|
||||
*/
|
||||
const TitleWrapper = (props: TDWrapper | TMWrapper): JSX.Element => {
|
||||
const TitleWrapper = (props: DWrapperProps | MWrapperProps): JSX.Element => {
|
||||
const isMobile = useMobile();
|
||||
return (
|
||||
<>
|
||||
{isMobile ? <MWrapper {...(props as TMWrapper)} /> : <DWrapper {...(props as TDWrapper)} />}
|
||||
{isMobile ? (
|
||||
<MWrapper {...(props as MWrapperProps)} />
|
||||
) : (
|
||||
<DWrapper {...(props as DWrapperProps)} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -62,7 +71,7 @@ const TitleWrapper = (props: TDWrapper | TMWrapper): JSX.Element => {
|
|||
/**
|
||||
* Title sub-component if `title_mode` is set to `text_only`.
|
||||
*/
|
||||
const TextOnly = (props: TTitleWrapper): JSX.Element => {
|
||||
const TextOnly = (props: TextOnlyProps): JSX.Element => {
|
||||
return (
|
||||
<TitleWrapper {...props}>
|
||||
<TitleOnly />
|
||||
|
|
@ -104,7 +113,7 @@ const All = (): JSX.Element => (
|
|||
/**
|
||||
* Title component which renders sub-components based on the `title_mode` configuration variable.
|
||||
*/
|
||||
export const Title = (props: TTitle): JSX.Element => {
|
||||
export const Title = (props: FlexProps): JSX.Element => {
|
||||
const { fontSize, ...rest } = props;
|
||||
const { web } = useConfig();
|
||||
const { titleMode } = web.text;
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
import type { FlexProps, HeadingProps, ImageProps, StackProps } from '@chakra-ui/react';
|
||||
import type { MotionProps } from 'framer-motion';
|
||||
|
||||
export interface THeader extends FlexProps {
|
||||
resetForm(): void;
|
||||
}
|
||||
|
||||
export type THeaderLayout = {
|
||||
sm: [JSX.Element, JSX.Element];
|
||||
md: [JSX.Element, JSX.Element];
|
||||
lg: [JSX.Element, JSX.Element];
|
||||
xl: [JSX.Element, JSX.Element];
|
||||
};
|
||||
export type TDWrapper = Omit<StackProps, 'transition'> & MotionProps;
|
||||
|
||||
export type TMWrapper = Omit<StackProps, 'transition'> & MotionProps;
|
||||
|
||||
export interface TTitle extends FlexProps {}
|
||||
|
||||
export interface TTitleOnly extends HeadingProps {}
|
||||
|
||||
export interface TLogo extends ImageProps {}
|
||||
|
||||
export interface TTitleWrapper extends Partial<MotionProps & Omit<StackProps, 'transition'>> {}
|
||||
|
||||
export interface THeaderCtx {
|
||||
showSubtitle: boolean;
|
||||
titleRef: React.MutableRefObject<HTMLHeadingElement>;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
export * from './animated';
|
||||
export * from './card';
|
||||
export * from './codeBlock';
|
||||
export * from './code-block';
|
||||
export * from './countdown';
|
||||
export * from './custom';
|
||||
export * from './debugger';
|
||||
|
|
@ -8,20 +8,27 @@ export * from './directive-info-modal';
|
|||
export * from './dynamic-icon';
|
||||
export * from './favicon';
|
||||
export * from './footer';
|
||||
export * from './form';
|
||||
export * from './form-field';
|
||||
export * from './form-row';
|
||||
export * from './greeting';
|
||||
export * from './header';
|
||||
export * from './label';
|
||||
export * from './layout';
|
||||
export * from './loadError';
|
||||
export * from './load-error';
|
||||
export * from './loading';
|
||||
export * from './lookingGlass';
|
||||
export * from './location-card';
|
||||
export * from './looking-glass-form';
|
||||
export * from './markdown';
|
||||
export * from './meta';
|
||||
export * from './output';
|
||||
export * from './path';
|
||||
export * from './prompt';
|
||||
export * from './query-location';
|
||||
export * from './query-target';
|
||||
export * from './query-type';
|
||||
export * from './resolved-target';
|
||||
export * from './results';
|
||||
export * from './select';
|
||||
export * from './submit';
|
||||
export * from './submit-button';
|
||||
export * from './table';
|
||||
export * from './user-ip';
|
||||
|
|
|
|||
|
|
@ -3,9 +3,20 @@ import { Flex } from '@chakra-ui/react';
|
|||
import { useColorValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TLabel } from './types';
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
const _Label: React.ForwardRefRenderFunction<HTMLDivElement, TLabel> = (props: TLabel, ref) => {
|
||||
interface LabelProps extends FlexProps {
|
||||
value: string;
|
||||
label: string;
|
||||
bg: string;
|
||||
valueColor?: string;
|
||||
labelColor?: string;
|
||||
}
|
||||
|
||||
const _Label: React.ForwardRefRenderFunction<HTMLDivElement, LabelProps> = (
|
||||
props: LabelProps,
|
||||
ref,
|
||||
) => {
|
||||
const { value, label, labelColor, bg = 'primary.600', valueColor, ...rest } = props;
|
||||
|
||||
const valueColorAuto = useOpposingColor(bg);
|
||||
|
|
@ -1 +0,0 @@
|
|||
export * from './label';
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export interface TLabel extends FlexProps {
|
||||
value: string;
|
||||
label: string;
|
||||
bg: string;
|
||||
valueColor?: string;
|
||||
labelColor?: string;
|
||||
}
|
||||
|
|
@ -6,13 +6,13 @@ import { If, Then } from 'react-if';
|
|||
import { Debugger, Greeting, Footer, Header } from '~/components';
|
||||
import { useConfig } from '~/context';
|
||||
import { useFormState } from '~/hooks';
|
||||
import { ResetButton } from './resetButton';
|
||||
import { ResetButton } from './reset-button';
|
||||
|
||||
import type { TFrame } from './types';
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
const AnimatedFlex = motion(Flex);
|
||||
|
||||
export const Frame = (props: TFrame): JSX.Element => {
|
||||
export const Frame = (props: FlexProps): JSX.Element => {
|
||||
const { developerMode } = useConfig();
|
||||
const { setStatus, reset } = useFormState(
|
||||
useCallback(({ setStatus, reset }) => ({ setStatus, reset }), []),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { AnimatePresence } from 'framer-motion';
|
||||
import { LookingGlass, Results } from '~/components';
|
||||
import { LookingGlassForm, Results } from '~/components';
|
||||
import { useView } from '~/hooks';
|
||||
import { Frame } from './frame';
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ export const Layout = (): JSX.Element => {
|
|||
<Results />
|
||||
) : (
|
||||
<AnimatePresence>
|
||||
<LookingGlass />
|
||||
<LookingGlassForm />
|
||||
</AnimatePresence>
|
||||
)}
|
||||
</Frame>
|
||||
|
|
|
|||
|
|
@ -4,9 +4,14 @@ import { AnimatedDiv, DynamicIcon } from '~/components';
|
|||
import { useColorValue } from '~/context';
|
||||
import { useOpposingColor, useFormState } from '~/hooks';
|
||||
|
||||
import type { TResetButton } from './types';
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export const ResetButton = (props: TResetButton): JSX.Element => {
|
||||
interface ResetButtonProps extends FlexProps {
|
||||
developerMode: boolean;
|
||||
resetForm(): void;
|
||||
}
|
||||
|
||||
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');
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export interface TFrame extends FlexProps {}
|
||||
|
||||
export interface TResetButton extends FlexProps {
|
||||
developerMode: boolean;
|
||||
resetForm(): void;
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import {
|
|||
AlertTitle,
|
||||
AlertDescription,
|
||||
} from '@chakra-ui/react';
|
||||
import { NoConfig } from './noconfig';
|
||||
import { NoConfig } from './no-config';
|
||||
|
||||
import type { CenterProps } from '@chakra-ui/react';
|
||||
import type { ConfigLoadError } from '~/util';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Spinner } from '@chakra-ui/react';
|
||||
import { NoConfig } from './noconfig';
|
||||
import { NoConfig } from './no-config';
|
||||
|
||||
export const Loading = (): JSX.Element => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@ import { motion } from 'framer-motion';
|
|||
import { useColorValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { LocationOption } from './queryLocation';
|
||||
import type { LocationCardProps } from './types';
|
||||
import type { SingleOption } from '~/types';
|
||||
import type { LocationOption } from './query-location';
|
||||
|
||||
interface LocationCardProps {
|
||||
option: SingleOption;
|
||||
defaultChecked: boolean;
|
||||
onChange(a: 'add' | 'remove', v: SingleOption): void;
|
||||
hasError: boolean;
|
||||
}
|
||||
|
||||
const MotionBox = motion(Box);
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ const fqdnPattern = new RegExp(
|
|||
/^(?!:\/\/)([a-zA-Z0-9-]+\.)?[a-zA-Z0-9-][a-zA-Z0-9-]+\.[a-zA-Z-]{2,6}?$/im,
|
||||
);
|
||||
|
||||
export const LookingGlass = (): JSX.Element => {
|
||||
export const LookingGlassForm = (): JSX.Element => {
|
||||
const { web, messages } = useConfig();
|
||||
|
||||
const greetingReady = useGreeting(s => s.greetingReady);
|
||||
|
|
@ -23,19 +23,46 @@ import { useColorValue } from '~/context';
|
|||
|
||||
import type {
|
||||
BoxProps,
|
||||
TextProps,
|
||||
CodeProps,
|
||||
LinkProps,
|
||||
TextProps,
|
||||
TableProps,
|
||||
ChakraProps,
|
||||
HeadingProps,
|
||||
DividerProps,
|
||||
TableRowProps,
|
||||
TableBodyProps,
|
||||
TableCellProps,
|
||||
TableHeadProps,
|
||||
ListProps as ChakraListProps,
|
||||
HeadingProps as ChakraHeadingProps,
|
||||
CheckboxProps as ChakraCheckboxProps,
|
||||
TableCellProps as ChakraTableCellProps,
|
||||
} from '@chakra-ui/react';
|
||||
import type { TCheckbox, TList, THeading, TCodeBlock, TTableData, TListItem } from './types';
|
||||
|
||||
interface CheckboxProps extends ChakraCheckboxProps {
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
interface ListItemProps {
|
||||
checked: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
interface ListProps extends ChakraListProps {
|
||||
ordered: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
interface HeadingProps extends ChakraHeadingProps {
|
||||
level: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
}
|
||||
|
||||
interface CodeBlockProps {
|
||||
value: React.ReactNode;
|
||||
}
|
||||
|
||||
interface TableCellProps extends BoxProps {
|
||||
isHeader: boolean;
|
||||
}
|
||||
|
||||
type MDProps = {
|
||||
node: Dict;
|
||||
|
|
@ -55,12 +82,12 @@ function clean<P extends ChakraProps>(props: P): P {
|
|||
return props;
|
||||
}
|
||||
|
||||
export const Checkbox = (props: TCheckbox & MDProps): JSX.Element => {
|
||||
export const Checkbox = (props: CheckboxProps & MDProps): JSX.Element => {
|
||||
const { checked, node, ...rest } = props;
|
||||
return <ChakraCheckbox isChecked={checked} {...rest} />;
|
||||
};
|
||||
|
||||
export const List = (props: TList): JSX.Element => {
|
||||
export const List = (props: ListProps): JSX.Element => {
|
||||
const { ordered, ...rest } = props;
|
||||
return (
|
||||
<If condition={ordered}>
|
||||
|
|
@ -74,7 +101,7 @@ export const List = (props: TList): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
export const ListItem = (props: TListItem & MDProps): JSX.Element => {
|
||||
export const ListItem = (props: ListItemProps & MDProps): JSX.Element => {
|
||||
const { checked, node, ...rest } = props;
|
||||
return checked ? (
|
||||
<Checkbox checked={checked} node={node} {...rest} />
|
||||
|
|
@ -83,7 +110,7 @@ export const ListItem = (props: TListItem & MDProps): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
export const Heading = (props: THeading): JSX.Element => {
|
||||
export const Heading = (props: HeadingProps): JSX.Element => {
|
||||
const { level, ...rest } = props;
|
||||
|
||||
const levelMap = {
|
||||
|
|
@ -93,9 +120,9 @@ export const Heading = (props: THeading): JSX.Element => {
|
|||
4: { as: 'h4', size: 'md', fontWeight: 'normal' },
|
||||
5: { as: 'h5', size: 'md', fontWeight: 'bold' },
|
||||
6: { as: 'h6', size: 'sm', fontWeight: 'bold' },
|
||||
} as { [i: number]: HeadingProps };
|
||||
} as { [i: number]: ChakraHeadingProps };
|
||||
|
||||
return <ChakraHeading {...levelMap[level]} {...clean<Omit<THeading, 'level'>>(rest)} />;
|
||||
return <ChakraHeading {...levelMap[level]} {...clean<Omit<HeadingProps, 'level'>>(rest)} />;
|
||||
};
|
||||
|
||||
export const Link = (props: LinkProps): JSX.Element => {
|
||||
|
|
@ -103,7 +130,7 @@ export const Link = (props: LinkProps): JSX.Element => {
|
|||
return <ChakraLink isExternal color={color} {...clean<LinkProps>(props)} />;
|
||||
};
|
||||
|
||||
export const CodeBlock = (props: TCodeBlock): JSX.Element => (
|
||||
export const CodeBlock = (props: CodeBlockProps): JSX.Element => (
|
||||
<CustomCodeBlock>{props.value}</CustomCodeBlock>
|
||||
);
|
||||
|
||||
|
|
@ -142,15 +169,15 @@ export const TableHead = (props: TableHeadProps): JSX.Element => (
|
|||
<Thead {...clean<TableHeadProps>(props)} />
|
||||
);
|
||||
|
||||
export const TableCell = (props: TTableData): JSX.Element => {
|
||||
export const TableCell = (props: TableCellProps): JSX.Element => {
|
||||
const { isHeader, ...rest } = props;
|
||||
return (
|
||||
<If condition={isHeader}>
|
||||
<Then>
|
||||
<Th {...clean<TableCellProps>(rest)} />
|
||||
<Th {...clean<ChakraTableCellProps>(rest)} />
|
||||
</Then>
|
||||
<Else>
|
||||
<Td {...clean<TableCellProps>(rest)} />
|
||||
<Td {...clean<ChakraTableCellProps>(rest)} />
|
||||
</Else>
|
||||
</If>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ import {
|
|||
} from './elements';
|
||||
|
||||
import type { ReactMarkdownProps } from 'react-markdown';
|
||||
import type { TMarkdown } from './types';
|
||||
|
||||
interface MarkdownProps {
|
||||
content: string;
|
||||
}
|
||||
|
||||
const renderers = {
|
||||
break: Br,
|
||||
|
|
@ -37,6 +40,6 @@ const renderers = {
|
|||
thematicBreak: Divider,
|
||||
} as ReactMarkdownProps['renderers'];
|
||||
|
||||
export const Markdown = (props: TMarkdown): JSX.Element => (
|
||||
export const Markdown = (props: MarkdownProps): JSX.Element => (
|
||||
<ReactMarkdown plugins={[gfm]} renderers={renderers} source={props.content} />
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
import type { BoxProps, CheckboxProps, HeadingProps, ListProps } from '@chakra-ui/react';
|
||||
|
||||
export interface TMarkdown {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface TCheckbox extends CheckboxProps {
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
export interface TListItem {
|
||||
checked: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface TList extends ListProps {
|
||||
ordered: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface THeading extends HeadingProps {
|
||||
level: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
}
|
||||
|
||||
export interface TCodeBlock {
|
||||
value: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface TTableData extends BoxProps {
|
||||
isHeader: boolean;
|
||||
}
|
||||
|
|
@ -3,10 +3,12 @@ import { Table } from '~/components';
|
|||
import { useConfig } from '~/context';
|
||||
import { Cell } from './cell';
|
||||
|
||||
import type { TColumn, ParsedDataField, TCellRender } from '~/types';
|
||||
import type { TBGPTable } from './types';
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
import type { TableColumn, ParsedDataField, CellRenderProps } from '~/types';
|
||||
|
||||
function makeColumns(fields: ParsedDataField[]): TColumn[] {
|
||||
type BGPTableProps = Swap<FlexProps, 'children', StructuredResponse>;
|
||||
|
||||
function makeColumns(fields: ParsedDataField[]): TableColumn[] {
|
||||
return fields.map(pair => {
|
||||
const [header, accessor, align] = pair;
|
||||
|
||||
|
|
@ -15,7 +17,7 @@ function makeColumns(fields: ParsedDataField[]): TColumn[] {
|
|||
accessor,
|
||||
hidden: false,
|
||||
Header: header,
|
||||
} as TColumn;
|
||||
} as TableColumn;
|
||||
|
||||
if (align === null) {
|
||||
columnConfig.hidden = true;
|
||||
|
|
@ -25,7 +27,7 @@ function makeColumns(fields: ParsedDataField[]): TColumn[] {
|
|||
});
|
||||
}
|
||||
|
||||
export const BGPTable = (props: TBGPTable): JSX.Element => {
|
||||
export const BGPTable = (props: BGPTableProps): JSX.Element => {
|
||||
const { children: data, ...rest } = props;
|
||||
const { parsedDataFields } = useConfig();
|
||||
const columns = makeColumns(parsedDataFields);
|
||||
|
|
@ -38,7 +40,7 @@ export const BGPTable = (props: TBGPTable): JSX.Element => {
|
|||
data={data.routes}
|
||||
rowHighlightBg="green"
|
||||
rowHighlightProp="active"
|
||||
Cell={(d: TCellRender) => <Cell data={d} rawData={data} />}
|
||||
Cell={(d: CellRenderProps) => <Cell data={d} rawData={data} />}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
import { MonoField, Active, Weight, Age, Communities, RPKIState, ASPath } from './fields';
|
||||
|
||||
import type { TCell } from './types';
|
||||
import type { CellRenderProps } from '~/types';
|
||||
|
||||
export const Cell = (props: TCell): JSX.Element => {
|
||||
interface CellProps {
|
||||
data: CellRenderProps;
|
||||
rawData: StructuredResponse;
|
||||
}
|
||||
|
||||
export const Cell = (props: CellProps): JSX.Element => {
|
||||
const { data, rawData } = props;
|
||||
const cellId = data.column.id as keyof Route;
|
||||
const component = {
|
||||
|
|
|
|||
|
|
@ -8,20 +8,47 @@ import { DynamicIcon } from '~/components';
|
|||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type {
|
||||
TAge,
|
||||
TActive,
|
||||
TWeight,
|
||||
TASPath,
|
||||
TMonoField,
|
||||
TRPKIState,
|
||||
TCommunities,
|
||||
} from './types';
|
||||
import type { TextProps } from '@chakra-ui/react';
|
||||
|
||||
interface ActiveProps {
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
interface MonoFieldProps extends TextProps {
|
||||
v: React.ReactNode;
|
||||
}
|
||||
|
||||
interface AgeProps extends TextProps {
|
||||
inSeconds: number;
|
||||
}
|
||||
|
||||
interface WeightProps extends TextProps {
|
||||
weight: number;
|
||||
winningWeight: 'low' | 'high';
|
||||
}
|
||||
|
||||
interface ASPathProps {
|
||||
path: number[];
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
interface CommunitiesProps {
|
||||
communities: string[];
|
||||
}
|
||||
|
||||
interface RPKIStateProps {
|
||||
state:
|
||||
| 0 // Invalid
|
||||
| 1 // Valid
|
||||
| 2 // Unknown
|
||||
| 3; // Unverified
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
dayjs.extend(relativeTimePlugin);
|
||||
dayjs.extend(utcPlugin);
|
||||
|
||||
export const MonoField = (props: TMonoField): JSX.Element => {
|
||||
export const MonoField = (props: MonoFieldProps): JSX.Element => {
|
||||
const { v, ...rest } = props;
|
||||
return (
|
||||
<Text as="span" fontSize="sm" fontFamily="mono" {...rest}>
|
||||
|
|
@ -30,7 +57,7 @@ export const MonoField = (props: TMonoField): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
export const Active = (props: TActive): JSX.Element => {
|
||||
export const Active = (props: ActiveProps): JSX.Element => {
|
||||
const { isActive } = props;
|
||||
const color = useColorValue(['gray.500', 'green.500'], ['whiteAlpha.300', 'blackAlpha.500']);
|
||||
return (
|
||||
|
|
@ -45,7 +72,7 @@ export const Active = (props: TActive): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
export const Age = (props: TAge): JSX.Element => {
|
||||
export const Age = (props: AgeProps): JSX.Element => {
|
||||
const { inSeconds, ...rest } = props;
|
||||
const now = dayjs.utc();
|
||||
const then = now.subtract(inSeconds, 'second');
|
||||
|
|
@ -58,7 +85,7 @@ export const Age = (props: TAge): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
export const Weight = (props: TWeight): JSX.Element => {
|
||||
export const Weight = (props: WeightProps): JSX.Element => {
|
||||
const { weight, winningWeight, ...rest } = props;
|
||||
const fixMeText =
|
||||
winningWeight === 'low' ? 'Lower Weight is Preferred' : 'Higher Weight is Preferred';
|
||||
|
|
@ -71,7 +98,7 @@ export const Weight = (props: TWeight): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
export const ASPath = (props: TASPath): JSX.Element => {
|
||||
export const ASPath = (props: ASPathProps): JSX.Element => {
|
||||
const { path, active } = props;
|
||||
const color = useColorValue(
|
||||
// light: inactive, active
|
||||
|
|
@ -108,7 +135,7 @@ export const ASPath = (props: TASPath): JSX.Element => {
|
|||
return <>{paths}</>;
|
||||
};
|
||||
|
||||
export const Communities = (props: TCommunities): JSX.Element => {
|
||||
export const Communities = (props: CommunitiesProps): JSX.Element => {
|
||||
const { communities } = props;
|
||||
const { web } = useConfig();
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
|
|
@ -147,8 +174,8 @@ export const Communities = (props: TCommunities): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
const _RPKIState: React.ForwardRefRenderFunction<HTMLDivElement, TRPKIState> = (
|
||||
props: TRPKIState,
|
||||
const _RPKIState: React.ForwardRefRenderFunction<HTMLDivElement, RPKIStateProps> = (
|
||||
props: RPKIStateProps,
|
||||
ref,
|
||||
) => {
|
||||
const { state, active } = props;
|
||||
|
|
@ -194,4 +221,4 @@ const _RPKIState: React.ForwardRefRenderFunction<HTMLDivElement, TRPKIState> = (
|
|||
);
|
||||
};
|
||||
|
||||
export const RPKIState = forwardRef<HTMLDivElement, TRPKIState>(_RPKIState);
|
||||
export const RPKIState = forwardRef<HTMLDivElement, RPKIStateProps>(_RPKIState);
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
export * from './table';
|
||||
export * from './text';
|
||||
export * from './bgp-table';
|
||||
export * from './text-output';
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ import { Box } from '@chakra-ui/react';
|
|||
import { useColorValue, useConfig } from '~/context';
|
||||
import { Highlighted } from './highlighted';
|
||||
|
||||
import type { TTextOutput } from './types';
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export const TextOutput = (props: TTextOutput): JSX.Element => {
|
||||
type TextOutputProps = Swap<BoxProps, 'children', string>;
|
||||
|
||||
export const TextOutput = (props: TextOutputProps): JSX.Element => {
|
||||
const { children, ...rest } = props;
|
||||
|
||||
const bg = useColorValue('blackAlpha.100', 'gray.800');
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
import type { BoxProps, FlexProps, TextProps } from '@chakra-ui/react';
|
||||
import type { TCellRender } from '~/types';
|
||||
|
||||
export interface TTextOutput extends Omit<BoxProps, 'children'> {
|
||||
children: string;
|
||||
}
|
||||
|
||||
export interface TActive {
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export interface TMonoField extends TextProps {
|
||||
v: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface TAge extends TextProps {
|
||||
inSeconds: number;
|
||||
}
|
||||
|
||||
export interface TWeight extends TextProps {
|
||||
weight: number;
|
||||
winningWeight: 'low' | 'high';
|
||||
}
|
||||
|
||||
export interface TASPath {
|
||||
path: number[];
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface TCommunities {
|
||||
communities: string[];
|
||||
}
|
||||
|
||||
export interface TRPKIState {
|
||||
state:
|
||||
| 0 // Invalid
|
||||
| 1 // Valid
|
||||
| 2 // Unknown
|
||||
| 3; // Unverified
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface TCell {
|
||||
data: TCellRender;
|
||||
rawData: StructuredResponse;
|
||||
}
|
||||
|
||||
export interface TBGPTable extends Omit<FlexProps, 'children'> {
|
||||
children: StructuredResponse;
|
||||
}
|
||||
|
|
@ -5,9 +5,24 @@ import { useASNDetail } from '~/hooks';
|
|||
import { Controls } from './controls';
|
||||
import { useElements } from './useElements';
|
||||
|
||||
import type { TChart, TNode, TNodeData } from './types';
|
||||
import type { NodeProps as ReactFlowNodeProps } from 'react-flow-renderer';
|
||||
|
||||
export const Chart = (props: TChart): JSX.Element => {
|
||||
interface ChartProps {
|
||||
data: StructuredResponse;
|
||||
}
|
||||
|
||||
interface NodeProps<D extends unknown> extends Omit<ReactFlowNodeProps, 'data'> {
|
||||
data: D;
|
||||
}
|
||||
|
||||
interface NodeData {
|
||||
asn: string;
|
||||
name: string;
|
||||
hasChildren: boolean;
|
||||
hasParents?: boolean;
|
||||
}
|
||||
|
||||
export const Chart = (props: ChartProps): JSX.Element => {
|
||||
const { data } = props;
|
||||
const { primaryAsn, orgName } = useConfig();
|
||||
|
||||
|
|
@ -32,7 +47,7 @@ export const Chart = (props: TChart): JSX.Element => {
|
|||
);
|
||||
};
|
||||
|
||||
const ASNode = (props: TNode<TNodeData>): JSX.Element => {
|
||||
const ASNode = (props: NodeProps<NodeData>): JSX.Element => {
|
||||
const { data } = props;
|
||||
const { asn, name, hasChildren, hasParents } = data;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { Button, Tooltip } from '@chakra-ui/react';
|
||||
import { DynamicIcon } from '~/components';
|
||||
|
||||
import type { TPathButton } from './types';
|
||||
interface PathButtonProps {
|
||||
onOpen(): void;
|
||||
}
|
||||
|
||||
export const PathButton = (props: TPathButton): JSX.Element => {
|
||||
export const PathButton = (props: PathButtonProps): JSX.Element => {
|
||||
const { onOpen } = props;
|
||||
return (
|
||||
<Tooltip hasArrow label="View AS Path" placement="top">
|
||||
|
|
@ -10,12 +10,14 @@ import {
|
|||
} from '@chakra-ui/react';
|
||||
import { useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useFormState } from '~/hooks';
|
||||
import { PathButton } from './button';
|
||||
import { PathButton } from './path-button';
|
||||
import { Chart } from './chart';
|
||||
|
||||
import type { TPath } from './types';
|
||||
interface PathProps {
|
||||
device: string;
|
||||
}
|
||||
|
||||
export const Path = (props: TPath): JSX.Element => {
|
||||
export const Path = (props: PathProps): JSX.Element => {
|
||||
const { device } = props;
|
||||
const displayTarget = useFormState(s => s.target.display);
|
||||
const getResponse = useFormState(s => s.response);
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
import type { NodeProps } from 'react-flow-renderer';
|
||||
|
||||
export interface TChart {
|
||||
data: StructuredResponse;
|
||||
}
|
||||
|
||||
export interface TPath {
|
||||
device: string;
|
||||
}
|
||||
|
||||
export interface TNode<D extends unknown> extends Omit<NodeProps, 'data'> {
|
||||
data: D;
|
||||
}
|
||||
|
||||
export interface TNodeData {
|
||||
asn: string;
|
||||
name: string;
|
||||
hasChildren: boolean;
|
||||
hasParents?: boolean;
|
||||
}
|
||||
|
||||
export interface BasePath {
|
||||
asn: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface TPathButton {
|
||||
onOpen(): void;
|
||||
}
|
||||
|
|
@ -3,7 +3,11 @@ import { useMemo } from 'react';
|
|||
import isEqual from 'react-fast-compare';
|
||||
|
||||
import type { FlowElement } from 'react-flow-renderer';
|
||||
import type { BasePath } from './types';
|
||||
|
||||
interface BasePath {
|
||||
asn: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const NODE_WIDTH = 200;
|
||||
const NODE_HEIGHT = 48;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
import { useMemo } from 'react';
|
||||
import { Wrap, VStack, Flex, chakra } from '@chakra-ui/react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { Select } from '~/components';
|
||||
import { Select, LocationCard } from '~/components';
|
||||
import { useConfig } from '~/context';
|
||||
import { useFormState } from '~/hooks';
|
||||
import { isMultiValue, isSingleValue } from '~/components/select';
|
||||
import { LocationCard } from './location-card';
|
||||
|
||||
import type { DeviceGroup, SingleOption, OptionGroup, FormData } from '~/types';
|
||||
import type { DeviceGroup, SingleOption, OptionGroup, FormData, OnChangeArgs } from '~/types';
|
||||
import type { SelectOnChange } from '~/components/select';
|
||||
import type { TQuerySelectField } from './types';
|
||||
|
||||
/** Location option type alias for future extensions. */
|
||||
export type LocationOption = SingleOption;
|
||||
|
||||
interface QueryLocationProps {
|
||||
onChange: (f: OnChangeArgs) => void;
|
||||
label: string;
|
||||
}
|
||||
|
||||
function buildOptions(devices: DeviceGroup[]): OptionGroup<LocationOption>[] {
|
||||
return devices
|
||||
.map(group => {
|
||||
|
|
@ -37,7 +40,7 @@ function buildOptions(devices: DeviceGroup[]): OptionGroup<LocationOption>[] {
|
|||
.sort((a, b) => (a.label < b.label ? -1 : a.label > b.label ? 1 : 0));
|
||||
}
|
||||
|
||||
export const QueryLocation = (props: TQuerySelectField): JSX.Element => {
|
||||
export const QueryLocation = (props: QueryLocationProps): JSX.Element => {
|
||||
const { onChange, label } = props;
|
||||
|
||||
const {
|
||||
|
|
@ -7,15 +7,22 @@ import { isSingleValue } from '~/components/select';
|
|||
import { useColorValue } from '~/context';
|
||||
import { useDirective, useFormState } from '~/hooks';
|
||||
import { isSelectDirective } from '~/types';
|
||||
import { UserIP } from './userIP';
|
||||
import { UserIP } from './user-ip';
|
||||
|
||||
import type { OptionProps, GroupBase } from 'react-select';
|
||||
import type { UseFormRegister } from 'react-hook-form';
|
||||
import type { SelectOnChange } from '~/components/select';
|
||||
import type { Directive, SingleOption } from '~/types';
|
||||
import type { TQueryTarget } from './types';
|
||||
import type { Directive, SingleOption, OnChangeArgs, FormData } from '~/types';
|
||||
|
||||
type OptionWithDescription = SingleOption<{ description: string | null }>;
|
||||
|
||||
interface QueryTargetProps {
|
||||
name: string;
|
||||
placeholder: string;
|
||||
onChange(e: OnChangeArgs): void;
|
||||
register: UseFormRegister<FormData>;
|
||||
}
|
||||
|
||||
function buildOptions(directive: Nullable<Directive>): OptionWithDescription[] {
|
||||
if (directive !== null && isSelectDirective(directive)) {
|
||||
return directive.options.map(o => ({
|
||||
|
|
@ -40,7 +47,7 @@ const Option = (props: OptionProps<OptionWithDescription, false>) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const QueryTarget = (props: TQueryTarget): JSX.Element => {
|
||||
export const QueryTarget = (props: QueryTargetProps): JSX.Element => {
|
||||
const { name, register, onChange, placeholder } = props;
|
||||
|
||||
const bg = useColorValue('white', 'blackSolid.800');
|
||||
|
|
@ -9,14 +9,14 @@ import { isSingleValue } from '~/components/select';
|
|||
|
||||
import type { UseRadioProps } from '@chakra-ui/react';
|
||||
import type { MenuListProps } from 'react-select';
|
||||
import type { SingleOption, OptionGroup, OptionsOrGroup } from '~/types';
|
||||
import type { SingleOption, OptionGroup, OptionsOrGroup, OnChangeArgs } from '~/types';
|
||||
import type { SelectOnChange } from '~/components/select';
|
||||
import type { TQuerySelectField } from './types';
|
||||
|
||||
type QueryTypeOption = SingleOption<{ group?: string }>;
|
||||
|
||||
function sorter<T extends QueryTypeOption | OptionGroup<QueryTypeOption>>(a: T, b: T): number {
|
||||
return a.label < b.label ? -1 : a.label > b.label ? 1 : 0;
|
||||
interface QueryTypeProps {
|
||||
onChange: (f: OnChangeArgs) => void;
|
||||
label: string;
|
||||
}
|
||||
|
||||
type UserFilter = {
|
||||
|
|
@ -25,6 +25,10 @@ type UserFilter = {
|
|||
filter(candidate: QueryTypeOption, input: string): boolean;
|
||||
};
|
||||
|
||||
function sorter<T extends QueryTypeOption | OptionGroup<QueryTypeOption>>(a: T, b: T): number {
|
||||
return a.label < b.label ? -1 : a.label > b.label ? 1 : 0;
|
||||
}
|
||||
|
||||
const useFilter = create<UserFilter>((set, get) => ({
|
||||
selected: '',
|
||||
setSelected(newValue: string) {
|
||||
|
|
@ -146,7 +150,7 @@ const MenuList = (props: MenuListProps<QueryTypeOption, boolean>): JSX.Element =
|
|||
);
|
||||
};
|
||||
|
||||
export const QueryType = (props: TQuerySelectField): JSX.Element => {
|
||||
export const QueryType = (props: QueryTypeProps): JSX.Element => {
|
||||
const { onChange, label } = props;
|
||||
const {
|
||||
formState: { errors },
|
||||
|
|
@ -5,7 +5,10 @@ import { useConfig, useColorValue } from '~/context';
|
|||
import { useStrf, useDNSQuery, useFormState } from '~/hooks';
|
||||
|
||||
import type { DnsOverHttps } from '~/types';
|
||||
import type { ResolvedTargetProps } from './types';
|
||||
|
||||
interface ResolvedTargetProps {
|
||||
errorClose(): void;
|
||||
}
|
||||
|
||||
function findAnswer(data: DnsOverHttps.Response | undefined): string {
|
||||
let answer = '';
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
import { Button, Tooltip, useClipboard } from '@chakra-ui/react';
|
||||
import { DynamicIcon } from '~/components';
|
||||
|
||||
import type { TCopyButton } from './types';
|
||||
import type { ButtonProps } from '@chakra-ui/react';
|
||||
|
||||
export const CopyButton = (props: TCopyButton): JSX.Element => {
|
||||
interface CopyButtonProps extends ButtonProps {
|
||||
copyValue: string;
|
||||
}
|
||||
|
||||
export const CopyButton = (props: CopyButtonProps): JSX.Element => {
|
||||
const { copyValue, ...rest } = props;
|
||||
const { onCopy, hasCopied } = useClipboard(copyValue);
|
||||
return (
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
import { Text } from '@chakra-ui/react';
|
||||
import { chakra } from '@chakra-ui/react';
|
||||
|
||||
import type { TFormattedError } from './types';
|
||||
interface FormattedErrorProps {
|
||||
keywords: string[];
|
||||
message: string;
|
||||
}
|
||||
|
||||
type TFormatError = string | JSX.Element;
|
||||
type FormatError = string | JSX.Element;
|
||||
|
||||
function formatError(text: string, values: string[], regex: RegExp): TFormatError[] | TFormatError {
|
||||
function formatError(text: string, values: string[], regex: RegExp): FormatError[] | FormatError {
|
||||
if (!values.length) {
|
||||
return text;
|
||||
}
|
||||
|
|
@ -19,16 +22,16 @@ function formatError(text: string, values: string[], regex: RegExp): TFormatErro
|
|||
return prev.concat(
|
||||
values.includes(current) ? <strong key={i + current}>{current}</strong> : current,
|
||||
);
|
||||
}, [] as TFormatError[]);
|
||||
}, [] as FormatError[]);
|
||||
}
|
||||
|
||||
export const FormattedError = (props: TFormattedError): JSX.Element => {
|
||||
export const FormattedError = (props: FormattedErrorProps): JSX.Element => {
|
||||
const { keywords, message } = props;
|
||||
const pattern = new RegExp(keywords.map(kw => `(${kw})`).join('|'), 'gi');
|
||||
const things = formatError(message, keywords, pattern);
|
||||
return (
|
||||
<Text as="span" fontWeight={keywords.length === 0 ? 'bold' : undefined}>
|
||||
<chakra.span fontWeight={keywords.length === 0 ? 'bold' : undefined}>
|
||||
{keywords.length !== 0 ? things : message}
|
||||
</Text>
|
||||
</chakra.span>
|
||||
);
|
||||
};
|
||||
|
|
@ -4,7 +4,16 @@ import { DynamicIcon } from '~/components';
|
|||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useOpposingColor, useStrf } from '~/hooks';
|
||||
|
||||
import type { TResultHeader } from './types';
|
||||
import type { ErrorLevels } from '~/types';
|
||||
|
||||
interface ResultHeaderProps {
|
||||
title: string;
|
||||
loading: boolean;
|
||||
isError?: boolean;
|
||||
errorMsg: string;
|
||||
errorLevel: ErrorLevels;
|
||||
runtime: number;
|
||||
}
|
||||
|
||||
const runtimeText = (runtime: number, text: string): string => {
|
||||
let unit = 'seconds';
|
||||
|
|
@ -14,7 +23,7 @@ const runtimeText = (runtime: number, text: string): string => {
|
|||
return `${text} ${unit}`;
|
||||
};
|
||||
|
||||
export const ResultHeader = (props: TResultHeader): JSX.Element => {
|
||||
export const ResultHeader = (props: ResultHeaderProps): JSX.Element => {
|
||||
const { title, loading, isError, errorMsg, errorLevel, runtime } = props;
|
||||
|
||||
const status = useColorValue('primary.500', 'primary.300');
|
||||
|
|
|
|||
|
|
@ -21,12 +21,17 @@ import { useColorValue, useConfig, useMobile } from '~/context';
|
|||
import { useStrf, useLGQuery, useTableToString, useFormState, useDevice } from '~/hooks';
|
||||
import { isStructuredOutput, isStringOutput } from '~/types';
|
||||
import { isStackError, isFetchError, isLGError, isLGOutputOrError } from './guards';
|
||||
import { RequeryButton } from './requeryButton';
|
||||
import { CopyButton } from './copyButton';
|
||||
import { FormattedError } from './error';
|
||||
import { RequeryButton } from './requery-button';
|
||||
import { CopyButton } from './copy-button';
|
||||
import { FormattedError } from './formatted-error';
|
||||
import { ResultHeader } from './header';
|
||||
|
||||
import type { ResultProps, TErrorLevels } from './types';
|
||||
import type { ErrorLevels } from '~/types';
|
||||
|
||||
interface ResultProps {
|
||||
index: number;
|
||||
queryLocation: string;
|
||||
}
|
||||
|
||||
const AnimatedAccordionItem = motion(AccordionItem);
|
||||
|
||||
|
|
@ -106,7 +111,7 @@ const _Result: React.ForwardRefRenderFunction<HTMLDivElement, ResultProps> = (
|
|||
}
|
||||
}, [error, data, messages.general, messages.requestTimeout]);
|
||||
|
||||
const errorLevel = useMemo<TErrorLevels>(() => {
|
||||
const errorLevel = useMemo<ErrorLevels>(() => {
|
||||
const statusMap = {
|
||||
success: 'success',
|
||||
warning: 'warning',
|
||||
|
|
@ -114,7 +119,7 @@ const _Result: React.ForwardRefRenderFunction<HTMLDivElement, ResultProps> = (
|
|||
danger: 'error',
|
||||
} as { [K in ResponseLevel]: 'success' | 'warning' | 'error' };
|
||||
|
||||
let e: TErrorLevels = 'error';
|
||||
let e: ErrorLevels = 'error';
|
||||
|
||||
if (isLGError(error)) {
|
||||
const idx = error.level as ResponseLevel;
|
||||
|
|
@ -182,103 +187,101 @@ const _Result: React.ForwardRefRenderFunction<HTMLDivElement, ResultProps> = (
|
|||
'&:last-of-type': { borderBottom: 'none' },
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<AccordionHeaderWrapper>
|
||||
<AccordionButton py={2} w="unset" _hover={{}} _focus={{}} flex="1 0 auto">
|
||||
<ResultHeader
|
||||
isError={isError}
|
||||
loading={isLoading}
|
||||
errorMsg={errorMsg}
|
||||
errorLevel={errorLevel}
|
||||
runtime={data?.runtime ?? 0}
|
||||
title={device.name}
|
||||
/>
|
||||
</AccordionButton>
|
||||
<HStack py={2} spacing={1}>
|
||||
{isStructuredOutput(data) && data.level === 'success' && tableComponent && (
|
||||
<Path device={device.id} />
|
||||
<AccordionHeaderWrapper>
|
||||
<AccordionButton py={2} w="unset" _hover={{}} _focus={{}} flex="1 0 auto">
|
||||
<ResultHeader
|
||||
isError={isError}
|
||||
loading={isLoading}
|
||||
errorMsg={errorMsg}
|
||||
errorLevel={errorLevel}
|
||||
runtime={data?.runtime ?? 0}
|
||||
title={device.name}
|
||||
/>
|
||||
</AccordionButton>
|
||||
<HStack py={2} spacing={1}>
|
||||
{isStructuredOutput(data) && data.level === 'success' && tableComponent && (
|
||||
<Path device={device.id} />
|
||||
)}
|
||||
<CopyButton copyValue={copyValue} isDisabled={isLoading} />
|
||||
<RequeryButton requery={refetch} isDisabled={isLoading} />
|
||||
</HStack>
|
||||
</AccordionHeaderWrapper>
|
||||
<AccordionPanel
|
||||
pb={4}
|
||||
overflowX="auto"
|
||||
css={{
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
'&::-webkit-scrollbar': { height: '5px' },
|
||||
'&::-webkit-scrollbar-track': {
|
||||
backgroundColor: scrollbarBg,
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
backgroundColor: scrollbar,
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
backgroundColor: scrollbarHover,
|
||||
},
|
||||
|
||||
'-ms-overflow-style': { display: 'none' },
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Flex direction="column" flex="1 0 auto" maxW={error ? '100%' : undefined}>
|
||||
{!isError && typeof data !== 'undefined' ? (
|
||||
<>
|
||||
{isStructuredOutput(data) && data.level === 'success' && tableComponent ? (
|
||||
<BGPTable>{data.output}</BGPTable>
|
||||
) : isStringOutput(data) && data.level === 'success' && !tableComponent ? (
|
||||
<TextOutput>{data.output}</TextOutput>
|
||||
) : isStringOutput(data) && data.level !== 'success' ? (
|
||||
<Alert rounded="lg" my={2} py={4} status={errorLevel} variant="solid">
|
||||
<FormattedError message={data.output} keywords={errorKeywords} />
|
||||
</Alert>
|
||||
) : (
|
||||
<Alert rounded="lg" my={2} py={4} status={errorLevel} variant="solid">
|
||||
<FormattedError message={errorMsg} keywords={errorKeywords} />
|
||||
</Alert>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Alert rounded="lg" my={2} py={4} status={errorLevel} variant="solid">
|
||||
<FormattedError message={errorMsg} keywords={errorKeywords} />
|
||||
</Alert>
|
||||
)}
|
||||
<CopyButton copyValue={copyValue} isDisabled={isLoading} />
|
||||
<RequeryButton requery={refetch} isDisabled={isLoading} />
|
||||
</HStack>
|
||||
</AccordionHeaderWrapper>
|
||||
<AccordionPanel
|
||||
pb={4}
|
||||
overflowX="auto"
|
||||
css={{
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
'&::-webkit-scrollbar': { height: '5px' },
|
||||
'&::-webkit-scrollbar-track': {
|
||||
backgroundColor: scrollbarBg,
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
backgroundColor: scrollbar,
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
backgroundColor: scrollbarHover,
|
||||
},
|
||||
|
||||
'-ms-overflow-style': { display: 'none' },
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Flex direction="column" flex="1 0 auto" maxW={error ? '100%' : undefined}>
|
||||
{!isError && typeof data !== 'undefined' ? (
|
||||
<>
|
||||
{isStructuredOutput(data) && data.level === 'success' && tableComponent ? (
|
||||
<BGPTable>{data.output}</BGPTable>
|
||||
) : isStringOutput(data) && data.level === 'success' && !tableComponent ? (
|
||||
<TextOutput>{data.output}</TextOutput>
|
||||
) : isStringOutput(data) && data.level !== 'success' ? (
|
||||
<Alert rounded="lg" my={2} py={4} status={errorLevel} variant="solid">
|
||||
<FormattedError message={data.output} keywords={errorKeywords} />
|
||||
</Alert>
|
||||
) : (
|
||||
<Alert rounded="lg" my={2} py={4} status={errorLevel} variant="solid">
|
||||
<FormattedError message={errorMsg} keywords={errorKeywords} />
|
||||
</Alert>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Alert rounded="lg" my={2} py={4} status={errorLevel} variant="solid">
|
||||
<FormattedError message={errorMsg} keywords={errorKeywords} />
|
||||
</Alert>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
<Flex direction="row" flexWrap="wrap">
|
||||
<HStack
|
||||
px={3}
|
||||
mt={2}
|
||||
spacing={1}
|
||||
flex="1 0 auto"
|
||||
justifyContent={{ base: 'flex-start', lg: 'flex-end' }}
|
||||
>
|
||||
<If condition={cache.showText && !isError && isCached}>
|
||||
<If condition={isMobile}>
|
||||
<Then>
|
||||
<Countdown timeout={cache.timeout} text={web.text.cachePrefix} />
|
||||
<Tooltip hasArrow label={cacheLabel} placement="top">
|
||||
<Box>
|
||||
<DynamicIcon icon={{ bs: 'BsLightningFill' }} color={color} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Then>
|
||||
<Else>
|
||||
<Tooltip hasArrow label={cacheLabel} placement="top">
|
||||
<Box>
|
||||
<DynamicIcon icon={{ bs: 'BsLightningFill' }} color={color} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Countdown timeout={cache.timeout} text={web.text.cachePrefix} />
|
||||
</Else>
|
||||
</If>
|
||||
</If>
|
||||
</HStack>
|
||||
</Flex>
|
||||
</AccordionPanel>
|
||||
</>
|
||||
</Box>
|
||||
|
||||
<Flex direction="row" flexWrap="wrap">
|
||||
<HStack
|
||||
px={3}
|
||||
mt={2}
|
||||
spacing={1}
|
||||
flex="1 0 auto"
|
||||
justifyContent={{ base: 'flex-start', lg: 'flex-end' }}
|
||||
>
|
||||
<If condition={cache.showText && !isError && isCached}>
|
||||
<If condition={isMobile}>
|
||||
<Then>
|
||||
<Countdown timeout={cache.timeout} text={web.text.cachePrefix} />
|
||||
<Tooltip hasArrow label={cacheLabel} placement="top">
|
||||
<Box>
|
||||
<DynamicIcon icon={{ bs: 'BsLightningFill' }} color={color} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Then>
|
||||
<Else>
|
||||
<Tooltip hasArrow label={cacheLabel} placement="top">
|
||||
<Box>
|
||||
<DynamicIcon icon={{ bs: 'BsLightningFill' }} color={color} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Countdown timeout={cache.timeout} text={web.text.cachePrefix} />
|
||||
</Else>
|
||||
</If>
|
||||
</If>
|
||||
</HStack>
|
||||
</Flex>
|
||||
</AccordionPanel>
|
||||
</AnimatedAccordionItem>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,10 +2,15 @@ import { forwardRef } from 'react';
|
|||
import { Button, Tooltip } from '@chakra-ui/react';
|
||||
import { DynamicIcon } from '~/components';
|
||||
|
||||
import type { TRequeryButton } from './types';
|
||||
import type { ButtonProps } from '@chakra-ui/react';
|
||||
import type { UseQueryResult } from 'react-query';
|
||||
|
||||
const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, TRequeryButton> = (
|
||||
props: TRequeryButton,
|
||||
interface RequeryButtonProps extends ButtonProps {
|
||||
requery: Get<UseQueryResult<QueryResponse>, 'refetch'>;
|
||||
}
|
||||
|
||||
const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, RequeryButtonProps> = (
|
||||
props: RequeryButtonProps,
|
||||
ref,
|
||||
) => {
|
||||
const { requery, ...rest } = props;
|
||||
|
|
@ -19,7 +24,7 @@ const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, TRequery
|
|||
size="sm"
|
||||
zIndex="1"
|
||||
variant="ghost"
|
||||
onClick={requery as TRequeryButton['onClick']}
|
||||
onClick={requery as Get<RequeryButtonProps, 'onClick'>}
|
||||
colorScheme="secondary"
|
||||
{...rest}
|
||||
>
|
||||
|
|
@ -29,4 +34,4 @@ const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, TRequery
|
|||
);
|
||||
};
|
||||
|
||||
export const RequeryButton = forwardRef<HTMLButtonElement, TRequeryButton>(_RequeryButton);
|
||||
export const RequeryButton = forwardRef<HTMLButtonElement, RequeryButtonProps>(_RequeryButton);
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
import type { ButtonProps } from '@chakra-ui/react';
|
||||
import type { UseQueryResult } from 'react-query';
|
||||
|
||||
export interface TResultHeader {
|
||||
title: string;
|
||||
loading: boolean;
|
||||
isError?: boolean;
|
||||
errorMsg: string;
|
||||
errorLevel: 'success' | 'warning' | 'error';
|
||||
runtime: number;
|
||||
}
|
||||
|
||||
export interface TFormattedError {
|
||||
keywords: string[];
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ResultProps {
|
||||
index: number;
|
||||
queryLocation: string;
|
||||
}
|
||||
|
||||
export type TErrorLevels = 'success' | 'warning' | 'error';
|
||||
|
||||
export interface TCopyButton extends ButtonProps {
|
||||
copyValue: string;
|
||||
}
|
||||
|
||||
export interface TRequeryButton extends ButtonProps {
|
||||
requery: UseQueryResult<QueryResponse>['refetch'];
|
||||
}
|
||||
|
|
@ -26,14 +26,14 @@ import type {
|
|||
SelectInstance,
|
||||
} from 'react-select';
|
||||
import type { SingleOption } from '~/types';
|
||||
import type { TSelectBase, TSelectContext } from './types';
|
||||
import type { SelectProps, SelectContextProps } from './types';
|
||||
|
||||
const SelectContext = createContext<TSelectContext>({} as TSelectContext);
|
||||
export const useSelectContext = (): TSelectContext => useContext(SelectContext);
|
||||
const SelectContext = createContext<SelectContextProps>({} as SelectContextProps);
|
||||
export const useSelectContext = (): SelectContextProps => useContext(SelectContext);
|
||||
|
||||
export const Select = forwardRef(
|
||||
<Opt extends SingleOption = SingleOption, IsMulti extends boolean = boolean>(
|
||||
props: TSelectBase<Opt, IsMulti>,
|
||||
props: SelectProps<Opt, IsMulti>,
|
||||
ref: React.Ref<SelectInstance<Opt, IsMulti>>,
|
||||
): JSX.Element => {
|
||||
const { options, isMulti, onSelect, isError = false, components, ...rest } = props;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import type { Theme, SingleOption } from '~/types';
|
|||
export type SelectOnChange<
|
||||
Opt extends SingleOption = SingleOption,
|
||||
IsMulti extends boolean = boolean,
|
||||
> = NonNullable<ReactSelect.Props<Opt, IsMulti>['onChange']>;
|
||||
> = NonNullable<Get<ReactSelect.Props<Opt, IsMulti>, 'onChange'>>;
|
||||
|
||||
export interface TSelectBase<Opt extends SingleOption, IsMulti extends boolean>
|
||||
export interface SelectProps<Opt extends SingleOption, IsMulti extends boolean>
|
||||
extends ReactSelect.Props<Opt, IsMulti> {
|
||||
name: string;
|
||||
isMulti?: IsMulti;
|
||||
|
|
@ -18,7 +18,7 @@ export interface TSelectBase<Opt extends SingleOption, IsMulti extends boolean>
|
|||
colorScheme?: Theme.ColorNames;
|
||||
}
|
||||
|
||||
export interface TSelectContext {
|
||||
export interface SelectContextProps {
|
||||
colorMode: 'light' | 'dark';
|
||||
isOpen: boolean;
|
||||
isError: boolean;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,14 @@ import { useMobile, useColorValue } from '~/context';
|
|||
import { useFormState } from '~/hooks';
|
||||
|
||||
import type { IconButtonProps } from '@chakra-ui/react';
|
||||
import type { SubmitButtonProps, ResponsiveSubmitButtonProps } from './types';
|
||||
|
||||
type SubmitButtonProps = Omit<IconButtonProps, 'aria-label'>;
|
||||
|
||||
interface ResponsiveSubmitButtonProps {
|
||||
isOpen: boolean;
|
||||
onClose(): void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const _SubmitIcon: React.ForwardRefRenderFunction<
|
||||
HTMLButtonElement,
|
||||
|
|
@ -1 +0,0 @@
|
|||
export * from './submit';
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import type { IconButtonProps } from '@chakra-ui/react';
|
||||
|
||||
export type SubmitButtonProps = Omit<IconButtonProps, 'aria-label'>;
|
||||
|
||||
export interface ResponsiveSubmitButtonProps {
|
||||
isOpen: boolean;
|
||||
onClose(): void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import { IconButton } from '@chakra-ui/react';
|
||||
|
||||
import type { TTableIconButton } from './types';
|
||||
import type { IconButtonProps } from '@chakra-ui/react';
|
||||
|
||||
type TTableIconButton = Omit<IconButtonProps, 'aria-label'>;
|
||||
|
||||
export const TableIconButton = (props: TTableIconButton): JSX.Element => (
|
||||
<IconButton size="sm" borderWidth={1} {...props} aria-label="Table Icon Button" />
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
import { chakra } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { TTableCell } from './types';
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export const TableCell = (props: TTableCell): JSX.Element => {
|
||||
interface TableCellProps extends Omit<BoxProps, 'align'> {
|
||||
bordersVertical?: [boolean, number];
|
||||
align?: 'left' | 'right' | 'center';
|
||||
}
|
||||
|
||||
export const TableCell = (props: TableCellProps): JSX.Element => {
|
||||
const { bordersVertical = [false, 0], align, ...rest } = props;
|
||||
const [doVerticalBorders, index] = bordersVertical;
|
||||
const borderLeftColor = useColorValue('blackAlpha.100', 'whiteAlpha.100');
|
||||
|
|
|
|||
|
|
@ -1,7 +1 @@
|
|||
export * from './body';
|
||||
export * from './button';
|
||||
export * from './cell';
|
||||
export * from './head';
|
||||
export * from './main';
|
||||
export * from './pageSelect';
|
||||
export * from './row';
|
||||
|
|
|
|||
|
|
@ -11,13 +11,24 @@ import { TableHead } from './head';
|
|||
import { TableRow } from './row';
|
||||
import { TableBody } from './body';
|
||||
import { TableIconButton } from './button';
|
||||
import { TableSelectShow } from './pageSelect';
|
||||
import { PageSelect } from './page-select';
|
||||
|
||||
import type { TableOptions, PluginHook } from 'react-table';
|
||||
import type { TCellRender } from '~/types';
|
||||
import type { TTable } from './types';
|
||||
import type { Theme, TableColumn, CellRenderProps } from '~/types';
|
||||
|
||||
export const Table = (props: TTable): JSX.Element => {
|
||||
interface TableProps {
|
||||
data: Route[];
|
||||
striped?: boolean;
|
||||
columns: TableColumn[];
|
||||
heading?: React.ReactNode;
|
||||
bordersVertical?: boolean;
|
||||
bordersHorizontal?: boolean;
|
||||
Cell?: React.FC<CellRenderProps>;
|
||||
rowHighlightProp?: keyof Route;
|
||||
rowHighlightBg?: Theme.ColorNames;
|
||||
}
|
||||
|
||||
export const Table = (props: TableProps): JSX.Element => {
|
||||
const {
|
||||
data,
|
||||
columns,
|
||||
|
|
@ -121,7 +132,7 @@ export const Table = (props: TTable): JSX.Element => {
|
|||
{...row.getRowProps()}
|
||||
>
|
||||
{row.cells.map((cell, i) => {
|
||||
const { column, row, value } = cell as TCellRender;
|
||||
const { column, row, value } = cell as CellRenderProps;
|
||||
return (
|
||||
<TableCell
|
||||
align={cell.column.align}
|
||||
|
|
@ -164,7 +175,7 @@ export const Table = (props: TTable): JSX.Element => {
|
|||
</strong>{' '}
|
||||
</Text>
|
||||
{!isMobile && (
|
||||
<TableSelectShow
|
||||
<PageSelect
|
||||
value={pageSize}
|
||||
onChange={e => {
|
||||
setPageSize(Number(e.target.value));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Select } from '@chakra-ui/react';
|
|||
|
||||
import type { SelectProps } from '@chakra-ui/react';
|
||||
|
||||
export const TableSelectShow = (props: SelectProps): JSX.Element => {
|
||||
export const PageSelect = (props: SelectProps): JSX.Element => {
|
||||
const { value, ...rest } = props;
|
||||
return (
|
||||
<Select size="sm" {...rest}>
|
||||
|
|
@ -2,9 +2,19 @@ import { chakra } from '@chakra-ui/react';
|
|||
import { useColorValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TTableRow } from './types';
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export const TableRow = (props: TTableRow): JSX.Element => {
|
||||
import type { Theme } from '~/types';
|
||||
|
||||
interface TableRowProps extends BoxProps {
|
||||
highlightBg?: Theme.ColorNames;
|
||||
doHorizontalBorders?: boolean;
|
||||
highlight?: boolean;
|
||||
doStripe?: boolean;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export const TableRow = (props: TableRowProps): JSX.Element => {
|
||||
const {
|
||||
index = 0,
|
||||
doStripe = false,
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
import type { BoxProps, IconButtonProps } from '@chakra-ui/react';
|
||||
|
||||
import type { Theme, TColumn, TCellRender } from '~/types';
|
||||
|
||||
export interface TTable {
|
||||
data: Route[];
|
||||
striped?: boolean;
|
||||
columns: TColumn[];
|
||||
heading?: React.ReactNode;
|
||||
bordersVertical?: boolean;
|
||||
bordersHorizontal?: boolean;
|
||||
Cell?: React.FC<TCellRender>;
|
||||
rowHighlightProp?: keyof Route;
|
||||
rowHighlightBg?: Theme.ColorNames;
|
||||
}
|
||||
|
||||
export interface TTableCell extends Omit<BoxProps, 'align'> {
|
||||
bordersVertical?: [boolean, number];
|
||||
align?: 'left' | 'right' | 'center';
|
||||
}
|
||||
|
||||
export interface TTableRow extends BoxProps {
|
||||
highlightBg?: Theme.ColorNames;
|
||||
doHorizontalBorders?: boolean;
|
||||
highlight?: boolean;
|
||||
doStripe?: boolean;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export type TTableIconButton = Omit<IconButtonProps, 'aria-label'>;
|
||||
|
|
@ -4,7 +4,9 @@ import { DynamicIcon, Prompt } from '~/components';
|
|||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useStrf, useWtf } from '~/hooks';
|
||||
|
||||
import type { UserIPProps } from './types';
|
||||
interface UserIPProps {
|
||||
setTarget(target: string): void;
|
||||
}
|
||||
|
||||
export const UserIP = (props: UserIPProps): JSX.Element => {
|
||||
const { setTarget } = props;
|
||||
|
|
@ -1,48 +1,4 @@
|
|||
import type { UseQueryOptions } from 'react-query';
|
||||
import type * as ReactGA from 'react-ga';
|
||||
import type { Device, TFormQuery } from '~/types';
|
||||
|
||||
export type LGQueryKey = [string, TFormQuery];
|
||||
export type DNSQueryKey = [string, { target: string | null; family: 4 | 6 }];
|
||||
|
||||
export type LGQueryOptions = Omit<
|
||||
UseQueryOptions<QueryResponse, Response | QueryResponse | Error, QueryResponse, LGQueryKey>,
|
||||
| 'queryKey'
|
||||
| 'queryFn'
|
||||
| 'cacheTime'
|
||||
| 'refetchOnWindowFocus'
|
||||
| 'refetchInterval'
|
||||
| 'refetchOnMount'
|
||||
>;
|
||||
|
||||
export interface TOpposingOptions {
|
||||
light?: string;
|
||||
dark?: string;
|
||||
}
|
||||
|
||||
export type UseDevice = (
|
||||
/**
|
||||
* Device's ID, e.g. the device.name field.
|
||||
*/
|
||||
deviceId: string,
|
||||
) => Device | null;
|
||||
|
||||
export type UseStrfArgs = { [k: string]: unknown } | string;
|
||||
|
||||
export type TTableToStringFormatter =
|
||||
| ((v: string) => string)
|
||||
| ((v: number) => string)
|
||||
| ((v: number[]) => string)
|
||||
| ((v: string[]) => string)
|
||||
| ((v: boolean) => string);
|
||||
|
||||
export type TTableToStringFormatted = {
|
||||
age: (v: number) => string;
|
||||
active: (v: boolean) => string;
|
||||
as_path: (v: number[]) => string;
|
||||
communities: (v: string[]) => string;
|
||||
rpki_state: (v: number, n: RPKIState) => string;
|
||||
};
|
||||
|
||||
export type GAEffect = (ga: typeof ReactGA) => void;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
import { useQuery } from 'react-query';
|
||||
|
||||
import type { QueryFunctionContext, QueryObserverResult, QueryFunction } from 'react-query';
|
||||
import type { TASNQuery } from '~/types';
|
||||
|
||||
const query: QueryFunction<TASNQuery, string> = async (ctx: QueryFunctionContext) => {
|
||||
interface ASNQuery {
|
||||
data: {
|
||||
asn: {
|
||||
organization: {
|
||||
orgName: string;
|
||||
} | null;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const query: QueryFunction<ASNQuery, string> = async (ctx: QueryFunctionContext) => {
|
||||
const asn = ctx.queryKey;
|
||||
const res = await fetch('https://api.asrank.caida.org/v2/graphql', {
|
||||
mode: 'cors',
|
||||
|
|
@ -19,8 +28,8 @@ const query: QueryFunction<TASNQuery, string> = async (ctx: QueryFunctionContext
|
|||
* Query the Caida AS Rank API to get an ASN's organization name for the AS Path component.
|
||||
* @see https://api.asrank.caida.org/v2/docs
|
||||
*/
|
||||
export function useASNDetail(asn: string): QueryObserverResult<TASNQuery> {
|
||||
return useQuery<TASNQuery, unknown, TASNQuery, string>({
|
||||
export function useASNDetail(asn: string): QueryObserverResult<ASNQuery> {
|
||||
return useQuery<ASNQuery, unknown, ASNQuery, string>({
|
||||
queryKey: asn,
|
||||
queryFn: query,
|
||||
refetchOnWindowFocus: false,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { fetchWithTimeout } from '~/util';
|
|||
|
||||
import type { QueryFunction, QueryFunctionContext, QueryObserverResult } from 'react-query';
|
||||
import type { DnsOverHttps } from '~/types';
|
||||
import type { DNSQueryKey } from './types';
|
||||
|
||||
type DNSQueryKey = [string, { target: string | null; family: 4 | 6 }];
|
||||
|
||||
/**
|
||||
* Perform a DNS over HTTPS query using the application/dns-json MIME type.
|
||||
|
|
|
|||
|
|
@ -2,19 +2,26 @@ import { useCallback, useMemo } from 'react';
|
|||
import { useConfig } from '~/context';
|
||||
|
||||
import type { Device } from '~/types';
|
||||
import type { UseDevice } from './types';
|
||||
|
||||
export type UseDeviceReturn = (
|
||||
/** Device's ID, e.g. the device.name field.*/
|
||||
deviceId: string,
|
||||
) => Nullable<Device>;
|
||||
|
||||
/**
|
||||
* Get a device's configuration from the global configuration context based on its name.
|
||||
*/
|
||||
export function useDevice(): UseDevice {
|
||||
export function useDevice(): UseDeviceReturn {
|
||||
const { devices } = useConfig();
|
||||
|
||||
const locations = useMemo(() => devices.map(group => group.locations).flat(), [devices]);
|
||||
const locations = useMemo<Device[]>(
|
||||
() => devices.map(group => group.locations).flat(),
|
||||
[devices],
|
||||
);
|
||||
|
||||
function getDevice(id: string): Device | null {
|
||||
function getDevice(id: string): Nullable<Device> {
|
||||
return locations.find(device => device.id === id) ?? null;
|
||||
}
|
||||
|
||||
return useCallback(getDevice, [locations]);
|
||||
return useCallback<UseDeviceReturn>(getDevice, [locations]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type { Directive } from '~/types';
|
|||
export function useDirective(): Nullable<Directive> {
|
||||
const { getDirective, form } = useFormState(({ getDirective, form }) => ({ getDirective, form }));
|
||||
|
||||
return useMemo((): Nullable<Directive> => {
|
||||
return useMemo<Nullable<Directive>>(() => {
|
||||
if (form.queryType === '') {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import type { StateCreator } from 'zustand';
|
|||
import type { UseFormSetError, UseFormClearErrors } from 'react-hook-form';
|
||||
import type { SingleValue, MultiValue } from 'react-select';
|
||||
import type { SingleOption, Directive, FormData, Text, Device } from '~/types';
|
||||
import type { UseDevice } from './types';
|
||||
import type { UseDeviceReturn } from './useDevice';
|
||||
|
||||
type FormStatus = 'form' | 'results';
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ interface FormStateType<Opt extends SingleOption = SingleOption> {
|
|||
extra: {
|
||||
setError: UseFormSetError<FormData>;
|
||||
clearErrors: UseFormClearErrors<FormData>;
|
||||
getDevice: UseDevice;
|
||||
getDevice: UseDeviceReturn;
|
||||
text: Text;
|
||||
},
|
||||
): void;
|
||||
|
|
@ -139,7 +139,7 @@ const formState: StateCreator<FormStateType> = (set, get) => ({
|
|||
extra: {
|
||||
setError: UseFormSetError<FormData>;
|
||||
clearErrors: UseFormClearErrors<FormData>;
|
||||
getDevice: UseDevice;
|
||||
getDevice: UseDeviceReturn;
|
||||
text: Text;
|
||||
},
|
||||
): void {
|
||||
|
|
|
|||
|
|
@ -3,15 +3,31 @@ import { useQuery } from 'react-query';
|
|||
import { useConfig } from '~/context';
|
||||
import { fetchWithTimeout } from '~/util';
|
||||
|
||||
import type { QueryFunction, QueryFunctionContext, QueryObserverResult } from 'react-query';
|
||||
import type { TFormQuery } from '~/types';
|
||||
import type { LGQueryKey, LGQueryOptions } from './types';
|
||||
import type {
|
||||
QueryFunction,
|
||||
UseQueryOptions,
|
||||
QueryObserverResult,
|
||||
QueryFunctionContext,
|
||||
} from 'react-query';
|
||||
import type { FormQuery } from '~/types';
|
||||
|
||||
type LGQueryKey = [string, FormQuery];
|
||||
|
||||
type LGQueryOptions = Omit<
|
||||
UseQueryOptions<QueryResponse, Response | QueryResponse | Error, QueryResponse, LGQueryKey>,
|
||||
| 'queryKey'
|
||||
| 'queryFn'
|
||||
| 'cacheTime'
|
||||
| 'refetchOnWindowFocus'
|
||||
| 'refetchInterval'
|
||||
| 'refetchOnMount'
|
||||
>;
|
||||
|
||||
/**
|
||||
* Custom hook handle submission of a query to the hyperglass backend.
|
||||
*/
|
||||
export function useLGQuery(
|
||||
query: TFormQuery,
|
||||
query: FormQuery,
|
||||
options: LGQueryOptions = {} as LGQueryOptions,
|
||||
): QueryObserverResult<QueryResponse> {
|
||||
const { requestTimeout, cache } = useConfig();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import { useMemo, useCallback } from 'react';
|
|||
import { getColor, isLight } from '@chakra-ui/theme-tools';
|
||||
import { useTheme } from '~/context';
|
||||
|
||||
import type { TOpposingOptions } from './types';
|
||||
interface OpposingColorOptions {
|
||||
light?: string;
|
||||
dark?: string;
|
||||
}
|
||||
|
||||
export type UseIsDarkCallbackReturn = (color: string) => boolean;
|
||||
|
||||
|
|
@ -37,7 +40,7 @@ export function useIsDarkCallback(): UseIsDarkCallbackReturn {
|
|||
/**
|
||||
* Determine if the foreground color for `color` should be white or black.
|
||||
*/
|
||||
export function useOpposingColor(color: string, options?: TOpposingOptions): string {
|
||||
export function useOpposingColor(color: string, options?: OpposingColorOptions): string {
|
||||
const isBlack = useIsDark(color);
|
||||
|
||||
return useMemo(() => {
|
||||
|
|
@ -49,7 +52,9 @@ export function useOpposingColor(color: string, options?: TOpposingOptions): str
|
|||
}, [isBlack, options?.dark, options?.light]);
|
||||
}
|
||||
|
||||
export function useOpposingColorCallback(options?: TOpposingOptions): (color: string) => string {
|
||||
export function useOpposingColorCallback(
|
||||
options?: OpposingColorOptions,
|
||||
): (color: string) => string {
|
||||
const isDark = useIsDarkCallback();
|
||||
return useCallback(
|
||||
(color: string) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback } from 'react';
|
||||
import format from 'string-format';
|
||||
|
||||
import type { UseStrfArgs } from './types';
|
||||
type UseStrfArgs = { [k: string]: unknown } | string;
|
||||
|
||||
/**
|
||||
* Format a string with variables, like Python's string.format()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,20 @@ import utcPlugin from 'dayjs/plugin/utc';
|
|||
import { useConfig } from '~/context';
|
||||
import { isStructuredOutput } from '~/types';
|
||||
|
||||
import type { TTableToStringFormatter, TTableToStringFormatted } from './types';
|
||||
type TableToStringFormatter =
|
||||
| ((v: string) => string)
|
||||
| ((v: number) => string)
|
||||
| ((v: number[]) => string)
|
||||
| ((v: string[]) => string)
|
||||
| ((v: boolean) => string);
|
||||
|
||||
interface TableToStringFormatted {
|
||||
age: (v: number) => string;
|
||||
active: (v: boolean) => string;
|
||||
as_path: (v: number[]) => string;
|
||||
communities: (v: string[]) => string;
|
||||
rpki_state: (v: number, n: RPKIState) => string;
|
||||
}
|
||||
|
||||
dayjs.extend(relativeTimePlugin);
|
||||
dayjs.extend(utcPlugin);
|
||||
|
|
@ -65,11 +78,11 @@ export function useTableToString(
|
|||
rpki_state: formatRpkiState,
|
||||
};
|
||||
|
||||
function isFormatted(key: string): key is keyof TTableToStringFormatted {
|
||||
function isFormatted(key: string): key is keyof TableToStringFormatted {
|
||||
return key in tableFormatMap;
|
||||
}
|
||||
|
||||
function getFmtFunc(accessor: keyof Route): TTableToStringFormatter {
|
||||
function getFmtFunc(accessor: keyof Route): TableToStringFormatter {
|
||||
if (isFormatted(accessor)) {
|
||||
return tableFormatMap[accessor];
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,14 +3,10 @@ import { fetchWithTimeout } from '~/util';
|
|||
|
||||
import type {
|
||||
QueryFunction,
|
||||
QueryFunctionContext,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
UseQueryOptions,
|
||||
QueryFunctionContext,
|
||||
} from 'react-query';
|
||||
import type { WtfIsMyIP } from '~/types';
|
||||
|
||||
const URL_IP4 = 'https://ipv4.json.myip.wtf';
|
||||
const URL_IP6 = 'https://ipv6.json.myip.wtf';
|
||||
|
||||
interface WtfIndividual {
|
||||
ip: string;
|
||||
|
|
@ -21,6 +17,23 @@ interface WtfIndividual {
|
|||
|
||||
type Wtf = [UseQueryResult<WtfIndividual>, UseQueryResult<WtfIndividual>, () => Promise<void>];
|
||||
|
||||
/**
|
||||
* myip.wtf response.
|
||||
*
|
||||
* @see https://github.com/wtfismyip/wtfismyip
|
||||
* @see https://wtfismyip.com/automation
|
||||
*/
|
||||
interface WtfIsMyIP {
|
||||
YourFuckingIPAddress: string;
|
||||
YourFuckingLocation: string;
|
||||
YourFuckingISP: string;
|
||||
YourFuckingTorExit: boolean;
|
||||
YourFuckingCountryCode: string;
|
||||
}
|
||||
|
||||
const URL_IP4 = 'https://ipv4.json.myip.wtf';
|
||||
const URL_IP6 = 'https://ipv6.json.myip.wtf';
|
||||
|
||||
function transform(wtf: WtfIsMyIP): WtfIndividual {
|
||||
const { YourFuckingIPAddress, YourFuckingISP, YourFuckingLocation, YourFuckingCountryCode } = wtf;
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -6,51 +6,23 @@
|
|||
"strict": true,
|
||||
"baseUrl": "." /* Base directory to resolve non-absolute module names. */,
|
||||
"paths": {
|
||||
"~/components": [
|
||||
"components/index"
|
||||
],
|
||||
"~/components/*": [
|
||||
"components/*"
|
||||
],
|
||||
"~/context": [
|
||||
"context/index"
|
||||
],
|
||||
"~/context/*": [
|
||||
"context/*"
|
||||
],
|
||||
"~/hooks": [
|
||||
"hooks/index"
|
||||
],
|
||||
"~/hooks/*": [
|
||||
"hooks/*"
|
||||
],
|
||||
"~/state": [
|
||||
"state/index"
|
||||
],
|
||||
"~/state/*": [
|
||||
"state/*"
|
||||
],
|
||||
"~/types": [
|
||||
"types/index"
|
||||
],
|
||||
"~/types/*": [
|
||||
"types/*"
|
||||
],
|
||||
"~/util": [
|
||||
"util/index"
|
||||
],
|
||||
"~/util/*": [
|
||||
"util/*"
|
||||
]
|
||||
"~/components": ["components/index"],
|
||||
"~/components/*": ["components/*"],
|
||||
"~/context": ["context/index"],
|
||||
"~/context/*": ["context/*"],
|
||||
"~/hooks": ["hooks/index"],
|
||||
"~/hooks/*": ["hooks/*"],
|
||||
"~/state": ["state/index"],
|
||||
"~/state/*": ["state/*"],
|
||||
"~/types": ["types/index"],
|
||||
"~/types/*": ["types/*"],
|
||||
"~/util": ["util/index"],
|
||||
"~/util/*": ["util/*"]
|
||||
},
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"moduleResolution": "node",
|
||||
|
|
@ -59,10 +31,7 @@
|
|||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".next"
|
||||
],
|
||||
"exclude": ["node_modules", ".next"],
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
export interface TASNQuery {
|
||||
data: {
|
||||
asn: {
|
||||
organization: {
|
||||
orgName: string;
|
||||
} | null;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,20 +1,13 @@
|
|||
export type TQueryTypes = '' | TValidQueryTypes;
|
||||
export type TValidQueryTypes = 'bgp_route' | 'bgp_community' | 'bgp_aspath' | 'ping' | 'traceroute';
|
||||
|
||||
export interface FormData {
|
||||
queryLocation: string[];
|
||||
queryType: string;
|
||||
queryTarget: string;
|
||||
}
|
||||
|
||||
export interface TFormQuery extends Omit<FormData, 'queryLocation'> {
|
||||
queryLocation: string;
|
||||
}
|
||||
export type FormQuery = Swap<FormData, 'queryLocation', string>;
|
||||
|
||||
export interface TStringTableData extends Omit<QueryResponse, 'output'> {
|
||||
output: StructuredResponse;
|
||||
}
|
||||
export type StringTableData = Swap<QueryResponse, 'output', StructuredResponse>;
|
||||
|
||||
export interface TQueryResponseString extends Omit<QueryResponse, 'output'> {
|
||||
output: string;
|
||||
}
|
||||
export type StringQueryResponse = Swap<QueryResponse, 'output', string>;
|
||||
|
||||
export type ErrorLevels = 'success' | 'warning' | 'error';
|
||||
|
|
|
|||
15
hyperglass/ui/types/globals.d.ts
vendored
15
hyperglass/ui/types/globals.d.ts
vendored
|
|
@ -1,11 +1,14 @@
|
|||
import type { MotionProps } from 'framer-motion';
|
||||
|
||||
declare global {
|
||||
export declare global {
|
||||
type Dict<T = string> = Record<string, T>;
|
||||
|
||||
type ValueOf<T> = T[keyof T];
|
||||
|
||||
type Nullable<T> = T | null;
|
||||
|
||||
type Get<T, K extends keyof T> = T[K];
|
||||
|
||||
type Swap<T, K extends keyof T, V> = Record<K, V> & Omit<T, K>;
|
||||
|
||||
type RPKIState = 0 | 1 | 2 | 3;
|
||||
|
||||
type ResponseLevel = 'success' | 'warning' | 'error' | 'danger';
|
||||
|
|
@ -45,12 +48,6 @@ declare global {
|
|||
output: string | StructuredResponse;
|
||||
format: 'text/plain' | 'application/json';
|
||||
};
|
||||
type ReactRef<T = HTMLElement> = MutableRefObject<T>;
|
||||
|
||||
type Animated<T> = Omit<T, keyof MotionProps> &
|
||||
Omit<MotionProps, keyof T> & { transition?: MotionProps['transition'] };
|
||||
|
||||
type MeronexIcon = import('@meronex/icons').IconBaseProps;
|
||||
|
||||
type RequiredProps<T> = { [P in keyof T]-?: Exclude<T[P], undefined> };
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { FormData, TStringTableData, TQueryResponseString } from './data';
|
||||
import type { DirectiveSelect, Directive } from './config';
|
||||
import type { FormData, StringTableData, StringQueryResponse } from './data';
|
||||
import type { DirectiveSelect, Directive, Link, Menu } from './config';
|
||||
|
||||
export function isString(a: unknown): a is string {
|
||||
return typeof a === 'string';
|
||||
|
|
@ -15,11 +15,11 @@ export function isObject<T extends unknown = unknown>(
|
|||
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
|
||||
}
|
||||
|
||||
export function isStructuredOutput(data: unknown): data is TStringTableData {
|
||||
export function isStructuredOutput(data: unknown): data is StringTableData {
|
||||
return isObject(data) && 'output' in data;
|
||||
}
|
||||
|
||||
export function isStringOutput(data: unknown): data is TQueryResponseString {
|
||||
export function isStringOutput(data: unknown): data is StringQueryResponse {
|
||||
return (
|
||||
isObject(data) && 'output' in data && typeof (data as { output: unknown }).output === 'string'
|
||||
);
|
||||
|
|
@ -38,3 +38,11 @@ export function isQueryField(field: string): field is keyof FormData {
|
|||
export function isSelectDirective(directive: Directive): directive is DirectiveSelect {
|
||||
return directive.fieldType === 'select';
|
||||
}
|
||||
|
||||
export function isLink(item: Link | Menu): item is Link {
|
||||
return 'url' in item;
|
||||
}
|
||||
|
||||
export function isMenu(item: Link | Menu): item is Menu {
|
||||
return 'content' in item;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
export * from './caida';
|
||||
export * from './common';
|
||||
export * from './config';
|
||||
export * from './data';
|
||||
|
|
@ -6,5 +5,3 @@ export * from './dns-over-https';
|
|||
export * from './guards';
|
||||
export * from './table';
|
||||
export * from './theme';
|
||||
export * from './util';
|
||||
export * from './wtfismyip';
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import type { CellProps } from 'react-table';
|
||||
|
||||
export interface TColumn {
|
||||
export interface TableColumn {
|
||||
Header: string;
|
||||
accessor: keyof Route;
|
||||
align: string;
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export type TCellRender = {
|
||||
export type CellRenderProps = {
|
||||
column: CellProps<RouteField>['column'];
|
||||
row: CellProps<RouteField>['row'];
|
||||
value: CellProps<RouteField>['value'];
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
export interface PathPart {
|
||||
base: number;
|
||||
children: PathPart[];
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* myip.wtf response.
|
||||
*
|
||||
* @see https://github.com/wtfismyip/wtfismyip
|
||||
* @see https://wtfismyip.com/automation
|
||||
*/
|
||||
export interface WtfIsMyIP {
|
||||
YourFuckingIPAddress: string;
|
||||
YourFuckingLocation: string;
|
||||
YourFuckingISP: string;
|
||||
YourFuckingTorExit: boolean;
|
||||
YourFuckingCountryCode: string;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue