Skip to content

Commit

Permalink
Implement global page structure
Browse files Browse the repository at this point in the history
  • Loading branch information
danielfdsilva committed Dec 11, 2024
1 parent 3e9a234 commit 70bcd51
Show file tree
Hide file tree
Showing 17 changed files with 543 additions and 466 deletions.
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@parcel/reporter-bundle-analyzer": "^2.12.0",
"@parcel/reporter-bundle-buddy": "^2.12.0",
"@types/babel__core": "^7",
"@types/node": "^22.9.0",
"@types/node": "^22.10.2",
"@types/react": "^18.3.4",
"@types/react-dom": "^18.3.0",
"buffer": "^6.0.3",
Expand Down Expand Up @@ -74,7 +74,7 @@
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"formik": "^2.4.6",
"framer-motion": "^11.0.3",
"framer-motion": "^10.16.5",
"mapbox-gl-draw-rectangle-mode": "^1.0.4",
"maplibre-gl": "^3.6.2",
"polished": "^4.3.1",
Expand Down
64 changes: 52 additions & 12 deletions packages/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { ChakraProvider, Box, Container } from '@chakra-ui/react';
import {
ChakraProvider,
Box,
Container,
Flex,
Heading,
Text,
Badge,
Divider
} from '@chakra-ui/react';
import { StacApiProvider } from '@developmentseed/stac-react';
import { PluginConfigProvider } from '@stac-manager/data-core';

Expand All @@ -21,20 +30,25 @@ export const App = () => (
<StacApiProvider apiUrl={process.env.REACT_APP_STAC_API!}>
<PluginConfigProvider config={config}>
<Router>
<Container mx='auto' p='5' bgColor='white' maxW='container.lg'>
<Box
<Container
maxW='container.lg'
minH='100vh'
display='flex'
flexDirection='column'
>
<Flex
as='header'
borderBottom='1px dashed'
borderColor='gray.300'
mb='4'
pb='4'
display='flex'
gap={4}
alignItems='center'
justifyContent='space-between'
p={4}
>
<Box flex='1' fontWeight='bold' textTransform='uppercase'>
STAC Admin
</Box>
<Heading as='p' size='sm'>
STAC Manager
</Heading>

<MainNavigation />
</Box>
</Flex>
<Box as='main'>
<Routes>
<Route path='/' element={<Home />} />
Expand All @@ -60,6 +74,32 @@ export const App = () => (
<Route path='*' element={<NotFound />} />
</Routes>
</Box>
<Flex
as='footer'
gap={4}
alignItems='center'
justifyContent='space-between'
mt='auto'
p={4}
>
<Flex gap={4} alignItems='center'>
<Text as='span'>
Powered by{' '}
<strong>
STAC Manager{' '}
<Badge bg='base.400a' color='surface.500' px='0.375rem'>
{process.env.APP_VERSION}
</Badge>
</strong>{' '}
</Text>
<Divider
orientation='vertical'
borderColor='base.200a'
h='1em'
/>
{new Date().getFullYear()}
</Flex>
</Flex>
</Container>
</Router>
</PluginConfigProvider>
Expand Down
79 changes: 79 additions & 0 deletions packages/client/src/components/InnerPageHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useEffect, useRef, useState } from 'react';
import { Flex, Heading, Text, forwardRef, FlexProps } from '@chakra-ui/react';

interface InnerPageHeaderProps extends FlexProps {
title: string;
overline?: string;
actions?: React.ReactNode;
}

export const InnerPageHeader = forwardRef<InnerPageHeaderProps, 'div'>(
({ title, overline, actions, ...rest }, ref) => {
return (
<Flex
ref={ref}
bg='base.50'
borderRadius='md'
p={4}
direction='column'
gap={2}
{...rest}
>
{overline && (
<Text as='p' color='base.400'>
{overline}
</Text>
)}
<Flex gap={4} justifyContent='space-between' alignItems='center'>
<Heading size='md'>{title}</Heading>
{actions && <Flex gap={2}>{actions}</Flex>}
</Flex>
</Flex>
);
}
);

export const InnerPageHeaderSticky = forwardRef<InnerPageHeaderProps, 'div'>(
(props, ref) => {
const [isAtTop, setIsAtTop] = useState(false);

const localRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
const el = localRef.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
setIsAtTop(entry.intersectionRatio < 1);
},
{ threshold: [1] }
);

observer.observe(el);

return () => {
observer.unobserve(el);
};
}, []);

const headerRef: React.RefCallback<HTMLDivElement> = (v) => {
localRef.current = v;
if (typeof ref === 'function') {
ref(v);
} else if (ref != null) {
(ref as React.MutableRefObject<any>).current = v;
}
};

return (
<InnerPageHeader
ref={headerRef}
position='sticky'
top='-1px'
boxShadow={isAtTop ? 'md' : 'none'}
zIndex={100}
{...props}
/>
);
}
);
50 changes: 29 additions & 21 deletions packages/client/src/components/MainNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
import { Link as RouterLink } from "react-router-dom";
import { Box, List, ListItem, Link } from "@chakra-ui/react";
import React from 'react';
import { Box, List, ListItem, Button, ButtonProps } from '@chakra-ui/react';
import {
CollecticonFolder,
CollecticonPlusSmall
} from '@devseed-ui/collecticons-chakra';

type NavItemProps = React.PropsWithChildren<{
to: string
}>
import SmartLink, { SmartLinkProps } from './SmartLink';

function NavItem({ to, children }: NavItemProps) {
function NavItem(props: ButtonProps & SmartLinkProps) {
return (
<ListItem>
<Link
as={RouterLink}
to={to}
borderRadius="5"
px="2"
py="1"
_hover={{ bgColor: "gray.100" }}
>
{children}
</Link>
<Button
as={SmartLink}
variant='ghost'
sx={{
'&:hover': {
textDecoration: 'none'
}
}}
{...props}
/>
</ListItem>
);
}

function MainNavigation() {
return (
<Box as="nav" aria-label="Main">
<List my="0" display="flex">
<NavItem to="/">Home</NavItem>
<NavItem to="/collections/">Collections</NavItem>
<NavItem to="/items/">Items</NavItem>
<Box as='nav' aria-label='Main'>
<List display='flex' gap={2}>
<NavItem to='/collections/' leftIcon={<CollecticonFolder />}>
Collections
</NavItem>
<NavItem to='/items/' leftIcon={<CollecticonFolder />}>
Items
</NavItem>
<NavItem to='/collections/new' leftIcon={<CollecticonPlusSmall />}>
Create
</NavItem>
</List>
</Box>
);
Expand Down
22 changes: 22 additions & 0 deletions packages/client/src/components/SmartLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Link as ChLink, LinkProps } from '@chakra-ui/react';
import { Link } from 'react-router-dom';

export interface SmartLinkProps extends LinkProps {
to: string;
}

export default React.forwardRef<HTMLLinkElement, SmartLinkProps>(
function SmartLink(props, ref) {
const { to, ...rest } = props;

const isExternal =
to.match(/^(https?:)?\/\//) || to.match(/^(mailto|tel):/);

return isExternal ? (
<ChLink ref={ref} href={to} {...rest} />
) : (
<ChLink ref={ref} as={Link} to={to} {...rest} />
);
}
);
12 changes: 4 additions & 8 deletions packages/client/src/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import HeadingLead from "./HeadingLead";
import Loading from "./Loading";
import MainNavigation from "./MainNavigation";
import HeadingLead from './HeadingLead';
import Loading from './Loading';
import MainNavigation from './MainNavigation';

export {
HeadingLead,
Loading,
MainNavigation
};
export { HeadingLead, Loading, MainNavigation };
Loading

0 comments on commit 70bcd51

Please sign in to comment.