mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
continue typescript & chakra v1 migrations [skip ci]
This commit is contained in:
@@ -1,190 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { Text, useColorMode, useTheme } from '@chakra-ui/core';
|
|
||||||
import Select from 'react-select';
|
|
||||||
import { opposingColor } from 'app/util';
|
|
||||||
|
|
||||||
export const ChakraSelect = React.forwardRef(
|
|
||||||
({ placeholder = 'Select...', isFullWidth, size, children, ...props }, ref) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const { colorMode } = useColorMode();
|
|
||||||
const sizeMap = {
|
|
||||||
lg: { height: theme.space[12] },
|
|
||||||
md: { height: theme.space[10] },
|
|
||||||
sm: { height: theme.space[8] },
|
|
||||||
};
|
|
||||||
const colorSetPrimaryBg = {
|
|
||||||
dark: theme.colors.primary[300],
|
|
||||||
light: theme.colors.primary[500],
|
|
||||||
};
|
|
||||||
const colorSetPrimaryColor = opposingColor(theme, colorSetPrimaryBg[colorMode]);
|
|
||||||
const bg = {
|
|
||||||
dark: theme.colors.whiteAlpha[100],
|
|
||||||
light: theme.colors.white,
|
|
||||||
};
|
|
||||||
const color = {
|
|
||||||
dark: theme.colors.whiteAlpha[800],
|
|
||||||
light: theme.colors.black,
|
|
||||||
};
|
|
||||||
const borderFocused = theme.colors.secondary[500];
|
|
||||||
const borderDisabled = theme.colors.whiteAlpha[100];
|
|
||||||
const border = {
|
|
||||||
dark: theme.colors.whiteAlpha[50],
|
|
||||||
light: theme.colors.gray[100],
|
|
||||||
};
|
|
||||||
const borderRadius = theme.space[1];
|
|
||||||
const hoverColor = {
|
|
||||||
dark: theme.colors.whiteAlpha[200],
|
|
||||||
light: theme.colors.gray[300],
|
|
||||||
};
|
|
||||||
const { height } = sizeMap[size];
|
|
||||||
const optionBgActive = {
|
|
||||||
dark: theme.colors.primary[400],
|
|
||||||
light: theme.colors.primary[600],
|
|
||||||
};
|
|
||||||
const optionBgColor = opposingColor(theme, optionBgActive[colorMode]);
|
|
||||||
const optionSelectedBg = {
|
|
||||||
dark: theme.colors.whiteAlpha[400],
|
|
||||||
light: theme.colors.blackAlpha[400],
|
|
||||||
};
|
|
||||||
const optionSelectedColor = opposingColor(theme, optionSelectedBg[colorMode]);
|
|
||||||
const selectedDisabled = theme.colors.whiteAlpha[400];
|
|
||||||
const placeholderColor = {
|
|
||||||
dark: theme.colors.whiteAlpha[700],
|
|
||||||
light: theme.colors.gray[600],
|
|
||||||
};
|
|
||||||
const menuBg = {
|
|
||||||
dark: theme.colors.blackFaded[800],
|
|
||||||
light: theme.colors.whiteFaded[50],
|
|
||||||
};
|
|
||||||
const menuColor = {
|
|
||||||
dark: theme.colors.white,
|
|
||||||
light: theme.colors.blackAlpha[800],
|
|
||||||
};
|
|
||||||
const scrollbar = {
|
|
||||||
dark: theme.colors.whiteAlpha[300],
|
|
||||||
light: theme.colors.blackAlpha[300],
|
|
||||||
};
|
|
||||||
const scrollbarHover = {
|
|
||||||
dark: theme.colors.whiteAlpha[400],
|
|
||||||
light: theme.colors.blackAlpha[400],
|
|
||||||
};
|
|
||||||
const scrollbarBg = {
|
|
||||||
dark: theme.colors.whiteAlpha[50],
|
|
||||||
light: theme.colors.blackAlpha[50],
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
ref={ref}
|
|
||||||
styles={{
|
|
||||||
container: base => ({
|
|
||||||
...base,
|
|
||||||
minHeight: height,
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
width: '100%',
|
|
||||||
}),
|
|
||||||
control: (base, state) => ({
|
|
||||||
...base,
|
|
||||||
minHeight: height,
|
|
||||||
backgroundColor: bg[colorMode],
|
|
||||||
color: color[colorMode],
|
|
||||||
borderColor: state.isDisabled
|
|
||||||
? borderDisabled
|
|
||||||
: state.isFocused
|
|
||||||
? borderFocused
|
|
||||||
: border[colorMode],
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
'&:hover': {
|
|
||||||
borderColor: hoverColor[colorMode],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
menu: base => ({
|
|
||||||
...base,
|
|
||||||
backgroundColor: menuBg[colorMode],
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
}),
|
|
||||||
menuList: base => ({
|
|
||||||
...base,
|
|
||||||
'&::-webkit-scrollbar': { width: '5px' },
|
|
||||||
'&::-webkit-scrollbar-track': {
|
|
||||||
backgroundColor: scrollbarBg[colorMode],
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb': {
|
|
||||||
backgroundColor: scrollbar[colorMode],
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb:hover': {
|
|
||||||
backgroundColor: scrollbarHover[colorMode],
|
|
||||||
},
|
|
||||||
|
|
||||||
'-ms-overflow-style': { display: 'none' },
|
|
||||||
}),
|
|
||||||
option: (base, state) => ({
|
|
||||||
...base,
|
|
||||||
backgroundColor: state.isDisabled
|
|
||||||
? selectedDisabled
|
|
||||||
: state.isSelected
|
|
||||||
? optionSelectedBg[colorMode]
|
|
||||||
: state.isFocused
|
|
||||||
? colorSetPrimaryBg[colorMode]
|
|
||||||
: 'transparent',
|
|
||||||
color: state.isDisabled
|
|
||||||
? selectedDisabled
|
|
||||||
: state.isFocused
|
|
||||||
? colorSetPrimaryColor
|
|
||||||
: state.isSelected
|
|
||||||
? optionSelectedColor
|
|
||||||
: menuColor[colorMode],
|
|
||||||
fontSize: theme.fontSizes[size],
|
|
||||||
'&:active': {
|
|
||||||
backgroundColor: optionBgActive[colorMode],
|
|
||||||
color: optionBgColor,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
indicatorSeparator: base => ({
|
|
||||||
...base,
|
|
||||||
backgroundColor: placeholderColor[colorMode],
|
|
||||||
}),
|
|
||||||
dropdownIndicator: base => ({
|
|
||||||
...base,
|
|
||||||
color: placeholderColor[colorMode],
|
|
||||||
'&:hover': {
|
|
||||||
color: color[colorMode],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
valueContainer: base => ({
|
|
||||||
...base,
|
|
||||||
paddingLeft: theme.space[4],
|
|
||||||
paddingRight: theme.space[4],
|
|
||||||
}),
|
|
||||||
multiValue: base => ({
|
|
||||||
...base,
|
|
||||||
backgroundColor: colorSetPrimaryBg[colorMode],
|
|
||||||
}),
|
|
||||||
multiValueLabel: base => ({
|
|
||||||
...base,
|
|
||||||
color: colorSetPrimaryColor,
|
|
||||||
}),
|
|
||||||
multiValueRemove: base => ({
|
|
||||||
...base,
|
|
||||||
color: colorSetPrimaryColor,
|
|
||||||
'&:hover': {
|
|
||||||
color: colorSetPrimaryColor,
|
|
||||||
backgroundColor: 'inherit',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
singleValue: base => ({
|
|
||||||
...base,
|
|
||||||
color: color[colorMode],
|
|
||||||
fontSize: theme.fontSizes[size],
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
placeholder={
|
|
||||||
<Text color={placeholderColor[colorMode]} fontSize={size} fontFamily={theme.fonts.body}>
|
|
||||||
{placeholder}
|
|
||||||
</Text>
|
|
||||||
}
|
|
||||||
{...props}>
|
|
||||||
{children}
|
|
||||||
</Select>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
@@ -1,66 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import {
|
|
||||||
IconButton,
|
|
||||||
Modal,
|
|
||||||
ModalOverlay,
|
|
||||||
ModalContent,
|
|
||||||
ModalHeader,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
useDisclosure,
|
|
||||||
useColorMode,
|
|
||||||
useTheme,
|
|
||||||
} from '@chakra-ui/core';
|
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
|
||||||
import { Markdown } from 'app/components';
|
|
||||||
|
|
||||||
const AnimatedIcon = motion.custom(IconButton);
|
|
||||||
|
|
||||||
export const HelpModal = ({ item, name }) => {
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
const { colors } = useTheme();
|
|
||||||
const { colorMode } = useColorMode();
|
|
||||||
const bg = { light: 'whiteFaded.50', dark: 'blackFaded.800' };
|
|
||||||
const color = { light: 'black', dark: 'white' };
|
|
||||||
const iconColor = {
|
|
||||||
light: colors.primary[500],
|
|
||||||
dark: colors.primary[300],
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<AnimatePresence>
|
|
||||||
<AnimatedIcon
|
|
||||||
initial={{ opacity: 0, scale: 0.3, color: colors.gray[500] }}
|
|
||||||
animate={{ opacity: 1, scale: 1, color: iconColor[colorMode] }}
|
|
||||||
transition={{ duration: 0.2 }}
|
|
||||||
exit={{ opacity: 0, scale: 0.3 }}
|
|
||||||
variantColor="primary"
|
|
||||||
aria-label={`${name}_help`}
|
|
||||||
icon="info-outline"
|
|
||||||
variant="link"
|
|
||||||
size="sm"
|
|
||||||
h="unset"
|
|
||||||
w={3}
|
|
||||||
minW={3}
|
|
||||||
maxW={3}
|
|
||||||
h={3}
|
|
||||||
minH={3}
|
|
||||||
maxH={3}
|
|
||||||
ml={1}
|
|
||||||
mb={1}
|
|
||||||
onClick={onOpen}
|
|
||||||
/>
|
|
||||||
</AnimatePresence>
|
|
||||||
<Modal isOpen={isOpen} onClose={onClose} size="xl">
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent bg={bg[colorMode]} color={color[colorMode]} py={4} borderRadius="md">
|
|
||||||
<ModalHeader>{item.params.title}</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody>
|
|
||||||
<Markdown content={item.content} />
|
|
||||||
</ModalBody>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@@ -1,46 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
|
||||||
import { Layout, HyperglassForm, Results } from 'app/components';
|
|
||||||
import { useHyperglassState } from 'app/context';
|
|
||||||
|
|
||||||
const AnimatedForm = motion.custom(HyperglassForm);
|
|
||||||
|
|
||||||
export const LookingGlass = () => {
|
|
||||||
const {
|
|
||||||
isSubmitting,
|
|
||||||
setSubmitting,
|
|
||||||
formData,
|
|
||||||
setFormData,
|
|
||||||
greetingAck,
|
|
||||||
setGreetingAck,
|
|
||||||
} = useHyperglassState();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
{isSubmitting && formData && (
|
|
||||||
<Results
|
|
||||||
queryLocation={formData.query_location}
|
|
||||||
queryType={formData.query_type}
|
|
||||||
queryVrf={formData.query_vrf}
|
|
||||||
queryTarget={formData.query_target}
|
|
||||||
setSubmitting={setSubmitting}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<AnimatePresence>
|
|
||||||
{!isSubmitting && (
|
|
||||||
<AnimatedForm
|
|
||||||
initial={{ opacity: 0, y: 300 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.3 }}
|
|
||||||
exit={{ opacity: 0, x: -300 }}
|
|
||||||
isSubmitting={isSubmitting}
|
|
||||||
setSubmitting={setSubmitting}
|
|
||||||
setFormData={setFormData}
|
|
||||||
greetingAck={greetingAck}
|
|
||||||
setGreetingAck={setGreetingAck}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
};
|
|
@@ -1,41 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { ChakraSelect } from 'app/components';
|
|
||||||
|
|
||||||
const buildLocations = networks => {
|
|
||||||
const locations = [];
|
|
||||||
networks.map(net => {
|
|
||||||
const netLocations = [];
|
|
||||||
net.locations.map(loc => {
|
|
||||||
netLocations.push({
|
|
||||||
label: loc.display_name,
|
|
||||||
value: loc.name,
|
|
||||||
group: net.display_name,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
locations.push({ label: net.display_name, options: netLocations });
|
|
||||||
});
|
|
||||||
return locations;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const QueryLocation = ({ locations, onChange, label }) => {
|
|
||||||
const options = buildLocations(locations);
|
|
||||||
const handleChange = e => {
|
|
||||||
const selected = [];
|
|
||||||
e &&
|
|
||||||
e.map(sel => {
|
|
||||||
selected.push(sel.value);
|
|
||||||
});
|
|
||||||
onChange({ field: 'query_location', value: selected });
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<ChakraSelect
|
|
||||||
isMulti
|
|
||||||
size="lg"
|
|
||||||
options={options}
|
|
||||||
aria-label={label}
|
|
||||||
name="query_location"
|
|
||||||
onChange={handleChange}
|
|
||||||
closeMenuOnSelect={false}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@@ -1,115 +0,0 @@
|
|||||||
/** @jsx jsx */
|
|
||||||
import { jsx } from '@emotion/core';
|
|
||||||
import { forwardRef } from 'react';
|
|
||||||
import { Button, Heading, Image, Stack, useColorMode } from '@chakra-ui/core';
|
|
||||||
import { useConfig, useMedia } from 'app/context';
|
|
||||||
|
|
||||||
const titleSize = { true: ['2xl', '2xl', '5xl', '5xl'], false: '2xl' };
|
|
||||||
const titleMargin = { true: 2, false: 0 };
|
|
||||||
const textAlignment = { false: ['right', 'center'], true: ['left', 'center'] };
|
|
||||||
const logoName = { light: 'dark', dark: 'light' };
|
|
||||||
const justifyMap = {
|
|
||||||
true: ['flex-end', 'center', 'center', 'center'],
|
|
||||||
false: ['flex-start', 'center', 'center', 'center'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const logoFallback = {
|
|
||||||
light: 'https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-dark.svg',
|
|
||||||
dark: 'https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-light.svg',
|
|
||||||
};
|
|
||||||
|
|
||||||
const TitleOnly = ({ text, showSubtitle }) => (
|
|
||||||
<Heading as="h1" mb={titleMargin[showSubtitle]} fontSize={titleSize[showSubtitle]}>
|
|
||||||
{text}
|
|
||||||
</Heading>
|
|
||||||
);
|
|
||||||
|
|
||||||
const SubtitleOnly = ({ text, mediaSize, ...props }) => (
|
|
||||||
<Heading
|
|
||||||
as="h3"
|
|
||||||
fontSize={['md', 'md', 'xl', 'xl']}
|
|
||||||
whiteSpace="break-spaces"
|
|
||||||
textAlign={['left', 'left', 'center', 'center']}
|
|
||||||
{...props}>
|
|
||||||
{text}
|
|
||||||
</Heading>
|
|
||||||
);
|
|
||||||
|
|
||||||
const TextOnly = ({ text, mediaSize, showSubtitle, ...props }) => (
|
|
||||||
<Stack spacing={2} maxW="100%" textAlign={textAlignment[showSubtitle]} {...props}>
|
|
||||||
<TitleOnly text={text.title} showSubtitle={showSubtitle} />
|
|
||||||
{showSubtitle && <SubtitleOnly text={text.subtitle} mediaSize={mediaSize} />}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Logo = ({ text, logo }) => {
|
|
||||||
const { colorMode } = useColorMode();
|
|
||||||
const { width, dark_format, light_format } = logo;
|
|
||||||
const logoExt = { light: dark_format, dark: light_format };
|
|
||||||
return (
|
|
||||||
<Image
|
|
||||||
css={{
|
|
||||||
userDrag: 'none',
|
|
||||||
userSelect: 'none',
|
|
||||||
msUserSelect: 'none',
|
|
||||||
MozUserSelect: 'none',
|
|
||||||
WebkitUserDrag: 'none',
|
|
||||||
WebkitUserSelect: 'none',
|
|
||||||
}}
|
|
||||||
alt={text.title}
|
|
||||||
width={width ?? 'auto'}
|
|
||||||
fallbackSrc={logoFallback[colorMode]}
|
|
||||||
src={`/images/${logoName[colorMode]}${logoExt[colorMode]}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const LogoSubtitle = ({ text, logo, mediaSize }) => (
|
|
||||||
<>
|
|
||||||
<Logo text={text} logo={logo} mediaSize={mediaSize} />
|
|
||||||
<SubtitleOnly mt={6} text={text.subtitle} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
const All = ({ text, logo, mediaSize, showSubtitle }) => (
|
|
||||||
<>
|
|
||||||
<Logo text={text} logo={logo} />
|
|
||||||
<TextOnly mediaSize={mediaSize} showSubtitle={showSubtitle} mt={2} text={text} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
const modeMap = {
|
|
||||||
text_only: TextOnly,
|
|
||||||
logo_only: Logo,
|
|
||||||
logo_subtitle: LogoSubtitle,
|
|
||||||
all: All,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Title = forwardRef(({ onClick, isSubmitting, ...props }, ref) => {
|
|
||||||
const { web } = useConfig();
|
|
||||||
const { mediaSize } = useMedia();
|
|
||||||
const titleMode = web.text.title_mode;
|
|
||||||
const MatchedMode = modeMap[titleMode];
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
px={0}
|
|
||||||
w="100%"
|
|
||||||
ref={ref}
|
|
||||||
variant="link"
|
|
||||||
flexWrap="wrap"
|
|
||||||
flexDir="column"
|
|
||||||
onClick={onClick}
|
|
||||||
_focus={{ boxShadow: 'none' }}
|
|
||||||
_hover={{ textDecoration: 'none' }}
|
|
||||||
justifyContent={justifyMap[isSubmitting]}
|
|
||||||
alignItems={['flex-start', 'flex-start', 'center']}
|
|
||||||
{...props}>
|
|
||||||
<MatchedMode
|
|
||||||
mediaSize={mediaSize}
|
|
||||||
showSubtitle={!isSubmitting}
|
|
||||||
text={web.text}
|
|
||||||
logo={web.logo}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
});
|
|
46
hyperglass/ui/components/form/queryLocation.tsx
Normal file
46
hyperglass/ui/components/form/queryLocation.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { Select } from '~/components';
|
||||||
|
|
||||||
|
import type { TNetwork } from '~/types';
|
||||||
|
import type { TQueryLocation, OnChangeArgs } from './types';
|
||||||
|
|
||||||
|
function isOnChangeArgsArray(e: OnChangeArgs | OnChangeArgs[]): e is OnChangeArgs[] {
|
||||||
|
return Array.isArray(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOptions(networks: TNetwork[]) {
|
||||||
|
return networks.map(net => {
|
||||||
|
const label = net.display_name;
|
||||||
|
const options = net.locations.map(loc => ({
|
||||||
|
label: loc.display_name,
|
||||||
|
value: loc.name,
|
||||||
|
group: net.display_name,
|
||||||
|
}));
|
||||||
|
return { label, options };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const QueryLocation = (props: TQueryLocation) => {
|
||||||
|
const { locations, onChange, label } = props;
|
||||||
|
|
||||||
|
const options = useMemo(() => buildOptions(locations), [locations.length]);
|
||||||
|
|
||||||
|
function handleChange(e: OnChangeArgs | OnChangeArgs[]): void {
|
||||||
|
if (isOnChangeArgsArray(e)) {
|
||||||
|
const value = e.map(sel => sel.value as string);
|
||||||
|
onChange({ label: 'query_location', value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
isMulti
|
||||||
|
size="lg"
|
||||||
|
options={options}
|
||||||
|
aria-label={label}
|
||||||
|
name="query_location"
|
||||||
|
onChange={handleChange}
|
||||||
|
closeMenuOnSelect={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@@ -1,5 +1,6 @@
|
|||||||
import type { FormControlProps } from '@chakra-ui/react';
|
import type { FormControlProps } from '@chakra-ui/react';
|
||||||
import type { FieldError } from 'react-hook-form';
|
import type { FieldError } from 'react-hook-form';
|
||||||
|
import type { TNetwork } from '~/types';
|
||||||
|
|
||||||
export interface TField extends FormControlProps {
|
export interface TField extends FormControlProps {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -9,3 +10,11 @@ export interface TField extends FormControlProps {
|
|||||||
labelAddOn?: React.ReactNode;
|
labelAddOn?: React.ReactNode;
|
||||||
fieldAddOn?: React.ReactNode;
|
fieldAddOn?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OnChangeArgs = { label: string; value: string | string[] };
|
||||||
|
|
||||||
|
export interface TQueryLocation {
|
||||||
|
locations: TNetwork[];
|
||||||
|
onChange(f: OnChangeArgs | OnChangeArgs[]): void;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
1
hyperglass/ui/components/help/index.ts
Normal file
1
hyperglass/ui/components/help/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './modal';
|
63
hyperglass/ui/components/help/modal.tsx
Normal file
63
hyperglass/ui/components/help/modal.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
import {
|
||||||
|
IconButton,
|
||||||
|
Modal,
|
||||||
|
ModalOverlay,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
useDisclosure,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import { Markdown } from '~/components';
|
||||||
|
import { useColorValue } from '~/context';
|
||||||
|
|
||||||
|
import type { THelpModal } from './types';
|
||||||
|
|
||||||
|
const Info = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiInfo));
|
||||||
|
|
||||||
|
export const HelpModal = (props: THelpModal) => {
|
||||||
|
const { item, name, ...rest } = props;
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
const bg = useColorValue('whiteFaded.50', 'blackFaded.800');
|
||||||
|
const color = useColorValue('black', 'white');
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AnimatePresence>
|
||||||
|
<motion.div
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.3 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
initial={{ opacity: 0, scale: 0.3 }}>
|
||||||
|
<IconButton
|
||||||
|
h={3}
|
||||||
|
w={3}
|
||||||
|
mb={1}
|
||||||
|
ml={1}
|
||||||
|
maxH={3}
|
||||||
|
maxW={3}
|
||||||
|
minH={3}
|
||||||
|
minW={3}
|
||||||
|
size="sm"
|
||||||
|
variant="link"
|
||||||
|
icon={<Info />}
|
||||||
|
onClick={onOpen}
|
||||||
|
colorScheme="primary"
|
||||||
|
aria-label={`${name}_help`}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose} size="xl">
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent bg={bg} color={color} py={4} borderRadius="md" {...rest}>
|
||||||
|
<ModalHeader>{item.params.title}</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody>
|
||||||
|
<Markdown content={item.content} />
|
||||||
|
</ModalBody>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
7
hyperglass/ui/components/help/types.ts
Normal file
7
hyperglass/ui/components/help/types.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { ModalContentProps } from '@chakra-ui/react';
|
||||||
|
import type { TQueryContent, TQueryFields } from '~/types';
|
||||||
|
|
||||||
|
export interface THelpModal extends ModalContentProps {
|
||||||
|
item: TQueryContent;
|
||||||
|
name: TQueryFields;
|
||||||
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
export * from './buttons';
|
export * from './buttons';
|
||||||
export * from './card';
|
export * from './card';
|
||||||
export * from './ChakraSelect';
|
|
||||||
export * from './codeBlock';
|
export * from './codeBlock';
|
||||||
export * from './CommunitySelect';
|
export * from './CommunitySelect';
|
||||||
export * from './countdown';
|
export * from './countdown';
|
||||||
@@ -9,21 +8,20 @@ export * from './footer';
|
|||||||
export * from './form';
|
export * from './form';
|
||||||
export * from './greeting';
|
export * from './greeting';
|
||||||
export * from './header';
|
export * from './header';
|
||||||
export * from './HelpModal';
|
export * from './help';
|
||||||
export * from './HyperglassForm';
|
export * from './HyperglassForm';
|
||||||
export * from './label';
|
export * from './label';
|
||||||
export * from './layout';
|
export * from './layout';
|
||||||
export * from './loading';
|
export * from './loading';
|
||||||
export * from './LookingGlass';
|
|
||||||
export * from './markdown';
|
export * from './markdown';
|
||||||
export * from './meta';
|
export * from './meta';
|
||||||
export * from './output';
|
export * from './output';
|
||||||
export * from './QueryLocation';
|
|
||||||
export * from './QueryTarget';
|
export * from './QueryTarget';
|
||||||
export * from './QueryType';
|
export * from './QueryType';
|
||||||
export * from './QueryVrf';
|
export * from './QueryVrf';
|
||||||
export * from './ResolvedTarget';
|
export * from './ResolvedTarget';
|
||||||
export * from './results';
|
export * from './results';
|
||||||
|
export * from './select';
|
||||||
export * from './table';
|
export * from './table';
|
||||||
export * from './Title';
|
export * from './title';
|
||||||
export * from './util';
|
export * from './util';
|
||||||
|
@@ -4,7 +4,9 @@ import { useConfig, useColorValue, useGlobalState } from '~/context';
|
|||||||
import { If, Debugger, Greeting, Footer, Header } from '~/components';
|
import { If, Debugger, Greeting, Footer, Header } from '~/components';
|
||||||
import { useGreeting } from '~/hooks';
|
import { useGreeting } from '~/hooks';
|
||||||
|
|
||||||
export const Layout: React.FC = props => {
|
import type { TFrame } from './types';
|
||||||
|
|
||||||
|
export const Frame = (props: TFrame) => {
|
||||||
const { web, developer_mode } = useConfig();
|
const { web, developer_mode } = useConfig();
|
||||||
const { isSubmitting, formData } = useGlobalState();
|
const { isSubmitting, formData } = useGlobalState();
|
||||||
const [greetingAck, setGreetingAck] = useGreeting();
|
const [greetingAck, setGreetingAck] = useGreeting();
|
||||||
@@ -27,9 +29,10 @@ export const Layout: React.FC = props => {
|
|||||||
bg={bg}
|
bg={bg}
|
||||||
w="100%"
|
w="100%"
|
||||||
color={color}
|
color={color}
|
||||||
|
flexDir="column"
|
||||||
minHeight="100vh"
|
minHeight="100vh"
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
flexDirection="column">
|
{...props}>
|
||||||
<Flex px={2} flex="0 1 auto" flexDirection="column">
|
<Flex px={2} flex="0 1 auto" flexDirection="column">
|
||||||
<Header resetForm={resetForm} />
|
<Header resetForm={resetForm} />
|
||||||
</Flex>
|
</Flex>
|
2
hyperglass/ui/components/layout/index.ts
Normal file
2
hyperglass/ui/components/layout/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './frame';
|
||||||
|
export * from './layout';
|
42
hyperglass/ui/components/layout/layout.tsx
Normal file
42
hyperglass/ui/components/layout/layout.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import { If, HyperglassForm, Results } from '~/components';
|
||||||
|
import { useGlobalState } from '~/context';
|
||||||
|
import { all } from '~/util';
|
||||||
|
import { Frame } from './frame';
|
||||||
|
|
||||||
|
export const Layout: React.FC = () => {
|
||||||
|
const { isSubmitting, formData } = useGlobalState();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Frame>
|
||||||
|
<If
|
||||||
|
c={
|
||||||
|
isSubmitting.value &&
|
||||||
|
all(
|
||||||
|
formData.query_location.value,
|
||||||
|
formData.query_target.value,
|
||||||
|
formData.query_type.value,
|
||||||
|
formData.query_vrf.value,
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
<Results
|
||||||
|
queryLocation={formData.query_location.value}
|
||||||
|
queryType={formData.query_type.value}
|
||||||
|
queryVrf={formData.query_vrf.value}
|
||||||
|
queryTarget={formData.query_target.value}
|
||||||
|
/>
|
||||||
|
</If>
|
||||||
|
<AnimatePresence>
|
||||||
|
<If c={!isSubmitting.value}>
|
||||||
|
<motion.div
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
exit={{ opacity: 0, x: -300 }}
|
||||||
|
initial={{ opacity: 0, y: 300 }}>
|
||||||
|
<HyperglassForm />
|
||||||
|
</motion.div>
|
||||||
|
</If>
|
||||||
|
</AnimatePresence>
|
||||||
|
</Frame>
|
||||||
|
);
|
||||||
|
};
|
3
hyperglass/ui/components/layout/types.ts
Normal file
3
hyperglass/ui/components/layout/types.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import type { FlexProps } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export interface TFrame extends FlexProps {}
|
@@ -8,7 +8,7 @@ import { Result } from './individual';
|
|||||||
import type { TResults } from './types';
|
import type { TResults } from './types';
|
||||||
|
|
||||||
export const Results = (props: TResults) => {
|
export const Results = (props: TResults) => {
|
||||||
const { queryLocation, queryType, queryVrf, queryTarget, setSubmitting, ...rest } = props;
|
const { queryLocation, queryType, queryVrf, queryTarget, ...rest } = props;
|
||||||
const { request_timeout, devices, queries, vrfs, web } = useConfig();
|
const { request_timeout, devices, queries, vrfs, web } = useConfig();
|
||||||
const targetBg = useToken('colors', 'teal.600');
|
const targetBg = useToken('colors', 'teal.600');
|
||||||
const queryBg = useToken('colors', 'cyan.500');
|
const queryBg = useToken('colors', 'cyan.500');
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { forwardRef, useMemo } from 'react';
|
import { forwardRef, useMemo } from 'react';
|
||||||
import { AccordionIcon, Icon, Spinner, Stack, Text, Tooltip, useColorMode } from '@chakra-ui/react';
|
import { AccordionIcon, Icon, Spinner, Stack, Text, Tooltip } from '@chakra-ui/react';
|
||||||
import { useConfig, useColorValue } from '~/context';
|
import { useConfig, useColorValue } from '~/context';
|
||||||
import { useStrf } from '~/hooks';
|
import { useStrf } from '~/hooks';
|
||||||
|
|
||||||
|
@@ -32,7 +32,6 @@ export interface TResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TResults extends BoxProps {
|
export interface TResults extends BoxProps {
|
||||||
setSubmitting(v: boolean): boolean;
|
|
||||||
queryType: TQueryTypes;
|
queryType: TQueryTypes;
|
||||||
queryLocation: string[];
|
queryLocation: string[];
|
||||||
queryTarget: string;
|
queryTarget: string;
|
||||||
|
1
hyperglass/ui/components/select/index.ts
Normal file
1
hyperglass/ui/components/select/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './select';
|
81
hyperglass/ui/components/select/select.tsx
Normal file
81
hyperglass/ui/components/select/select.tsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { createContext, useContext, useMemo, useState } from 'react';
|
||||||
|
import ReactSelect from 'react-select';
|
||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
|
import { useColorMode } from '~/context';
|
||||||
|
import {
|
||||||
|
useRSTheme,
|
||||||
|
useMenuStyle,
|
||||||
|
useMenuPortal,
|
||||||
|
useOptionStyle,
|
||||||
|
useControlStyle,
|
||||||
|
useMenuListStyle,
|
||||||
|
useMultiValueStyle,
|
||||||
|
usePlaceholderStyle,
|
||||||
|
useSingleValueStyle,
|
||||||
|
useMultiValueLabelStyle,
|
||||||
|
useMultiValueRemoveStyle,
|
||||||
|
useIndicatorSeparatorStyle,
|
||||||
|
} from './styles';
|
||||||
|
|
||||||
|
import type { TSelect, TSelectOption, TSelectContext, TBoxAsReactSelect } from './types';
|
||||||
|
|
||||||
|
const SelectContext = createContext<TSelectContext>(Object());
|
||||||
|
export const useSelectContext = () => useContext(SelectContext);
|
||||||
|
|
||||||
|
const ReactSelectAsBox = (props: TBoxAsReactSelect) => <Box as={ReactSelect} {...props} />;
|
||||||
|
|
||||||
|
export const Select = (props: TSelect) => {
|
||||||
|
const { ctl, options, multi, onSelect, ...rest } = props;
|
||||||
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
|
const selectContext = useMemo<TSelectContext>(() => ({ colorMode, isOpen }), [colorMode, isOpen]);
|
||||||
|
|
||||||
|
const handleChange = (changed: TSelectOption | TSelectOption[]) => {
|
||||||
|
if (!Array.isArray(changed)) {
|
||||||
|
changed = [changed];
|
||||||
|
}
|
||||||
|
if (typeof onSelect === 'function') {
|
||||||
|
onSelect(changed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const multiValue = useMultiValueStyle({ colorMode });
|
||||||
|
const multiValueLabel = useMultiValueLabelStyle({ colorMode });
|
||||||
|
const multiValueRemove = useMultiValueRemoveStyle({ colorMode });
|
||||||
|
const menuPortal = useMenuPortal({ colorMode });
|
||||||
|
const rsTheme = useRSTheme({ colorMode });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SelectContext.Provider value={selectContext}>
|
||||||
|
<ReactSelectAsBox
|
||||||
|
as={ReactSelect}
|
||||||
|
options={options}
|
||||||
|
isMulti={multi}
|
||||||
|
onChange={handleChange}
|
||||||
|
ref={ctl}
|
||||||
|
onMenuClose={() => {
|
||||||
|
isOpen && setIsOpen(false);
|
||||||
|
}}
|
||||||
|
onMenuOpen={() => {
|
||||||
|
!isOpen && setIsOpen(true);
|
||||||
|
}}
|
||||||
|
theme={rsTheme}
|
||||||
|
styles={{
|
||||||
|
menuPortal,
|
||||||
|
multiValue,
|
||||||
|
multiValueLabel,
|
||||||
|
multiValueRemove,
|
||||||
|
menu: useMenuStyle,
|
||||||
|
option: useOptionStyle,
|
||||||
|
control: useControlStyle,
|
||||||
|
menuList: useMenuListStyle,
|
||||||
|
singleValue: useSingleValueStyle,
|
||||||
|
placeholder: usePlaceholderStyle,
|
||||||
|
indicatorSeparator: useIndicatorSeparatorStyle,
|
||||||
|
}}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
</SelectContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
221
hyperglass/ui/components/select/styles.tsx
Normal file
221
hyperglass/ui/components/select/styles.tsx
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { useToken } from '@chakra-ui/react';
|
||||||
|
import { mergeWith } from '@chakra-ui/utils';
|
||||||
|
import { useOpposingColor } from '~/hooks';
|
||||||
|
import { useColorValue, useMobile } from '~/context';
|
||||||
|
import { useSelectContext } from './select';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
TControl,
|
||||||
|
TIndicator,
|
||||||
|
TMenu,
|
||||||
|
TMenuList,
|
||||||
|
TMultiValueState,
|
||||||
|
TOption,
|
||||||
|
TPlaceholder,
|
||||||
|
TStyles,
|
||||||
|
TRSTheme,
|
||||||
|
TMultiValue,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export const useControlStyle = (base: TStyles, state: TControl): TStyles => {
|
||||||
|
const { isFocused } = state;
|
||||||
|
const { colorMode } = useSelectContext();
|
||||||
|
const borderHover = useColorValue(
|
||||||
|
useToken('colors', 'gray.300'),
|
||||||
|
useToken('colors', 'whiteAlpha.200'),
|
||||||
|
);
|
||||||
|
const focusBorder = useToken('colors', 'secondary.500');
|
||||||
|
const invalidBorder = useColorValue(useToken('colors', 'red.500'), useToken('colors', 'red.300'));
|
||||||
|
|
||||||
|
const borderRadius = useToken('radii', 'md');
|
||||||
|
const minHeight = useToken('sizes', 'lg');
|
||||||
|
const color = useColorValue(useToken('colors', 'black'), useToken('colors', 'whiteAlpha.800'));
|
||||||
|
const backgroundColor = useColorValue(
|
||||||
|
useToken('colors', 'white'),
|
||||||
|
useToken('colors', 'whiteAlpha.100'),
|
||||||
|
);
|
||||||
|
const styles = {
|
||||||
|
backgroundColor,
|
||||||
|
borderRadius,
|
||||||
|
color,
|
||||||
|
minHeight,
|
||||||
|
'&:hover': { borderColor: isFocused ? focusBorder : borderHover },
|
||||||
|
'&:hover > div > span': { backgroundColor: borderHover },
|
||||||
|
|
||||||
|
'&.invalid': { borderColor: invalidBorder, boxShadow: `0 0 0 1px ${invalidBorder}` },
|
||||||
|
};
|
||||||
|
return useMemo(() => mergeWith({}, base, styles), [colorMode, isFocused]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMenuStyle = (base: TStyles, state: TMenu): TStyles => {
|
||||||
|
const { colorMode, isOpen } = useSelectContext();
|
||||||
|
const backgroundColor = useColorValue(
|
||||||
|
useToken('colors', 'white'),
|
||||||
|
useToken('colors', 'whiteFaded.50'),
|
||||||
|
);
|
||||||
|
const borderRadius = useToken('radii', 'md');
|
||||||
|
const styles = { borderRadius, backgroundColor };
|
||||||
|
return useMemo(() => mergeWith({}, base, styles), [colorMode, isOpen]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMenuListStyle = (base: TStyles, state: TMenuList): TStyles => {
|
||||||
|
const { colorMode, isOpen } = useSelectContext();
|
||||||
|
|
||||||
|
const scrollbarTrack = useColorValue(
|
||||||
|
useToken('colors', 'blackAlpha.50'),
|
||||||
|
useToken('colors', 'whiteAlpha.50'),
|
||||||
|
);
|
||||||
|
const scrollbarThumb = useColorValue(
|
||||||
|
useToken('colors', 'blackAlpha.300'),
|
||||||
|
useToken('colors', 'whiteAlpha.300'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const scrollbarThumbHover = useColorValue(
|
||||||
|
useToken('colors', 'blackAlpha.400'),
|
||||||
|
useToken('colors', 'whiteAlpha.400'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
'&::-webkit-scrollbar': { width: '5px' },
|
||||||
|
'&::-webkit-scrollbar-track': { backgroundColor: scrollbarTrack },
|
||||||
|
'&::-webkit-scrollbar-thumb': { backgroundColor: scrollbarThumb },
|
||||||
|
'&::-webkit-scrollbar-thumb:hover': { backgroundColor: scrollbarThumbHover },
|
||||||
|
'-ms-overflow-style': { display: 'none' },
|
||||||
|
};
|
||||||
|
return useMemo(() => mergeWith({}, base, styles), [colorMode, isOpen]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useOptionStyle = (base: TStyles, state: TOption): TStyles => {
|
||||||
|
const { isFocused } = state;
|
||||||
|
const { colorMode, isOpen } = useSelectContext();
|
||||||
|
const fontSize = useToken('fontSizes', 'lg');
|
||||||
|
const disabled = useToken('colors', 'whiteAlpha.400');
|
||||||
|
const selected = useColorValue(
|
||||||
|
useToken('colors', 'blackAlpha.400'),
|
||||||
|
useToken('colors', 'whiteAlpha.400'),
|
||||||
|
);
|
||||||
|
const focused = useColorValue(
|
||||||
|
useToken('colors', 'primary.500'),
|
||||||
|
useToken('colors', 'primary.300'),
|
||||||
|
);
|
||||||
|
const active = useColorValue(
|
||||||
|
useToken('colors', 'primary.600'),
|
||||||
|
useToken('colors', 'primary.400'),
|
||||||
|
);
|
||||||
|
const disabledColor = useOpposingColor(disabled);
|
||||||
|
const selectedColor = useOpposingColor(selected);
|
||||||
|
const focusedColor = useOpposingColor(focused);
|
||||||
|
const activeColor = useOpposingColor(active);
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
backgroundColor: state.isDisabled
|
||||||
|
? disabled
|
||||||
|
: state.isSelected
|
||||||
|
? selected
|
||||||
|
: state.isFocused
|
||||||
|
? focused
|
||||||
|
: 'transparent',
|
||||||
|
color: state.isDisabled
|
||||||
|
? disabledColor
|
||||||
|
: state.isSelected
|
||||||
|
? selectedColor
|
||||||
|
: state.isFocused
|
||||||
|
? focusedColor
|
||||||
|
: 'transparent',
|
||||||
|
fontSize,
|
||||||
|
'&:focus': { backgroundColor: active, color: activeColor },
|
||||||
|
'&:active': { backgroundColor: active, color: activeColor },
|
||||||
|
};
|
||||||
|
return useMemo(() => mergeWith({}, base, styles), [colorMode, isFocused, isOpen]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useIndicatorSeparatorStyle = (base: TStyles, state: TIndicator): TStyles => {
|
||||||
|
const { colorMode } = useSelectContext();
|
||||||
|
const backgroundColor = useColorValue(
|
||||||
|
useToken('colors', 'whiteAlpha.700'),
|
||||||
|
useToken('colors', 'gray.600'),
|
||||||
|
);
|
||||||
|
const styles = { backgroundColor };
|
||||||
|
return useMemo(() => mergeWith({}, base, styles), [colorMode]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePlaceholderStyle = (base: TStyles, state: TPlaceholder): TStyles => {
|
||||||
|
const { colorMode } = useSelectContext();
|
||||||
|
const color = useColorValue(useToken('colors', 'whiteAlpha.700'), useToken('colors', 'gray.600'));
|
||||||
|
return useMemo(() => mergeWith({}, base, { color }), [colorMode]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSingleValueStyle = (props: TStyles) => {
|
||||||
|
const { colorMode } = useSelectContext();
|
||||||
|
|
||||||
|
const color = useColorValue('black', 'whiteAlpha.800');
|
||||||
|
const fontSize = useToken('fontSizes', 'lg');
|
||||||
|
|
||||||
|
const styles = { color, fontSize };
|
||||||
|
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [
|
||||||
|
color,
|
||||||
|
colorMode,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMultiValueStyle = (props: TMultiValue) => {
|
||||||
|
const { colorMode } = props;
|
||||||
|
|
||||||
|
const backgroundColor = useColorValue(
|
||||||
|
useToken('colors', 'primary.500'),
|
||||||
|
useToken('colors', 'primary.300'),
|
||||||
|
);
|
||||||
|
const color = useOpposingColor(backgroundColor);
|
||||||
|
const styles = { backgroundColor, color };
|
||||||
|
|
||||||
|
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [
|
||||||
|
backgroundColor,
|
||||||
|
colorMode,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMultiValueLabelStyle = (props: TMultiValue) => {
|
||||||
|
const { colorMode } = props;
|
||||||
|
const backgroundColor = useColorValue(
|
||||||
|
useToken('colors', 'primary.500'),
|
||||||
|
useToken('colors', 'primary.300'),
|
||||||
|
);
|
||||||
|
const color = useOpposingColor(backgroundColor);
|
||||||
|
const styles = { color };
|
||||||
|
|
||||||
|
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [
|
||||||
|
colorMode,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMultiValueRemoveStyle = (props: TMultiValue) => {
|
||||||
|
const { colorMode } = props;
|
||||||
|
const backgroundColor = useColorValue(
|
||||||
|
useToken('colors', 'primary.500'),
|
||||||
|
useToken('colors', 'primary.300'),
|
||||||
|
);
|
||||||
|
const color = useOpposingColor(backgroundColor);
|
||||||
|
const styles = {
|
||||||
|
color,
|
||||||
|
'&:hover': { backgroundColor: 'inherit', color, opacity: 0.7 },
|
||||||
|
};
|
||||||
|
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [
|
||||||
|
colorMode,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRSTheme = (props: TMultiValue) => {
|
||||||
|
const borderRadius = useToken('radii', 'md');
|
||||||
|
return useCallback((t: TRSTheme): TRSTheme => ({ ...t, borderRadius }), []);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMenuPortal = (props: TMultiValue) => {
|
||||||
|
const isMobile = useMobile();
|
||||||
|
const styles = {
|
||||||
|
zIndex: isMobile ? 1500 : 1,
|
||||||
|
};
|
||||||
|
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [
|
||||||
|
isMobile,
|
||||||
|
]);
|
||||||
|
};
|
81
hyperglass/ui/components/select/types.ts
Normal file
81
hyperglass/ui/components/select/types.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import type {
|
||||||
|
Props as IReactSelect,
|
||||||
|
ControlProps,
|
||||||
|
MenuProps,
|
||||||
|
MenuListComponentProps,
|
||||||
|
OptionProps,
|
||||||
|
MultiValueProps,
|
||||||
|
IndicatorProps,
|
||||||
|
Theme,
|
||||||
|
PlaceholderProps,
|
||||||
|
} from 'react-select';
|
||||||
|
import type { BoxProps } from '@chakra-ui/react';
|
||||||
|
import type { ColorNames } from '~/types';
|
||||||
|
|
||||||
|
export interface TSelectState {
|
||||||
|
[k: string]: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSelectOption = {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TSelectOptionGroup = {
|
||||||
|
label: string;
|
||||||
|
options: TSelectOption[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TOptions = Array<TSelectOptionGroup | TSelectOption>;
|
||||||
|
|
||||||
|
export type TBoxAsReactSelect = Omit<IReactSelect, 'isMulti' | 'onSelect' | 'onChange'> &
|
||||||
|
Omit<BoxProps, 'onChange' | 'onSelect'>;
|
||||||
|
|
||||||
|
export interface TSelect extends TBoxAsReactSelect {
|
||||||
|
options: TOptions;
|
||||||
|
name: string;
|
||||||
|
required?: boolean;
|
||||||
|
multi?: boolean;
|
||||||
|
onSelect?: (v: TSelectOption[]) => void;
|
||||||
|
onChange?: (c: TSelectOption | TSelectOption[]) => void;
|
||||||
|
colorScheme?: ColorNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSelectContext {
|
||||||
|
colorMode: 'light' | 'dark';
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TMultiValueRemoveProps {
|
||||||
|
children: Node;
|
||||||
|
data: any;
|
||||||
|
innerProps: {
|
||||||
|
className: string;
|
||||||
|
onTouchEnd: (e: any) => void;
|
||||||
|
onClick: (e: any) => void;
|
||||||
|
onMouseDown: (e: any) => void;
|
||||||
|
};
|
||||||
|
selectProps: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TRSTheme extends Omit<Theme, 'borderRadius'> {
|
||||||
|
borderRadius: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TControl = ControlProps<TOptions>;
|
||||||
|
|
||||||
|
export type TMenu = MenuProps<TOptions>;
|
||||||
|
|
||||||
|
export type TMenuList = MenuListComponentProps<TOptions>;
|
||||||
|
|
||||||
|
export type TOption = OptionProps<TOptions>;
|
||||||
|
|
||||||
|
export type TMultiValueState = MultiValueProps<TOptions>;
|
||||||
|
|
||||||
|
export type TIndicator = IndicatorProps<TOptions>;
|
||||||
|
|
||||||
|
export type TPlaceholder = PlaceholderProps<TOptions>;
|
||||||
|
|
||||||
|
export type TMultiValue = Pick<TSelectContext, 'colorMode'>;
|
||||||
|
|
||||||
|
export type { Styles as TStyles } from 'react-select';
|
1
hyperglass/ui/components/title/index.ts
Normal file
1
hyperglass/ui/components/title/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './title';
|
33
hyperglass/ui/components/title/logo.tsx
Normal file
33
hyperglass/ui/components/title/logo.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Image } from '@chakra-ui/react';
|
||||||
|
import { useColorValue, useConfig } from '~/context';
|
||||||
|
|
||||||
|
import type { TLogo } from './types';
|
||||||
|
|
||||||
|
export const Logo = (props: TLogo) => {
|
||||||
|
const { web } = useConfig();
|
||||||
|
const { width, dark_format, light_format } = web.logo;
|
||||||
|
|
||||||
|
const src = useColorValue(`/images/dark${dark_format}`, `/images/light${light_format}`);
|
||||||
|
const fallbackSrc = useColorValue(
|
||||||
|
'https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-dark.svg',
|
||||||
|
'https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-light.svg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
css={{
|
||||||
|
userDrag: 'none',
|
||||||
|
userSelect: 'none',
|
||||||
|
msUserSelect: 'none',
|
||||||
|
MozUserSelect: 'none',
|
||||||
|
WebkitUserDrag: 'none',
|
||||||
|
WebkitUserSelect: 'none',
|
||||||
|
}}
|
||||||
|
fallbackSrc={fallbackSrc}
|
||||||
|
width={width ?? 'auto'}
|
||||||
|
alt={web.text.title}
|
||||||
|
src={src}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
18
hyperglass/ui/components/title/subtitleOnly.tsx
Normal file
18
hyperglass/ui/components/title/subtitleOnly.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Heading } from '@chakra-ui/react';
|
||||||
|
import { useConfig } from '~/context';
|
||||||
|
|
||||||
|
import type { TSubtitleOnly } from './types';
|
||||||
|
|
||||||
|
export const SubtitleOnly = (props: TSubtitleOnly) => {
|
||||||
|
const { web } = useConfig();
|
||||||
|
return (
|
||||||
|
<Heading
|
||||||
|
as="h3"
|
||||||
|
whiteSpace="break-spaces"
|
||||||
|
fontSize={{ base: 'md', lg: 'xl' }}
|
||||||
|
textAlign={{ base: 'left', xl: 'center' }}
|
||||||
|
{...props}>
|
||||||
|
{web.text.subtitle}
|
||||||
|
</Heading>
|
||||||
|
);
|
||||||
|
};
|
85
hyperglass/ui/components/title/title.tsx
Normal file
85
hyperglass/ui/components/title/title.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { forwardRef } from 'react';
|
||||||
|
import { Button, Stack } from '@chakra-ui/react';
|
||||||
|
import { If } from '~/components';
|
||||||
|
import { useConfig, useGlobalState } from '~/context';
|
||||||
|
import { useBooleanValue } from '~/hooks';
|
||||||
|
import { TitleOnly } from './titleOnly';
|
||||||
|
import { SubtitleOnly } from './subtitleOnly';
|
||||||
|
import { Logo } from './logo';
|
||||||
|
|
||||||
|
import type { TTitle, TTextOnly } from './types';
|
||||||
|
|
||||||
|
const TextOnly = (props: TTextOnly) => {
|
||||||
|
const { showSubtitle, ...rest } = props;
|
||||||
|
const textAlign = useBooleanValue(
|
||||||
|
showSubtitle,
|
||||||
|
{ base: 'right', md: 'center' },
|
||||||
|
{ base: 'left', md: 'center' },
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Stack spacing={2} maxW="100%" textAlign={textAlign} {...rest}>
|
||||||
|
<TitleOnly showSubtitle={showSubtitle} />
|
||||||
|
<If c={showSubtitle}>
|
||||||
|
<SubtitleOnly />
|
||||||
|
</If>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LogoSubtitle = () => (
|
||||||
|
<>
|
||||||
|
<Logo />
|
||||||
|
<SubtitleOnly mt={6} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const All = (props: TTextOnly) => {
|
||||||
|
const { showSubtitle, ...rest } = props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Logo />
|
||||||
|
<TextOnly showSubtitle={showSubtitle} mt={2} {...rest} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Title = forwardRef<HTMLButtonElement, TTitle>((props, ref) => {
|
||||||
|
const { web } = useConfig();
|
||||||
|
const titleMode = web.text.title_mode;
|
||||||
|
|
||||||
|
const { isSubmitting } = useGlobalState();
|
||||||
|
|
||||||
|
const justify = useBooleanValue(
|
||||||
|
isSubmitting.value,
|
||||||
|
{ base: 'flex-end', md: 'center' },
|
||||||
|
{ base: 'flex-start', md: 'center' },
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
px={0}
|
||||||
|
w="100%"
|
||||||
|
ref={ref}
|
||||||
|
variant="link"
|
||||||
|
flexWrap="wrap"
|
||||||
|
flexDir="column"
|
||||||
|
justifyContent={justify}
|
||||||
|
_focus={{ boxShadow: 'none' }}
|
||||||
|
_hover={{ textDecoration: 'none' }}
|
||||||
|
alignItems={{ base: 'flex-start', lg: 'center' }}
|
||||||
|
{...props}>
|
||||||
|
<If c={titleMode === 'text_only'}>
|
||||||
|
<TextOnly showSubtitle={!isSubmitting.value} />
|
||||||
|
</If>
|
||||||
|
<If c={titleMode === 'logo_only'}>
|
||||||
|
<Logo />
|
||||||
|
</If>
|
||||||
|
<If c={titleMode === 'logo_subtitle'}>
|
||||||
|
<LogoSubtitle />
|
||||||
|
</If>
|
||||||
|
<If c={titleMode === 'all'}>
|
||||||
|
<All showSubtitle={!isSubmitting.value} />
|
||||||
|
</If>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
17
hyperglass/ui/components/title/titleOnly.tsx
Normal file
17
hyperglass/ui/components/title/titleOnly.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Heading } from '@chakra-ui/react';
|
||||||
|
import { useConfig } from '~/context';
|
||||||
|
import { useBooleanValue } from '~/hooks';
|
||||||
|
|
||||||
|
import type { TTitleOnly } from './types';
|
||||||
|
|
||||||
|
export const TitleOnly = (props: TTitleOnly) => {
|
||||||
|
const { showSubtitle, ...rest } = props;
|
||||||
|
const { web } = useConfig();
|
||||||
|
const fontSize = useBooleanValue(showSubtitle, { base: '2xl', lg: '5xl' }, '2xl');
|
||||||
|
const margin = useBooleanValue(showSubtitle, 2, 0);
|
||||||
|
return (
|
||||||
|
<Heading as="h1" mb={margin} fontSize={fontSize} {...rest}>
|
||||||
|
{web.text.title}
|
||||||
|
</Heading>
|
||||||
|
);
|
||||||
|
};
|
15
hyperglass/ui/components/title/types.ts
Normal file
15
hyperglass/ui/components/title/types.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { ButtonProps, HeadingProps, ImageProps, StackProps } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export interface TTitle extends ButtonProps {}
|
||||||
|
|
||||||
|
export interface TTitleOnly extends HeadingProps {
|
||||||
|
showSubtitle: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSubtitleOnly extends HeadingProps {}
|
||||||
|
|
||||||
|
export interface TLogo extends ImageProps {}
|
||||||
|
|
||||||
|
export interface TTextOnly extends StackProps {
|
||||||
|
showSubtitle: boolean;
|
||||||
|
}
|
@@ -1,41 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { createContext, useContext, useMemo } from 'react';
|
|
||||||
import { useMediaLayout } from 'use-media';
|
|
||||||
|
|
||||||
const MediaContext = createContext(null);
|
|
||||||
|
|
||||||
export const MediaProvider = ({ theme, children }) => {
|
|
||||||
const { sm, md, lg, xl } = theme.breakpoints;
|
|
||||||
const isSm = useMediaLayout({ maxWidth: md });
|
|
||||||
const isMd = useMediaLayout({ minWidth: md, maxWidth: lg });
|
|
||||||
const isLg = useMediaLayout({ minWidth: lg, maxWidth: xl });
|
|
||||||
const isXl = useMediaLayout({ minWidth: xl });
|
|
||||||
let mediaSize = false;
|
|
||||||
switch (true) {
|
|
||||||
case isSm:
|
|
||||||
mediaSize = 'sm';
|
|
||||||
break;
|
|
||||||
case isMd:
|
|
||||||
mediaSize = 'md';
|
|
||||||
break;
|
|
||||||
case isLg:
|
|
||||||
mediaSize = 'lg';
|
|
||||||
break;
|
|
||||||
case isXl:
|
|
||||||
mediaSize = 'xl';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const value = useMemo(
|
|
||||||
() => ({
|
|
||||||
isSm: isSm,
|
|
||||||
isMd: isMd,
|
|
||||||
isLg: isLg,
|
|
||||||
isXl: isXl,
|
|
||||||
mediaSize: mediaSize,
|
|
||||||
}),
|
|
||||||
[isSm, isMd, isLg, isXl, mediaSize],
|
|
||||||
);
|
|
||||||
return <MediaContext.Provider value={value}>{children}</MediaContext.Provider>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMedia = () => useContext(MediaContext);
|
|
@@ -2,12 +2,10 @@ import * as React from 'react';
|
|||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { Meta, Loading } from 'app/components';
|
import { Meta, Loading } from 'app/components';
|
||||||
const LookingGlass = dynamic(
|
|
||||||
() => import('app/components/LookingGlass').then(i => i.LookingGlass),
|
const Layout = dynamic(() => import('~/components').then(i => i.Layout), {
|
||||||
{
|
loading: Loading,
|
||||||
loading: Loading,
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const Index = ({ faviconComponents }) => {
|
const Index = ({ faviconComponents }) => {
|
||||||
return (
|
return (
|
||||||
@@ -18,7 +16,7 @@ const Index = ({ faviconComponents }) => {
|
|||||||
))}
|
))}
|
||||||
</Head>
|
</Head>
|
||||||
<Meta />
|
<Meta />
|
||||||
<LookingGlass />
|
<Layout />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
import { Colors, Fonts } from './theme';
|
import { Colors, Fonts } from './theme';
|
||||||
|
|
||||||
|
export type TQueryFields = 'query_type' | 'query_target' | 'query_location' | 'query_vrf';
|
||||||
|
|
||||||
export interface IConfigMessages {
|
export interface IConfigMessages {
|
||||||
no_input: string;
|
no_input: string;
|
||||||
acl_denied: string;
|
acl_denied: string;
|
||||||
@@ -49,13 +51,20 @@ export interface TConfigGreeting {
|
|||||||
required: boolean;
|
required: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TConfigWebLogo {
|
||||||
|
width: string;
|
||||||
|
height: string | null;
|
||||||
|
light_format: string;
|
||||||
|
dark_format: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IConfigWeb {
|
export interface IConfigWeb {
|
||||||
credit: { enable: boolean };
|
credit: { enable: boolean };
|
||||||
dns_provider: { name: string; url: string };
|
dns_provider: { name: string; url: string };
|
||||||
external_link: { enable: boolean; title: string; url: string };
|
external_link: { enable: boolean; title: string; url: string };
|
||||||
greeting: TConfigGreeting;
|
greeting: TConfigGreeting;
|
||||||
help_menu: { enable: boolean; title: string };
|
help_menu: { enable: boolean; title: string };
|
||||||
logo: { width: string; height: string | null; light_format: string; dark_format: string };
|
logo: TConfigWebLogo;
|
||||||
terms: { enable: boolean; title: string };
|
terms: { enable: boolean; title: string };
|
||||||
text: IConfigWebText;
|
text: IConfigWebText;
|
||||||
theme: IConfigTheme;
|
theme: IConfigTheme;
|
||||||
@@ -111,18 +120,18 @@ export interface TDevice extends TDeviceBase {
|
|||||||
vrfs: IDeviceVrf[];
|
vrfs: IDeviceVrf[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INetworkLocation extends TDeviceBase {
|
export interface TNetworkLocation extends TDeviceBase {
|
||||||
vrfs: IDeviceVrfBase[];
|
vrfs: IDeviceVrfBase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INetwork {
|
export interface TNetwork {
|
||||||
display_name: string;
|
display_name: string;
|
||||||
locations: INetworkLocation[];
|
locations: TNetworkLocation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TParsedDataField = [string, keyof TRoute, 'left' | 'right' | 'center' | null];
|
export type TParsedDataField = [string, keyof TRoute, 'left' | 'right' | 'center' | null];
|
||||||
|
|
||||||
export interface IQueryContent {
|
export interface TQueryContent {
|
||||||
content: string;
|
content: string;
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
params: {
|
params: {
|
||||||
@@ -141,11 +150,11 @@ export interface IConfigContent {
|
|||||||
greeting: string;
|
greeting: string;
|
||||||
vrf: {
|
vrf: {
|
||||||
[k: string]: {
|
[k: string]: {
|
||||||
bgp_route: IQueryContent;
|
bgp_route: TQueryContent;
|
||||||
bgp_community: IQueryContent;
|
bgp_community: TQueryContent;
|
||||||
bgp_aspath: IQueryContent;
|
bgp_aspath: TQueryContent;
|
||||||
ping: IQueryContent;
|
ping: TQueryContent;
|
||||||
traceroute: IQueryContent;
|
traceroute: TQueryContent;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -166,6 +175,7 @@ export interface IConfig {
|
|||||||
hyperglass_version: string;
|
hyperglass_version: string;
|
||||||
queries: IConfigQueries;
|
queries: IConfigQueries;
|
||||||
devices: TDevice[];
|
devices: TDevice[];
|
||||||
|
networks: TNetwork[];
|
||||||
vrfs: IDeviceVrfBase[];
|
vrfs: IDeviceVrfBase[];
|
||||||
parsed_data_fields: TParsedDataField[];
|
parsed_data_fields: TParsedDataField[];
|
||||||
content: IConfigContent;
|
content: IConfigContent;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
export type TQueryTypes = 'bgp_route' | 'bgp_community' | 'bgp_aspath' | 'ping' | 'traceroute';
|
export type TQueryTypes = '' | 'bgp_route' | 'bgp_community' | 'bgp_aspath' | 'ping' | 'traceroute';
|
||||||
|
|
||||||
export interface IFormData {
|
export interface IFormData {
|
||||||
query_location: string[];
|
query_location: string[];
|
||||||
|
2
hyperglass/ui/types/globals.d.ts
vendored
2
hyperglass/ui/types/globals.d.ts
vendored
@@ -62,5 +62,5 @@ declare global {
|
|||||||
type Animated<T> = Omit<T, keyof MotionProps> &
|
type Animated<T> = Omit<T, keyof MotionProps> &
|
||||||
Omit<MotionProps, keyof T> & { transition?: MotionProps['transition'] };
|
Omit<MotionProps, keyof T> & { transition?: MotionProps['transition'] };
|
||||||
|
|
||||||
type MeronexIcon = import('@meronex/icons').IconType;
|
type MeronexIcon = import('@meronex/icons').IconBaseProps;
|
||||||
}
|
}
|
||||||
|
@@ -35,10 +35,10 @@ interface CustomColors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AllColors = CustomColors & ChakraColors;
|
type AllColors = CustomColors & ChakraColors;
|
||||||
type ColorKey = keyof AllColors;
|
export type ColorNames = keyof AllColors;
|
||||||
|
|
||||||
export interface Colors extends AllColors {
|
export interface Colors extends AllColors {
|
||||||
original: { [key in ColorKey]: string };
|
original: { [key in ColorNames]: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Fonts {
|
export interface Fonts {
|
||||||
|
8
hyperglass/ui/util/common.ts
Normal file
8
hyperglass/ui/util/common.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export function all(...iter: any[]) {
|
||||||
|
for (let i of iter) {
|
||||||
|
if (!i) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
@@ -1,2 +1,3 @@
|
|||||||
|
export * from './common';
|
||||||
export * from './formatters';
|
export * from './formatters';
|
||||||
export * from './theme';
|
export * from './theme';
|
||||||
|
Reference in New Issue
Block a user