1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00

update UI deps, migrate from yup to vest for form validation, migrate to palette-by-numbers for theming

This commit is contained in:
checktheroads
2021-02-26 11:15:35 -07:00
parent f69322013d
commit b77dbf198a
7 changed files with 672 additions and 2602 deletions

View File

@@ -4,7 +4,8 @@ import { If } from '~/components';
import { useColorValue } from '~/context';
import { useBooleanValue } from '~/hooks';
import { TField, TFormError } from './types';
import type { FieldError } from 'react-hook-form';
import type { TField } from './types';
export const FormField: React.FC<TField> = (props: TField) => {
const { name, label, children, labelAddOn, fieldAddOn, hiddenLabels = false, ...rest } = props;
@@ -14,10 +15,10 @@ export const FormField: React.FC<TField> = (props: TField) => {
const { errors } = useFormContext();
const error = name in errors && (errors[name] as TFormError);
const error = name in errors && (errors[name] as FieldError);
if (error !== false) {
console.warn(`${label} Error: ${error.message}`);
console.warn(`Error on field '${label}': ${error.message}`);
}
return (

View File

@@ -1,9 +1,6 @@
import type { FormControlProps } from '@chakra-ui/react';
import type { Control } from 'react-hook-form';
import type { TDeviceVrf, TBGPCommunity, OnChangeArgs } from '~/types';
import type { ValidationError } from 'yup';
export type TFormError = Pick<ValidationError, 'message' | 'type'>;
export interface TField extends FormControlProps {
name: string;

View File

@@ -2,8 +2,8 @@ import { useCallback, useEffect, useMemo } from 'react';
import { Flex } from '@chakra-ui/react';
import { FormProvider, useForm } from 'react-hook-form';
import { intersectionWith } from 'lodash';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { vestResolver } from '@hookform/resolvers/vest';
import vest, { test, enforce } from 'vest';
import {
If,
FormRow,
@@ -51,15 +51,29 @@ export const LookingGlass: React.FC = () => {
const noQueryLoc = useStrf(messages.no_input, { field: web.text.query_location });
const noQueryTarget = useStrf(messages.no_input, { field: web.text.query_target });
const formSchema = yup.object().shape({
query_location: yup.array().of(yup.string()).required(noQueryLoc),
query_target: yup.string().required(noQueryTarget),
query_type: yup.string().required(noQueryType),
query_vrf: yup.string(),
const formSchema = vest.create((data: TFormData = {} as TFormData) => {
test('query_location', noQueryLoc, () => {
enforce(data.query_location).isArrayOf(enforce.isString()).isNotEmpty();
});
test('query_target', noQueryTarget, () => {
enforce(data.query_target).longerThan(1);
});
test('query_type', noQueryType, () => {
enforce(data.query_type).anyOf(
enforce.equals('bgp_route'),
enforce.equals('bgp_community'),
enforce.equals('bgp_aspath'),
enforce.equals('ping'),
enforce.equals('traceroute'),
);
});
test('query_vrf', 'Query VRF is empty', () => {
enforce(data.query_vrf).isString();
});
});
const formInstance = useForm<TFormData>({
resolver: yupResolver(formSchema),
resolver: vestResolver(formSchema),
defaultValues: { query_vrf: 'default', query_target: '', query_location: [], query_type: '' },
});

View File

@@ -18,19 +18,19 @@
},
"browserslist": "> 0.25%, not dead",
"dependencies": {
"@chakra-ui/react": "^1.1.6",
"@emotion/react": "^11.1.4",
"@emotion/styled": "^11.0.0",
"@hookform/resolvers": "^1.2.0",
"@hookstate/core": "^3.0.3",
"@chakra-ui/react": "^1.3.3",
"@emotion/react": "^11.1.5",
"@emotion/styled": "^11.1.5",
"@hookform/resolvers": "1.3.0",
"@hookstate/core": "^3.0.6",
"@hookstate/persistence": "^3.0.0",
"@meronex/icons": "^4.0.0",
"color2k": "^1.1.1",
"dagre": "^0.8.5",
"dayjs": "^1.8.25",
"framer-motion": "^3.2.2-rc.1",
"lodash": "^4.17.15",
"next": "^10.0.5",
"next": "^10.0.7",
"palette-by-numbers": "^0.1.5",
"react": "^17.0.1",
"react-countdown": "^2.2.1",
"react-device-detect": "^1.15.0",
@@ -38,13 +38,13 @@
"react-fast-compare": "^3.2.0",
"react-flow-renderer": "^8.2.3",
"react-ga": "^3.3.0",
"react-hook-form": "^6.14.2",
"react-hook-form": "^6.15.4",
"react-markdown": "^5.0.3",
"react-query": "^3.6.0",
"react-select": "^3.1.1",
"react-table": "^7.6.2",
"string-format": "^2.0.0",
"yup": "^0.32.8"
"vest": "^3.1.2"
},
"devDependencies": {
"@hookstate/devtools": "^3.0.0",
@@ -54,7 +54,6 @@
"@types/react-select": "^3.0.28",
"@types/react-table": "^7.0.25",
"@types/string-format": "^2.0.0",
"@types/yup": "^0.29.9",
"@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.1",
"@upstatement/eslint-config": "^0.4.3",

View File

@@ -1,21 +1,7 @@
import type { Theme as DefaultTheme } from '@chakra-ui/theme';
import type { ColorHues } from '@chakra-ui/theme/dist/types/foundations/colors';
import type { ChakraTheme } from '@chakra-ui/theme';
export namespace Theme {
type ExtraColors = {
dark: ColorHues;
light: ColorHues;
error: ColorHues;
danger: ColorHues;
primary: ColorHues;
success: ColorHues;
warning: ColorHues;
secondary: ColorHues;
blackSolid: ColorHues;
whiteSolid: ColorHues;
};
export type Colors = ExtraColors & DefaultTheme['colors'];
export type Colors = ChakraTheme['colors'];
export type ColorNames = keyof Colors;
@@ -24,9 +10,9 @@ export namespace Theme {
mono: string;
};
export type FontWeights = Partial<DefaultTheme['fontWeights']>;
export type FontWeights = Partial<ChakraTheme['fontWeights']>;
export interface Full extends Omit<DefaultTheme, 'colors'> {
export interface Full extends Omit<ChakraTheme, 'colors'> {
colors: Colors;
}
}

View File

@@ -1,115 +1,27 @@
import {
hsla,
saturate,
desaturate,
parseToHsla,
transparentize,
readableColorIsBlack,
} from 'color2k';
import { extendTheme } from '@chakra-ui/react';
import { mode } from '@chakra-ui/theme-tools';
import { generateFontFamily, generatePalette } from 'palette-by-numbers';
import type { Theme as ChakraTheme } from '@chakra-ui/react';
import type { ChakraTheme } from '@chakra-ui/react';
import type { IConfigTheme, Theme } from '~/types';
const defaultBodyFonts = [
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Helvetica',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
];
function importFonts(userFonts: Theme.Fonts): ChakraTheme['fonts'] {
const { body: userBody, mono: userMono } = userFonts;
const defaultMonoFonts = [
'SFMono-Regular',
'Melno',
'Monaco',
'Consolas',
'"Liberation Mono"',
'"Courier New"',
'monospace',
];
export function isLight(color: string): boolean {
return readableColorIsBlack(color);
}
export function isDark(color: string): boolean {
return !readableColorIsBlack(color);
}
function alphaColors(color: string) {
return {
50: transparentize(color, Number((1 - 0.04).toFixed(2))),
100: transparentize(color, Number((1 - 0.08).toFixed(2))),
200: transparentize(color, Number((1 - 0.12).toFixed(2))),
300: transparentize(color, Number((1 - 0.16).toFixed(2))),
400: transparentize(color, Number((1 - 0.24).toFixed(2))),
500: transparentize(color, Number((1 - 0.38).toFixed(2))),
600: transparentize(color, Number((1 - 0.48).toFixed(2))),
700: transparentize(color, Number((1 - 0.6).toFixed(2))),
800: transparentize(color, Number((1 - 0.8).toFixed(2))),
900: transparentize(color, Number((1 - 0.92).toFixed(2))),
body: generateFontFamily('sans-serif', userBody),
heading: generateFontFamily('sans-serif', userBody),
mono: generateFontFamily('monospace', userMono),
};
}
function generateColors(colorInput: string) {
const colorMap = Object();
const lightnessMap = [0.95, 0.85, 0.75, 0.65, 0.55, 0.45, 0.35, 0.25, 0.15, 0.05];
const saturationMap = [0.32, 0.16, 0.08, 0.04, 0, 0, 0.04, 0.08, 0.16, 0.32];
const colorHsla = parseToHsla(colorInput);
const lightnessGoal = colorHsla[2];
const closestLightness = lightnessMap.reduce((prev, curr) =>
Math.abs(curr - lightnessGoal) < Math.abs(prev - lightnessGoal) ? curr : prev,
);
const baseColorIndex = lightnessMap.findIndex(l => l === closestLightness);
const colors = lightnessMap
.map(l => {
const [h, s, _, a] = colorHsla;
return hsla(h, s, l, a);
})
.map((color, i) => {
const saturationDelta = saturationMap[i] - saturationMap[baseColorIndex];
return saturationDelta >= 0
? saturate(color, saturationDelta)
: desaturate(color, saturationDelta * -1);
});
const getColorNumber = (index: number) => (index === 0 ? 50 : index * 100);
colors.map((color, i) => {
const colorIndex = getColorNumber(i);
if (colorIndex === 500) {
colorMap[500] = colorInput;
} else {
colorMap[colorIndex] = color;
}
});
return colorMap;
}
function generatePalette(palette: IConfigTheme['colors']): Theme.Colors {
const generatedPalette = Object();
for (const color of Object.keys(palette)) {
if (!['black', 'white'].includes(color)) {
generatedPalette[color] = generateColors(palette[color]);
} else {
generatedPalette[color] = palette[color];
generatedPalette[`${color}Alpha`] = alphaColors(palette[color]);
}
function importColors(userColors: IConfigTheme['colors']): Theme.Colors {
const generatedColors = {} as Theme.Colors;
for (const [k, v] of Object.entries(userColors)) {
generatedColors[k] = generatePalette(v);
}
generatedPalette.blackSolid = {
generatedColors.blackSolid = {
50: '#444444',
100: '#3c3c3c',
200: '#353535',
@@ -121,7 +33,7 @@ function generatePalette(palette: IConfigTheme['colors']): Theme.Colors {
800: '#080808',
900: '#000000',
};
generatedPalette.whiteSolid = {
generatedColors.whiteSolid = {
50: '#ffffff',
100: '#f7f7f7',
200: '#f0f0f0',
@@ -133,38 +45,6 @@ function generatePalette(palette: IConfigTheme['colors']): Theme.Colors {
800: '#c3c3c3',
900: '#bbbbbb',
};
return generatedPalette;
}
function formatFont(font: string): string {
const fontList = font.split(' ');
const fontFmt = fontList.length >= 2 ? `'${fontList.join(' ')}'` : fontList.join(' ');
return fontFmt;
}
function importFonts(userFonts: Theme.Fonts): [ChakraTheme['fonts'], Theme.FontWeights] {
const [body, mono] = [defaultBodyFonts, defaultMonoFonts];
const { body: userBody, mono: userMono, ...fontWeights } = userFonts;
const bodyFmt = formatFont(userBody);
const monoFmt = formatFont(userMono);
if (userFonts.body && !body.includes(bodyFmt)) {
body.unshift(bodyFmt);
}
if (userFonts.mono && !mono.includes(monoFmt)) {
mono.unshift(monoFmt);
}
return [
{
body: body.join(', '),
heading: body.join(', '),
mono: mono.join(', '),
},
fontWeights,
];
}
function importColors(userColors: IConfigTheme['colors']): Theme.Colors {
const generatedColors = generatePalette(userColors);
return {
...generatedColors,
@@ -177,9 +57,20 @@ export function makeTheme(
userTheme: IConfigTheme,
defaultColorMode: 'dark' | 'light' | null,
): Theme.Full {
const [fonts, fontWeights] = importFonts(userTheme.fonts);
const fonts = importFonts(userTheme.fonts);
const colors = importColors(userTheme.colors);
const config = {} as Theme.Full['config'];
const fontWeights = {
hairline: 300,
thin: 300,
light: 300,
normal: 400,
medium: 400,
semibold: 700,
bold: 700,
extrabold: 700,
black: 700,
} as ChakraTheme['fontWeights'];
switch (defaultColorMode) {
case null:

3034
hyperglass/ui/yarn.lock vendored

File diff suppressed because it is too large Load Diff