1
0
Fork 1
mirror of https://github.com/thatmattlove/hyperglass.git synced 2026-01-17 08:48:05 +00:00

improve form & results style & layout [skip ci]

This commit is contained in:
checktheroads 2020-12-29 02:08:45 -07:00
parent 7cb445d9e7
commit 80af987034
10 changed files with 49 additions and 41 deletions

View file

@ -37,9 +37,9 @@ export const FormField = (props: TField) => {
htmlFor={name}
display="flex"
opacity={opacity}
color={error !== false ? errorColor : labelColor}
alignItems="center"
justifyContent="space-between">
justifyContent="space-between"
color={error !== false ? errorColor : labelColor}>
{label}
<If c={typeof labelAddOn !== 'undefined'}>{labelAddOn}</If>
</FormLabel>

View file

@ -19,5 +19,4 @@ export * from './path';
export * from './results';
export * from './select';
export * from './table';
export * from './title';
export * from './util';

View file

@ -18,7 +18,7 @@ import {
} from '~/components';
import { useConfig } from '~/context';
import { useStrf, useGreeting, useDevice, useLGState } from '~/hooks';
import { isQueryType, isString } from '~/types';
import { isQueryType, isQueryContent, isString } from '~/types';
import type { TFormData, TDeviceVrf, OnChangeArgs } from '~/types';
@ -63,8 +63,6 @@ export const HyperglassForm = () => {
} = useLGState();
function submitHandler(values: TFormData) {
console.dir(values);
console.dir(formData.value);
if (!greetingAck && web.greeting.required) {
window.location.reload(false);
setGreetingAck(false);
@ -143,15 +141,15 @@ export const HyperglassForm = () => {
} else if (e.field === 'query_target' && isString(e.value)) {
queryTarget.set(e.value);
}
console.log(e.field, e.value);
console.dir(getValues());
}
const vrfContent = useMemo(() => {
if (Object.keys(content.vrf).includes(queryVrf.value) && queryType.value !== '') {
return content.vrf[queryVrf.value][queryType.value];
} else {
return null;
}
}, [queryVrf]);
}, [queryVrf.value, queryLocation.value, queryType.value]);
const isFqdnQuery = useMemo(() => {
return ['bgp_route', 'ping', 'traceroute'].includes(queryType.value);
@ -185,7 +183,9 @@ export const HyperglassForm = () => {
<FormField
name="query_type"
label={web.text.query_type}
labelAddOn={vrfContent && <HelpModal item={vrfContent} name="query_type" />}>
labelAddOn={
<HelpModal visible={isQueryContent(vrfContent)} item={vrfContent} name="query_type" />
}>
<QueryType onChange={handleChange} label={web.text.query_type} />
</FormField>
</FormRow>

View file

@ -19,7 +19,7 @@ export const Chart = (props: TChart) => {
const flowProps = useBreakpointValue<Omit<ReactFlowProps, 'elements'>>({
base: { defaultPosition: [0, 300], defaultZoom: 0 },
lg: { defaultPosition: [500, 450] },
lg: { defaultPosition: [100, 300], defaultZoom: 0.7 },
});
const elements = useMemo(() => [...buildElements({ asn: primary_asn, name: org_name }, data)], [
@ -29,7 +29,7 @@ export const Chart = (props: TChart) => {
return (
<ReactFlowProvider>
<Box boxSize="100%" zIndex={1}>
<ReactFlow elements={elements} nodeTypes={{ TestNode }} {...flowProps}>
<ReactFlow elements={elements} nodeTypes={{ ASNode }} {...flowProps}>
<Background color={dots} />
<Controls />
</ReactFlow>
@ -38,7 +38,7 @@ export const Chart = (props: TChart) => {
);
};
const TestNode = (props: TNode<TNodeData>) => {
const ASNode = (props: TNode<TNodeData>) => {
const { data } = props;
const { asn, name, hasChildren, hasParents } = data;

View file

@ -23,6 +23,7 @@ export const Path = (props: TPath) => {
const response = getResponse(device);
const output = response?.output as TStructuredResponse;
const bg = useColorValue('whiteFaded.50', 'blackFaded.900');
return (
<>
<PathButton onOpen={onOpen} />

View file

@ -10,7 +10,7 @@ function treeToElement(part: PathPart, len: number, index: number): FlowElement[
let elements = [
{
id: String(part.base),
type: 'TestNode',
type: 'ASNode',
position: { x, y },
data: {
asn: part.base,
@ -50,7 +50,7 @@ export function* buildElements(base: BasePath, data: TStructuredResponse): Gener
// Add the first hop at the base.
yield {
id: base.asn,
type: 'TestNode',
type: 'ASNode',
position: { x: 150, y: numHops * 10 },
data: { asn: base.asn, name: base.name, hasChildren: true, hasParents: false },
};

View file

@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { Accordion, Box, Stack, useToken } from '@chakra-ui/react';
import { motion, AnimatePresence } from 'framer-motion';
import { AnimatedDiv, Label } from '~/components';
@ -63,7 +63,7 @@ export const Results = () => {
xl: { opacity: 0, x: 100 },
});
const [resultsComplete, setComplete] = useState<number | null>(null);
const [resultsComplete, setComplete] = useState<number[]>([]);
const matchedVrf =
vrfs.filter(v => v.id === queryVrf.value)[0] ?? vrfs.filter(v => v.id === 'default')[0];
@ -73,6 +73,13 @@ export const Results = () => {
queryTypeLabel = queries[queryType.value].display_name;
}
// Scroll to the top of the page when results load - primarily for mobile.
useEffect(() => {
if (typeof window !== 'undefined') {
window.scrollTo(0, 0);
}
}, []);
return (
<>
<Box
@ -141,7 +148,7 @@ export const Results = () => {
transition={{ duration: 0.3 }}
animate={{ opacity: 1, y: 0 }}
maxW={{ base: '100%', md: '75%' }}>
<Accordion allowMultiple allowToggle>
<Accordion allowMultiple allowToggle index={resultsComplete}>
<AnimatePresence>
{queryLocation &&
queryLocation.map((loc, i) => {

View file

@ -16,9 +16,9 @@ import { BGPTable, Countdown, CopyButton, RequeryButton, TextOutput, If, Path }
import { useColorValue, useConfig, useMobile } from '~/context';
import { useStrf, useLGQuery, useLGState, useTableToString } from '~/hooks';
import { isStructuredOutput, isStringOutput } from '~/types';
import { isStackError, isFetchError, isLGError } from './guards';
import { FormattedError } from './error';
import { ResultHeader } from './header';
import { isStackError, isFetchError, isLGError } from './guards';
import type { TAccordionHeaderWrapper, TResult, TErrorLevels } from './types';
@ -71,12 +71,15 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
const cacheLabel = useStrf(web.text.cache_icon, { time: data?.timestamp }, [data?.timestamp]);
const [isOpen, setOpen] = useState(false);
const [hasOverride, setOverride] = useState(false);
const handleToggle = () => {
setOpen(!isOpen);
setOverride(true);
// Close if open.
if (resultsComplete.includes(index)) {
setComplete(p => p.filter(i => i !== index));
}
// Open if closed.
else if (!resultsComplete.includes(index)) {
setComplete(p => [...p, index]);
}
};
const errorKeywords = useMemo(() => {
@ -140,17 +143,12 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
copyValue = errorMsg;
}
// If this is the first completed result, open it.
useEffect(() => {
if (isLoading && resultsComplete === null) {
setComplete(index);
if (!isLoading && !isError && resultsComplete.length === 0) {
setComplete([index]);
}
}, [isLoading, resultsComplete]);
useEffect(() => {
if (resultsComplete === index && !hasOverride) {
setOpen(true);
}
}, [resultsComplete, index]);
}, [isLoading, isError]);
return (
<AnimatedAccordionItem

View file

@ -23,11 +23,11 @@ export interface TResult {
index: number;
device: TDevice;
queryVrf: string;
queryType: TQueryTypes;
queryTarget: string;
setComplete(v: number | null): void;
queryLocation: string;
resultsComplete: number | null;
queryType: TQueryTypes;
resultsComplete: number[];
setComplete: React.Dispatch<React.SetStateAction<number[]>>;
}
export type TErrorLevels = 'success' | 'warning' | 'error';

View file

@ -27,7 +27,10 @@ export const useControlStyle = (base: TStyles, state: TControl): TStyles => {
);
const focusBorder = useColorValue(useToken('colors', 'blue.500'), useToken('colors', 'blue.300'));
const invalidBorder = useColorValue(useToken('colors', 'red.500'), useToken('colors', 'red.300'));
const borderColor = useColorValue('inherit', useToken('colors', 'whiteAlpha.50'));
const borderColor = useColorValue(
useToken('colors', 'gray.100'),
useToken('colors', 'whiteAlpha.50'),
);
const borderRadius = useToken('radii', 'md');
const minHeight = useToken('space', 12);
const color = useColorValue(useToken('colors', 'black'), useToken('colors', 'whiteAlpha.800'));
@ -132,11 +135,11 @@ export const useOptionStyle = (base: TStyles, state: TOption): TStyles => {
const color = useOpposingColor(backgroundColor);
const styles = {
backgroundColor,
color,
fontSize,
'&:focus': { backgroundColor: active, color: activeColor },
color: backgroundColor === 'transparent' ? 'currentColor' : color,
'&:active': { backgroundColor: active, color: activeColor },
'&:focus': { backgroundColor: active, color: activeColor },
backgroundColor,
fontSize,
};
return useMemo(() => mergeWith({}, base, styles), [