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:
@@ -3,7 +3,9 @@ import { Button, Icon, Tooltip } from '@chakra-ui/react';
|
|||||||
|
|
||||||
import type { TPathButton } from './types';
|
import type { TPathButton } from './types';
|
||||||
|
|
||||||
const PathIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/gr').then(i => i.GrNetwork));
|
const PathIcon = dynamic<MeronexIcon>(() =>
|
||||||
|
import('@meronex/icons/bi').then(i => i.BisNetworkChart),
|
||||||
|
);
|
||||||
|
|
||||||
export const PathButton = (props: TPathButton) => {
|
export const PathButton = (props: TPathButton) => {
|
||||||
const { onOpen } = props;
|
const { onOpen } = props;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { forwardRef } from 'react';
|
||||||
import {
|
import {
|
||||||
Modal,
|
Modal,
|
||||||
Popover,
|
Popover,
|
||||||
@@ -18,24 +19,26 @@ import { useMobile } from '~/context';
|
|||||||
import { useLGState } from '~/hooks';
|
import { useLGState } from '~/hooks';
|
||||||
|
|
||||||
import type { IconButtonProps } from '@chakra-ui/react';
|
import type { IconButtonProps } from '@chakra-ui/react';
|
||||||
import type { OnChangeArgs } from '~/types';
|
|
||||||
import type { TSubmitButton, TRSubmitButton } from './types';
|
import type { TSubmitButton, TRSubmitButton } from './types';
|
||||||
|
|
||||||
const SubmitIcon = (props: Omit<IconButtonProps, 'aria-label'>) => {
|
const SubmitIcon = forwardRef<HTMLButtonElement, Omit<IconButtonProps, 'aria-label'>>(
|
||||||
const { isLoading } = props;
|
(props, ref) => {
|
||||||
return (
|
const { isLoading } = props;
|
||||||
<IconButton
|
return (
|
||||||
size="lg"
|
<IconButton
|
||||||
width={16}
|
ref={ref}
|
||||||
type="submit"
|
size="lg"
|
||||||
icon={<FiSearch />}
|
width={16}
|
||||||
title="Submit Query"
|
type="submit"
|
||||||
colorScheme="primary"
|
icon={<FiSearch />}
|
||||||
isLoading={isLoading}
|
title="Submit Query"
|
||||||
aria-label="Submit Query"
|
colorScheme="primary"
|
||||||
/>
|
isLoading={isLoading}
|
||||||
);
|
aria-label="Submit Query"
|
||||||
};
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mobile Submit Button
|
* Mobile Submit Button
|
||||||
|
@@ -1,57 +0,0 @@
|
|||||||
import { useEffect, useMemo } from 'react';
|
|
||||||
import { Text } from '@chakra-ui/react';
|
|
||||||
import { components } from 'react-select';
|
|
||||||
import { Select } from '~/components';
|
|
||||||
|
|
||||||
import type { OptionProps } from 'react-select';
|
|
||||||
import type { TBGPCommunity, TSelectOption } from '~/types';
|
|
||||||
import type { TCommunitySelect } from './types';
|
|
||||||
|
|
||||||
function buildOptions(communities: TBGPCommunity[]): TSelectOption[] {
|
|
||||||
return communities.map(c => ({
|
|
||||||
value: c.community,
|
|
||||||
label: c.display_name,
|
|
||||||
description: c.description,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const Option = (props: OptionProps<Dict, false>) => {
|
|
||||||
const { label, data } = props;
|
|
||||||
return (
|
|
||||||
<components.Option {...props}>
|
|
||||||
<Text as="span">{label}</Text>
|
|
||||||
<br />
|
|
||||||
<Text fontSize="xs" as="span">
|
|
||||||
{data.description}
|
|
||||||
</Text>
|
|
||||||
</components.Option>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CommunitySelect = (props: TCommunitySelect) => {
|
|
||||||
const { name, communities, onChange, register, unregister } = props;
|
|
||||||
|
|
||||||
const options = useMemo(() => buildOptions(communities), [communities.length]);
|
|
||||||
|
|
||||||
function handleChange(e: TSelectOption | TSelectOption[]): void {
|
|
||||||
if (!Array.isArray(e) && e !== null) {
|
|
||||||
onChange({ field: name, value: e.value });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
register({ name });
|
|
||||||
return () => unregister(name);
|
|
||||||
}, [name, register, unregister]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
size="lg"
|
|
||||||
name={name}
|
|
||||||
options={options}
|
|
||||||
innerRef={register}
|
|
||||||
onChange={handleChange}
|
|
||||||
components={{ Option }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@@ -1,4 +1,3 @@
|
|||||||
export * from './communitySelect';
|
|
||||||
export * from './field';
|
export * from './field';
|
||||||
export * from './queryLocation';
|
export * from './queryLocation';
|
||||||
export * from './queryTarget';
|
export * from './queryTarget';
|
||||||
|
@@ -1,46 +1,95 @@
|
|||||||
import { Input } from '@chakra-ui/react';
|
import { useMemo } from 'react';
|
||||||
import { useColorValue } from '~/context';
|
import { Input, Text } from '@chakra-ui/react';
|
||||||
|
import { components } from 'react-select';
|
||||||
|
import { If, Select } from '~/components';
|
||||||
|
import { useConfig, useColorValue } from '~/context';
|
||||||
import { useLGState } from '~/hooks';
|
import { useLGState } from '~/hooks';
|
||||||
|
|
||||||
|
import type { OptionProps } from 'react-select';
|
||||||
|
import type { TBGPCommunity, TSelectOption } from '~/types';
|
||||||
import type { TQueryTarget } from './types';
|
import type { TQueryTarget } from './types';
|
||||||
|
|
||||||
const fqdnPattern = /^(?!:\/\/)([a-zA-Z0-9-]+\.)?[a-zA-Z0-9-][a-zA-Z0-9-]+\.[a-zA-Z-]{2,6}?$/gim;
|
const fqdnPattern = /^(?!:\/\/)([a-zA-Z0-9-]+\.)?[a-zA-Z0-9-][a-zA-Z0-9-]+\.[a-zA-Z-]{2,6}?$/gim;
|
||||||
|
|
||||||
|
function buildOptions(communities: TBGPCommunity[]): TSelectOption[] {
|
||||||
|
return communities.map(c => ({
|
||||||
|
value: c.community,
|
||||||
|
label: c.display_name,
|
||||||
|
description: c.description,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Option = (props: OptionProps<Dict, false>) => {
|
||||||
|
const { label, data } = props;
|
||||||
|
return (
|
||||||
|
<components.Option {...props}>
|
||||||
|
<Text as="span">{label}</Text>
|
||||||
|
<br />
|
||||||
|
<Text fontSize="xs" as="span">
|
||||||
|
{data.description}
|
||||||
|
</Text>
|
||||||
|
</components.Option>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const QueryTarget = (props: TQueryTarget) => {
|
export const QueryTarget = (props: TQueryTarget) => {
|
||||||
const { name, register, setTarget, placeholder, resolveTarget } = props;
|
const { name, register, onChange, placeholder, resolveTarget } = props;
|
||||||
|
|
||||||
const bg = useColorValue('white', 'whiteAlpha.100');
|
const bg = useColorValue('white', 'whiteAlpha.100');
|
||||||
const color = useColorValue('gray.400', 'whiteAlpha.800');
|
const color = useColorValue('gray.400', 'whiteAlpha.800');
|
||||||
const border = useColorValue('gray.100', 'whiteAlpha.50');
|
const border = useColorValue('gray.100', 'whiteAlpha.50');
|
||||||
const placeholderColor = useColorValue('gray.600', 'whiteAlpha.700');
|
const placeholderColor = useColorValue('gray.600', 'whiteAlpha.700');
|
||||||
|
|
||||||
const { queryTarget, fqdnTarget, displayTarget } = useLGState();
|
const { queryType, queryTarget, fqdnTarget, displayTarget } = useLGState();
|
||||||
|
|
||||||
|
const { queries } = useConfig();
|
||||||
|
|
||||||
|
const options = useMemo(() => buildOptions(queries.bgp_community.communities), []);
|
||||||
|
|
||||||
function handleChange(e: React.ChangeEvent<HTMLInputElement>): void {
|
function handleChange(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
displayTarget.set(e.target.value);
|
displayTarget.set(e.target.value);
|
||||||
setTarget({ field: name, value: e.target.value });
|
onChange({ field: name, value: e.target.value });
|
||||||
|
|
||||||
if (resolveTarget && displayTarget.value && fqdnPattern.test(displayTarget.value)) {
|
if (resolveTarget && displayTarget.value && fqdnPattern.test(displayTarget.value)) {
|
||||||
fqdnTarget.set(displayTarget.value);
|
fqdnTarget.set(displayTarget.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSelectChange(e: TSelectOption | TSelectOption[]): void {
|
||||||
|
if (!Array.isArray(e) && e !== null) {
|
||||||
|
onChange({ field: name, value: e.value });
|
||||||
|
displayTarget.set(e.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<input hidden readOnly name={name} ref={register} value={queryTarget.value} />
|
<input hidden readOnly name={name} ref={register} value={queryTarget.value} />
|
||||||
<Input
|
<If c={queryType.value === 'bgp_community' && queries.bgp_community.mode === 'select'}>
|
||||||
bg={bg}
|
<Select
|
||||||
size="lg"
|
size="lg"
|
||||||
color={color}
|
name={name}
|
||||||
borderRadius="md"
|
options={options}
|
||||||
value={displayTarget.value}
|
innerRef={register}
|
||||||
borderColor={border}
|
onChange={handleSelectChange}
|
||||||
onChange={handleChange}
|
components={{ Option }}
|
||||||
aria-label={placeholder}
|
/>
|
||||||
placeholder={placeholder}
|
</If>
|
||||||
name="query_target_display"
|
<If c={!(queryType.value === 'bgp_community' && queries.bgp_community.mode === 'select')}>
|
||||||
_placeholder={{ color: placeholderColor }}
|
<Input
|
||||||
/>
|
bg={bg}
|
||||||
|
size="lg"
|
||||||
|
color={color}
|
||||||
|
borderRadius="md"
|
||||||
|
borderColor={border}
|
||||||
|
onChange={handleChange}
|
||||||
|
aria-label={placeholder}
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={displayTarget.value}
|
||||||
|
name="query_target_display"
|
||||||
|
_placeholder={{ color: placeholderColor }}
|
||||||
|
/>
|
||||||
|
</If>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -29,7 +29,6 @@ export interface TCommunitySelect {
|
|||||||
onChange: OnChange;
|
onChange: OnChange;
|
||||||
communities: TBGPCommunity[];
|
communities: TBGPCommunity[];
|
||||||
register: Control['register'];
|
register: Control['register'];
|
||||||
unregister: Control['unregister'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TQueryTarget {
|
export interface TQueryTarget {
|
||||||
@@ -37,7 +36,7 @@ export interface TQueryTarget {
|
|||||||
placeholder: string;
|
placeholder: string;
|
||||||
resolveTarget: boolean;
|
resolveTarget: boolean;
|
||||||
register: Control['register'];
|
register: Control['register'];
|
||||||
setTarget(e: OnChangeArgs): void;
|
onChange(e: OnChangeArgs): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TResolvedTarget {
|
export interface TResolvedTarget {
|
||||||
|
@@ -31,19 +31,15 @@ export const HelpModal = (props: THelpModal) => {
|
|||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
initial={{ opacity: 0, scale: 0.3 }}>
|
initial={{ opacity: 0, scale: 0.3 }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
h={3}
|
|
||||||
w={3}
|
|
||||||
mb={1}
|
mb={1}
|
||||||
ml={1}
|
ml={1}
|
||||||
maxH={3}
|
|
||||||
maxW={3}
|
|
||||||
minH={3}
|
minH={3}
|
||||||
minW={3}
|
minW={3}
|
||||||
size="sm"
|
size="md"
|
||||||
variant="link"
|
variant="link"
|
||||||
icon={<Info />}
|
icon={<Info />}
|
||||||
onClick={onOpen}
|
onClick={onOpen}
|
||||||
colorScheme="primary"
|
colorScheme="blue"
|
||||||
aria-label={`${name}_help`}
|
aria-label={`${name}_help`}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
@@ -15,7 +15,6 @@ import {
|
|||||||
QueryTarget,
|
QueryTarget,
|
||||||
SubmitButton,
|
SubmitButton,
|
||||||
QueryLocation,
|
QueryLocation,
|
||||||
CommunitySelect,
|
|
||||||
} from '~/components';
|
} from '~/components';
|
||||||
import { useConfig } from '~/context';
|
import { useConfig } from '~/context';
|
||||||
import { useStrf, useGreeting, useDevice, useLGState } from '~/hooks';
|
import { useStrf, useGreeting, useDevice, useLGState } from '~/hooks';
|
||||||
@@ -37,16 +36,17 @@ export const HyperglassForm = () => {
|
|||||||
|
|
||||||
const formSchema = yup.object().shape({
|
const formSchema = yup.object().shape({
|
||||||
query_location: yup.array().of(yup.string()).required(noQueryLoc),
|
query_location: yup.array().of(yup.string()).required(noQueryLoc),
|
||||||
|
query_target: yup.string().required(noQueryTarget),
|
||||||
query_type: yup.string().required(noQueryType),
|
query_type: yup.string().required(noQueryType),
|
||||||
query_vrf: yup.string(),
|
query_vrf: yup.string(),
|
||||||
query_target: yup.string().required(noQueryTarget),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const formInstance = useForm<TFormData>({
|
const formInstance = useForm<TFormData>({
|
||||||
resolver: yupResolver(formSchema),
|
resolver: yupResolver(formSchema),
|
||||||
defaultValues: { query_vrf: 'default', query_target: '', query_location: [], query_type: '' },
|
defaultValues: { query_vrf: 'default', query_target: '', query_location: [], query_type: '' },
|
||||||
});
|
});
|
||||||
const { handleSubmit, register, unregister, setValue, errors } = formInstance;
|
|
||||||
|
const { handleSubmit, register, unregister, setValue, getValues } = formInstance;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
queryVrf,
|
queryVrf,
|
||||||
@@ -63,6 +63,8 @@ export const HyperglassForm = () => {
|
|||||||
} = useLGState();
|
} = useLGState();
|
||||||
|
|
||||||
function submitHandler(values: TFormData) {
|
function submitHandler(values: TFormData) {
|
||||||
|
console.dir(values);
|
||||||
|
console.dir(formData.value);
|
||||||
if (!greetingAck && web.greeting.required) {
|
if (!greetingAck && web.greeting.required) {
|
||||||
window.location.reload(false);
|
window.location.reload(false);
|
||||||
setGreetingAck(false);
|
setGreetingAck(false);
|
||||||
@@ -141,6 +143,8 @@ export const HyperglassForm = () => {
|
|||||||
} else if (e.field === 'query_target' && isString(e.value)) {
|
} else if (e.field === 'query_target' && isString(e.value)) {
|
||||||
queryTarget.set(e.value);
|
queryTarget.set(e.value);
|
||||||
}
|
}
|
||||||
|
console.log(e.field, e.value);
|
||||||
|
console.dir(getValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
const vrfContent = useMemo(() => {
|
const vrfContent = useMemo(() => {
|
||||||
@@ -155,9 +159,9 @@ export const HyperglassForm = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
register({ name: 'query_location', required: true });
|
register({ name: 'query_location', required: true });
|
||||||
|
register({ name: 'query_target', required: true });
|
||||||
register({ name: 'query_type', required: true });
|
register({ name: 'query_type', required: true });
|
||||||
register({ name: 'query_vrf' });
|
register({ name: 'query_vrf' });
|
||||||
register({ name: 'query_target', required: true });
|
|
||||||
}, [register]);
|
}, [register]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -192,25 +196,13 @@ export const HyperglassForm = () => {
|
|||||||
</FormField>
|
</FormField>
|
||||||
</If>
|
</If>
|
||||||
<FormField name="query_target" label={web.text.query_target}>
|
<FormField name="query_target" label={web.text.query_target}>
|
||||||
<If c={queryType.value === 'bgp_community' && queries.bgp_community.mode === 'select'}>
|
<QueryTarget
|
||||||
<CommunitySelect
|
name="query_target"
|
||||||
name="query_target"
|
register={register}
|
||||||
register={register}
|
onChange={handleChange}
|
||||||
unregister={unregister}
|
resolveTarget={isFqdnQuery}
|
||||||
onChange={handleChange}
|
placeholder={web.text.query_target}
|
||||||
communities={queries.bgp_community.communities}
|
/>
|
||||||
/>
|
|
||||||
</If>
|
|
||||||
<If
|
|
||||||
c={!(queryType.value === 'bgp_community' && queries.bgp_community.mode === 'select')}>
|
|
||||||
<QueryTarget
|
|
||||||
name="query_target"
|
|
||||||
register={register}
|
|
||||||
setTarget={handleChange}
|
|
||||||
resolveTarget={isFqdnQuery}
|
|
||||||
placeholder={web.text.query_target}
|
|
||||||
/>
|
|
||||||
</If>
|
|
||||||
</FormField>
|
</FormField>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow mt={0} justifyContent="flex-end">
|
<FormRow mt={0} justifyContent="flex-end">
|
||||||
|
@@ -1,40 +1,52 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Box, Flex, Icon, Badge, VStack } from '@chakra-ui/react';
|
import { Box, Flex, SkeletonText, Badge, VStack } from '@chakra-ui/react';
|
||||||
import { BeatLoader } from 'react-spinners';
|
|
||||||
import ReactFlow from 'react-flow-renderer';
|
import ReactFlow from 'react-flow-renderer';
|
||||||
import { Background, Controls } from 'react-flow-renderer';
|
import { Background, ReactFlowProvider } from 'react-flow-renderer';
|
||||||
import { Handle, Position } from 'react-flow-renderer';
|
import { Handle, Position } from 'react-flow-renderer';
|
||||||
import { useConfig, useColorValue, useColorToken } from '~/context';
|
import { useConfig, useColorValue, useColorToken, useBreakpointValue } from '~/context';
|
||||||
import { useASNDetail } from '~/hooks';
|
import { useASNDetail } from '~/hooks';
|
||||||
|
import { Controls } from './controls';
|
||||||
import { buildElements } from './util';
|
import { buildElements } from './util';
|
||||||
|
|
||||||
|
import type { ReactFlowProps } from 'react-flow-renderer';
|
||||||
import type { TChart, TNode, TNodeData } from './types';
|
import type { TChart, TNode, TNodeData } from './types';
|
||||||
|
|
||||||
export const Chart = (props: TChart) => {
|
export const Chart = (props: TChart) => {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
const { primary_asn, org_name } = useConfig();
|
const { primary_asn, org_name } = useConfig();
|
||||||
// const elements = useMemo(() => [...buildElements({ asn: primary_asn, name: org_name }, data)], [
|
|
||||||
// data,
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
const elements = [...buildElements({ asn: primary_asn, name: org_name }, data)];
|
|
||||||
const dots = useColorToken('blackAlpha.500', 'whiteAlpha.400');
|
const dots = useColorToken('blackAlpha.500', 'whiteAlpha.400');
|
||||||
|
|
||||||
|
const flowProps = useBreakpointValue<Omit<ReactFlowProps, 'elements'>>({
|
||||||
|
base: { defaultPosition: [0, 300], defaultZoom: 0 },
|
||||||
|
lg: { defaultPosition: [500, 450] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const elements = useMemo(() => [...buildElements({ asn: primary_asn, name: org_name }, data)], [
|
||||||
|
data,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box boxSize="100%">
|
<ReactFlowProvider>
|
||||||
<ReactFlow elements={elements} nodeTypes={{ TestNode }} defaultPosition={[500, 450]}>
|
<Box boxSize="100%" zIndex={1}>
|
||||||
<Background color={dots} />
|
<ReactFlow elements={elements} nodeTypes={{ TestNode }} {...flowProps}>
|
||||||
<Controls />
|
<Background color={dots} />
|
||||||
</ReactFlow>
|
<Controls />
|
||||||
</Box>
|
</ReactFlow>
|
||||||
|
</Box>
|
||||||
|
</ReactFlowProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TestNode = (props: TNode<TNodeData>) => {
|
const TestNode = (props: TNode<TNodeData>) => {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
const { asn, name, hasChildren, hasParents } = data;
|
const { asn, name, hasChildren, hasParents } = data;
|
||||||
|
|
||||||
const color = useColorValue('black', 'white');
|
const color = useColorValue('black', 'white');
|
||||||
const bg = useColorValue('white', 'blackAlpha.800');
|
const bg = useColorValue('white', 'whiteAlpha.100');
|
||||||
|
|
||||||
const { data: asnData, isError, isLoading } = useASNDetail(asn);
|
const { data: asnData, isError, isLoading } = useASNDetail(asn);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{hasChildren && <Handle type="source" position={Position.Top} />}
|
{hasChildren && <Handle type="source" position={Position.Top} />}
|
||||||
@@ -42,7 +54,9 @@ const TestNode = (props: TNode<TNodeData>) => {
|
|||||||
<VStack spacing={4}>
|
<VStack spacing={4}>
|
||||||
<Flex fontSize="lg">
|
<Flex fontSize="lg">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<BeatLoader color={color} />
|
<Box h={2} w={24}>
|
||||||
|
<SkeletonText noOfLines={1} color={color} />
|
||||||
|
</Box>
|
||||||
) : !isError && asnData?.data?.description_short ? (
|
) : !isError && asnData?.data?.description_short ? (
|
||||||
asnData.data.description_short
|
asnData.data.description_short
|
||||||
) : (
|
) : (
|
||||||
|
27
hyperglass/ui/components/path/controls.tsx
Normal file
27
hyperglass/ui/components/path/controls.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
import { ButtonGroup, IconButton } from '@chakra-ui/react';
|
||||||
|
import { useZoomPanHelper } from 'react-flow-renderer';
|
||||||
|
|
||||||
|
const Plus = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiPlus));
|
||||||
|
const Minus = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiMinus));
|
||||||
|
const Square = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiSquare));
|
||||||
|
|
||||||
|
export const Controls = () => {
|
||||||
|
const { fitView, zoomIn, zoomOut } = useZoomPanHelper();
|
||||||
|
return (
|
||||||
|
<ButtonGroup
|
||||||
|
m={4}
|
||||||
|
size="sm"
|
||||||
|
right={0}
|
||||||
|
zIndex={4}
|
||||||
|
bottom={0}
|
||||||
|
isAttached
|
||||||
|
pos="absolute"
|
||||||
|
variant="solid"
|
||||||
|
colorScheme="secondary">
|
||||||
|
<IconButton icon={<Plus />} onClick={() => zoomIn()} aria-label="Zoom In" />
|
||||||
|
<IconButton icon={<Minus />} onClick={() => zoomOut()} aria-label="Zoom Out" />
|
||||||
|
<IconButton icon={<Square />} onClick={() => fitView()} aria-label="Fit Nodes" />
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
};
|
@@ -22,13 +22,13 @@ export const Path = (props: TPath) => {
|
|||||||
const { displayTarget } = useLGState();
|
const { displayTarget } = useLGState();
|
||||||
const response = getResponse(device);
|
const response = getResponse(device);
|
||||||
const output = response?.output as TStructuredResponse;
|
const output = response?.output as TStructuredResponse;
|
||||||
const bg = useColorValue('white', 'blackFaded.800');
|
const bg = useColorValue('whiteFaded.50', 'blackFaded.900');
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PathButton onOpen={onOpen} />
|
<PathButton onOpen={onOpen} />
|
||||||
<Modal isOpen={isOpen} onClose={onClose} size="full" isCentered>
|
<Modal isOpen={isOpen} onClose={onClose} size="full" isCentered>
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent bg={bg} maxW="80%" maxH="60%">
|
<ModalContent bg={bg} maxW={{ base: '100%', lg: '80%' }} maxH={{ base: '80%', lg: '60%' }}>
|
||||||
<ModalHeader>{`Path to ${displayTarget.value}`}</ModalHeader>
|
<ModalHeader>{`Path to ${displayTarget.value}`}</ModalHeader>
|
||||||
<ModalCloseButton />
|
<ModalCloseButton />
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
@@ -148,6 +148,7 @@ export const Results = () => {
|
|||||||
const device = getDevice(loc.value);
|
const device = getDevice(loc.value);
|
||||||
return (
|
return (
|
||||||
<Result
|
<Result
|
||||||
|
key={i}
|
||||||
index={i}
|
index={i}
|
||||||
device={device}
|
device={device}
|
||||||
queryLocation={loc.value}
|
queryLocation={loc.value}
|
||||||
|
Reference in New Issue
Block a user