diff --git a/hyperglass/ui/components/util/animated.tsx b/hyperglass/ui/components/animated.ts
similarity index 61%
rename from hyperglass/ui/components/util/animated.tsx
rename to hyperglass/ui/components/animated.ts
index 39054ec..eec5d51 100644
--- a/hyperglass/ui/components/util/animated.tsx
+++ b/hyperglass/ui/components/animated.ts
@@ -1,5 +1,5 @@
-import { chakra, Box, forwardRef } from '@chakra-ui/react';
-import { motion, isValidMotionProp } from 'framer-motion';
+import { chakra } from '@chakra-ui/react';
+import { motion } from 'framer-motion';
import type { BoxProps } from '@chakra-ui/react';
import type { CustomDomComponent, Transition, MotionProps } from 'framer-motion';
@@ -10,19 +10,6 @@ type MakeMotionProps
= React.PropsWithChildren<
Omit
& Omit & { transition?: Transition }
>;
-/**
- * Combined Chakra + Framer Motion component.
- * @see https://chakra-ui.com/guides/integrations/with-framer
- */
-export const AnimatedDiv = motion(
- forwardRef>((props, ref) => {
- const chakraProps = Object.fromEntries(
- Object.entries(props).filter(([key]) => !isValidMotionProp(key)),
- );
- return ;
- }),
-);
-
/**
* Combine `chakra` and `motion` factories.
*
@@ -37,3 +24,5 @@ export function motionChakra(
// @ts-expect-error I don't know how to fix this.
return motion
(chakra(component, options));
}
+
+export const AnimatedDiv = motionChakra('div');
diff --git a/hyperglass/ui/components/util/dynamic-icon.tsx b/hyperglass/ui/components/dynamic-icon.tsx
similarity index 100%
rename from hyperglass/ui/components/util/dynamic-icon.tsx
rename to hyperglass/ui/components/dynamic-icon.tsx
diff --git a/hyperglass/ui/components/header/header.tsx b/hyperglass/ui/components/header/header.tsx
index 9f80bbd..338bc95 100644
--- a/hyperglass/ui/components/header/header.tsx
+++ b/hyperglass/ui/components/header/header.tsx
@@ -1,21 +1,18 @@
-import { useRef } from 'react';
import { Flex, ScaleFade } from '@chakra-ui/react';
-import { AnimatedDiv } from '~/components';
+import { motionChakra } from '~/components';
import { useBreakpointValue } from '~/context';
-import { useBooleanValue, useFormState } from '~/hooks';
+import { useBooleanValue, useFormInteractive } from '~/hooks';
import { Title } from './title';
-import type { THeader } from './types';
+const Wrapper = motionChakra('header', {
+ baseStyle: { display: 'flex', px: 4, pt: 6, minH: 16, w: 'full', flex: '0 1 auto' },
+});
-export const Header = (props: THeader): JSX.Element => {
- const { resetForm, ...rest } = props;
-
- const status = useFormState(s => s.status);
-
- const titleRef = useRef({} as HTMLDivElement);
+export const Header = (): JSX.Element => {
+ const formInteractive = useFormInteractive();
const titleWidth = useBooleanValue(
- status === 'results',
+ formInteractive,
{ base: '75%', lg: '50%' },
{ base: '75%', lg: '75%' },
);
@@ -23,21 +20,18 @@ export const Header = (props: THeader): JSX.Element => {
const justify = useBreakpointValue({ base: 'flex-start', lg: 'center' });
return (
-
+
-
-
+
-
+
);
};
diff --git a/hyperglass/ui/components/header/title.tsx b/hyperglass/ui/components/header/title.tsx
index aec01c2..150f0d0 100644
--- a/hyperglass/ui/components/header/title.tsx
+++ b/hyperglass/ui/components/header/title.tsx
@@ -3,7 +3,7 @@ import { motion } from 'framer-motion';
import { isSafari } from 'react-device-detect';
import { Switch, Case } from 'react-if';
import { useConfig, useMobile } from '~/context';
-import { useBooleanValue, useFormState } from '~/hooks';
+import { useFormState, useFormInteractive } from '~/hooks';
import { SubtitleOnly } from './subtitleOnly';
import { TitleOnly } from './titleOnly';
import { Logo } from './logo';
@@ -11,17 +11,18 @@ import { Logo } from './logo';
import type { TTitle, TTitleWrapper, TDWrapper, TMWrapper } from './types';
const AnimatedVStack = motion(VStack);
+const AnimatedFlex = motion(Flex);
/**
* Title wrapper for mobile devices, breakpoints sm & md.
*/
const MWrapper = (props: TMWrapper): JSX.Element => {
- const status = useFormState(s => s.status);
+ const formInteractive = useFormInteractive();
return (
);
@@ -31,13 +32,13 @@ const MWrapper = (props: TMWrapper): JSX.Element => {
* Title wrapper for desktop devices, breakpoints lg & xl.
*/
const DWrapper = (props: TDWrapper): JSX.Element => {
- const status = useFormState(s => s.status);
+ const formInteractive = useFormInteractive();
return (
{
const { web } = useConfig();
const { titleMode } = web.text;
- const { status, reset } = useFormState(({ status, reset }) => ({
- status,
- reset,
- }));
-
- const titleHeight = useBooleanValue(status === 'results', undefined, { md: '20vh' });
+ const reset = useFormState(s => s.reset);
+ const formInteractive = useFormInteractive();
return (
- {
div up to the parent's max-width. The fix is to hard-code a flex-basis width.
*/
flexBasis={{ base: '100%', lg: isSafari ? '33%' : '100%' }}
- mt={{ md: status === 'results' ? undefined : 'auto' }}
+ mt={{ md: formInteractive ? undefined : 'auto' }}
{...rest}
>
-
+
);
};
diff --git a/hyperglass/ui/components/header/titleOnly.tsx b/hyperglass/ui/components/header/titleOnly.tsx
index 7957937..91907e3 100644
--- a/hyperglass/ui/components/header/titleOnly.tsx
+++ b/hyperglass/ui/components/header/titleOnly.tsx
@@ -1,12 +1,12 @@
import { Heading } from '@chakra-ui/react';
import { useConfig } from '~/context';
-import { useBooleanValue, useFormState } from '~/hooks';
+import { useBooleanValue, useFormInteractive } from '~/hooks';
import { useTitleSize } from './useTitleSize';
export const TitleOnly = (): JSX.Element => {
const { web } = useConfig();
- const status = useFormState(s => s.status);
- const margin = useBooleanValue(status === 'results', 0, 2);
+ const formInteractive = useFormInteractive();
+ const margin = useBooleanValue(formInteractive, 0, 2);
const sizeSm = useTitleSize(web.text.title, '2xl', []);
return (
diff --git a/hyperglass/ui/components/index.ts b/hyperglass/ui/components/index.ts
index 4084e00..924556d 100644
--- a/hyperglass/ui/components/index.ts
+++ b/hyperglass/ui/components/index.ts
@@ -1,8 +1,10 @@
+export * from './animated';
export * from './card';
export * from './codeBlock';
export * from './countdown';
export * from './custom';
export * from './debugger';
+export * from './dynamic-icon';
export * from './favicon';
export * from './footer';
export * from './form';
@@ -23,4 +25,3 @@ export * from './results';
export * from './select';
export * from './submit';
export * from './table';
-export * from './util';
diff --git a/hyperglass/ui/components/layout/frame.tsx b/hyperglass/ui/components/layout/frame.tsx
index e0e7eef..c403b94 100644
--- a/hyperglass/ui/components/layout/frame.tsx
+++ b/hyperglass/ui/components/layout/frame.tsx
@@ -1,5 +1,6 @@
import { useCallback, useRef } from 'react';
import { Flex } from '@chakra-ui/react';
+import { motion } from 'framer-motion';
import { isSafari } from 'react-device-detect';
import { If, Then } from 'react-if';
import { Debugger, Greeting, Footer, Header } from '~/components';
@@ -9,6 +10,8 @@ import { ResetButton } from './resetButton';
import type { TFrame } from './types';
+const AnimatedFlex = motion(Flex);
+
export const Frame = (props: TFrame): JSX.Element => {
const { developerMode } = useConfig();
const { setStatus, reset } = useFormState(
@@ -38,19 +41,25 @@ export const Frame = (props: TFrame): JSX.Element => {
*/
minHeight={isSafari ? '-webkit-fill-available' : '100vh'}
>
- handleReset()} />
-
+
+ alignItems="center"
+ justifyContent="start"
+ animate={{ opacity: 1, y: 0 }}
+ transition={{ duration: 0.3 }}
+ exit={{ opacity: 0, x: -300 }}
+ initial={{ opacity: 0, y: 300 }}
+ >
+ {props.children}
+
diff --git a/hyperglass/ui/components/lookingGlass.tsx b/hyperglass/ui/components/lookingGlass.tsx
index a220bd3..9e5e944 100644
--- a/hyperglass/ui/components/lookingGlass.tsx
+++ b/hyperglass/ui/components/lookingGlass.tsx
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo } from 'react';
import isEqual from 'react-fast-compare';
-import { Flex, ScaleFade, SlideFade } from '@chakra-ui/react';
+import { chakra, Flex, ScaleFade, SlideFade } from '@chakra-ui/react';
import { FormProvider, useForm } from 'react-hook-form';
import { vestResolver } from '@hookform/resolvers/vest';
import vest, { test, enforce } from 'vest';
@@ -9,7 +9,6 @@ import {
FormField,
HelpModal,
QueryType,
- AnimatedDiv,
QueryTarget,
SubmitButton,
QueryLocation,
@@ -169,17 +168,12 @@ export const LookingGlass = (): JSX.Element => {
return (
-
@@ -232,7 +226,7 @@ export const LookingGlass = (): JSX.Element => {
-
+
);
};
diff --git a/hyperglass/ui/components/util/index.ts b/hyperglass/ui/components/util/index.ts
deleted file mode 100644
index c693265..0000000
--- a/hyperglass/ui/components/util/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './animated';
-export * from './dynamic-icon';
diff --git a/hyperglass/ui/components/util/types.ts b/hyperglass/ui/components/util/types.ts
deleted file mode 100644
index 106caa5..0000000
--- a/hyperglass/ui/components/util/types.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export interface TIf {
- c: boolean;
- children?: React.ReactNode;
-}
diff --git a/hyperglass/ui/hooks/useFormState.ts b/hyperglass/ui/hooks/useFormState.ts
index 99c4052..2292e13 100644
--- a/hyperglass/ui/hooks/useFormState.ts
+++ b/hyperglass/ui/hooks/useFormState.ts
@@ -235,3 +235,8 @@ export function useView(): FormStatus {
return ready ? 'results' : 'form';
}, [status, form]);
}
+
+export function useFormInteractive(): boolean {
+ const { status, selections } = useFormState(({ status, selections }) => ({ status, selections }));
+ return status === 'results' || selections.queryLocation.length > 0;
+}