Skip to content

Commit

Permalink
refactor(web): styles and styleguide
Browse files Browse the repository at this point in the history
  • Loading branch information
paularmstrong authored and blakeblackshear committed Feb 20, 2021
1 parent 01c3b4f commit 5ed7a17
Show file tree
Hide file tree
Showing 26 changed files with 834 additions and 182 deletions.
1 change: 1 addition & 0 deletions web/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
</head>
<body>
<div id="root"></div>
<div id="menus"></div>
<noscript>You need to enable JavaScript to run this app.</noscript>
<script type="module" src="/dist/index.js"></script>
</body>
Expand Down
42 changes: 25 additions & 17 deletions web/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { h } from 'preact';
import ActivityIndicator from './components/ActivityIndicator';
import AppBar from './components/AppBar';
import Camera from './Camera';
import CameraMap from './CameraMap';
import Cameras from './Cameras';
Expand All @@ -8,27 +9,34 @@ import Event from './Event';
import Events from './Events';
import { Router } from 'preact-router';
import Sidebar from './Sidebar';
import StyleGuide from './StyleGuide';
import Api, { FetchStatus, useConfig } from './api';

export default function App() {
const { data, status } = useConfig();
return status !== FetchStatus.LOADED ? (
<div className="flex flex-grow-1 min-h-screen justify-center items-center">
<ActivityIndicator />
</div>
) : (
<div className="md:flex flex-col md:flex-row md:min-h-screen w-full bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white">
<Sidebar />
<div className="flex-auto p-2 md:p-4 lg:pl-8 lg:pr-8 min-w-0">
<Router>
<CameraMap path="/cameras/:camera/editor" />
<Camera path="/cameras/:camera" />
<Event path="/events/:eventId" />
<Events path="/events" />
<Debug path="/debug" />
<Cameras default path="/" />
</Router>
</div>
return (
<div class="w-full">
<AppBar title="Frigate" />
{status !== FetchStatus.LOADED ? (
<div className="flex flex-grow-1 min-h-screen justify-center items-center">
<ActivityIndicator />
</div>
) : (
<div className="md:flex flex-col md:flex-row md:min-h-screen w-full bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<Sidebar />
<div className="w-full flex-auto p-2 mt-20 md:p-4 lg:pl-8 lg:pr-8 min-w-0">
<Router>
<CameraMap path="/cameras/:camera/editor" />
<Camera path="/cameras/:camera" />
<Event path="/events/:eventId" />
<Events path="/events" />
<Debug path="/debug" />
{import.meta.env.SNOWPACK_MODE !== 'development' ? <StyleGuide path="/styleguide" /> : null}
<Cameras default path="/" />
</Router>
</div>
</div>
)}
</div>
);
}
60 changes: 40 additions & 20 deletions web/src/Camera.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { h } from 'preact';
import AutoUpdatingCameraImage from './components/AutoUpdatingCameraImage';
import Box from './components/Box';
import Card from './components/Card';
import Heading from './components/Heading';
import Link from './components/Link';
import Switch from './components/Switch';
Expand All @@ -17,6 +17,7 @@ export default function Camera({ camera, url }) {
}

const cameraConfig = config.cameras[camera];
const objectCount = cameraConfig.objects.track.length;

const { pathname, searchParams } = new URL(`${window.location.protocol}//${window.location.host}${url}`);
const searchParamsString = searchParams.toString();
Expand All @@ -36,31 +37,50 @@ export default function Camera({ camera, url }) {
return (
<div className="space-y-4">
<Heading size="2xl">{camera}</Heading>
<Box>
<div>
<AutoUpdatingCameraImage camera={camera} searchParams={searchParamsString} />
</Box>
</div>

<Box className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4">
<Switch checked={getBoolean('bbox')} id="bbox" label="Bounding box" onChange={handleSetOption} />
<Switch checked={getBoolean('timestamp')} id="timestamp" label="Timestamp" onChange={handleSetOption} />
<Switch checked={getBoolean('zones')} id="zones" label="Zones" onChange={handleSetOption} />
<Switch checked={getBoolean('mask')} id="mask" label="Masks" onChange={handleSetOption} />
<Switch checked={getBoolean('motion')} id="motion" label="Motion boxes" onChange={handleSetOption} />
<Switch checked={getBoolean('regions')} id="regions" label="Regions" onChange={handleSetOption} />
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4">
<div className="flex space-x-3">
<Switch checked={getBoolean('bbox')} id="bbox" onChange={handleSetOption} />
<span class="inline-flex">Bounding box</span>
</div>
<div className="flex space-x-3">
<Switch checked={getBoolean('timestamp')} id="timestamp" onChange={handleSetOption} />
<span class="inline-flex">Timestamp</span>
</div>
<div className="flex space-x-3">
<Switch checked={getBoolean('zones')} id="zones" onChange={handleSetOption} />
<span class="inline-flex">Zones</span>
</div>
<div className="flex space-x-3">
<Switch checked={getBoolean('mask')} id="mask" onChange={handleSetOption} />
<span class="inline-flex">Masks</span>
</div>
<div className="flex space-x-3">
<Switch checked={getBoolean('motion')} id="motion" onChange={handleSetOption} />
<span class="inline-flex">Motion boxes</span>
</div>
<div className="flex space-x-3">
<Switch checked={getBoolean('regions')} id="regions" onChange={handleSetOption} />
<span class="inline-flex">Regions</span>
</div>
<Link href={`/cameras/${camera}/editor`}>Mask & Zone creator</Link>
</Box>
</div>

<div className="space-y-4">
<Heading size="sm">Tracked objects</Heading>
<div className="grid grid-cols-3 md:grid-cols-4 gap-4">
{cameraConfig.objects.track.map((objectType) => {
return (
<Box key={objectType} hover href={`/events?camera=${camera}&label=${objectType}`}>
<Heading size="sm">{objectType}</Heading>
<img src={`${apiHost}/api/${camera}/${objectType}/best.jpg?crop=1&h=150`} />
</Box>
);
})}
<div className="flex flex-wrap justify-start">
{cameraConfig.objects.track.map((objectType) => (
<Card
className="mb-4 mr-4"
key={objectType}
header={objectType}
href={`/events?camera=${camera}&label=${objectType}`}
media={<img src={`${apiHost}/api/${camera}/${objectType}/best.jpg?crop=1&h=150`} />}
/>
))}
</div>
</div>
</div>
Expand Down
33 changes: 19 additions & 14 deletions web/src/CameraMap.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { h } from 'preact';
import Box from './components/Box';
import Card from './components/Card';
import Button from './components/Button';
import Heading from './components/Heading';
import Switch from './components/Switch';
Expand Down Expand Up @@ -242,15 +242,18 @@ ${Object.keys(objectMaskPoints)
<div class="flex-col space-y-4">
<Heading size="2xl">{camera} mask & zone creator</Heading>

<Box>
<p>
This tool can help you create masks & zones for your {camera} camera. When done, copy each mask configuration
into your <code className="font-mono">config.yml</code> file restart your Frigate instance to save your
changes.
</p>
</Box>

<Box className="space-y-4">
<Card
content={
<p>
This tool can help you create masks & zones for your {camera} camera. When done, copy each mask
configuration into your <code className="font-mono">config.yml</code> file restart your Frigate instance to
save your changes.
</p>
}
header="Warning"
/>

<div className="space-y-4">
<div className="relative">
<img ref={imageRef} src={`${apiHost}/api/${camera}/latest.jpg`} />
<EditableMask
Expand All @@ -262,8 +265,10 @@ ${Object.keys(objectMaskPoints)
height={height}
/>
</div>
<Switch checked={snap} label="Snap to edges" onChange={handleChangeSnap} />
</Box>
<div class="flex space-x-4">
<span>Snap to edges</span> <Switch checked={snap} onChange={handleChangeSnap} />
</div>
</div>

<div class="flex-col space-y-4">
<MaskValues
Expand Down Expand Up @@ -475,7 +480,7 @@ function MaskValues({
);

return (
<Box className="overflow-hidden" onmouseover={handleMousein} onmouseout={handleMouseout}>
<div className="overflow-hidden" onmouseover={handleMousein} onmouseout={handleMouseout}>
<div class="flex space-x-4">
<Heading className="flex-grow self-center" size="base">
{title}
Expand Down Expand Up @@ -525,7 +530,7 @@ function MaskValues({
}
})}
</pre>
</Box>
</div>
);
}

Expand Down
14 changes: 4 additions & 10 deletions web/src/Cameras.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { h } from 'preact';
import ActivityIndicator from './components/ActivityIndicator';
import Box from './components/Box';
import Card from './components/Card';
import CameraImage from './components/CameraImage';
import Events from './Events';
import Heading from './components/Heading';
import { route } from 'preact-router';
import { useConfig } from './api';
import { useMemo } from 'preact/hooks';

export default function Cameras() {
const { data: config, status } = useConfig();
Expand All @@ -25,14 +26,7 @@ export default function Cameras() {

function Camera({ name }) {
const href = `/cameras/${name}`;
const buttons = useMemo(() => [{ name: 'Events', href: `/events?camera=${name}` }], [name]);

return (
<Box
className="bg-white dark:bg-gray-700 shadow-lg rounded-lg p-4 hover:bg-gray-300 hover:dark:bg-gray-500 dark:hover:text-gray-900 dark:hover:text-gray-900"
href={href}
>
<Heading size="base">{name}</Heading>
<CameraImage camera={name} />
</Box>
);
return <Card buttons={buttons} href={href} header={name} media={<CameraImage camera={name} />} />;
}
83 changes: 39 additions & 44 deletions web/src/Debug.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { h } from 'preact';
import ActivityIndicator from './components/ActivityIndicator';
import Box from './components/Box';
import Button from './components/Button';
import Heading from './components/Heading';
import Link from './components/Link';
Expand Down Expand Up @@ -50,63 +49,59 @@ export default function Debug() {
Debug <span className="text-sm">{service.version}</span>
</Heading>

<Box>
<Table className="w-full">
<Thead>
<Tr>
<Th>detector</Th>
<Table className="w-full">
<Thead>
<Tr>
<Th>detector</Th>
{detectorDataKeys.map((name) => (
<Th>{name.replace('_', ' ')}</Th>
))}
</Tr>
</Thead>
<Tbody>
{detectorNames.map((detector, i) => (
<Tr index={i}>
<Td>{detector}</Td>
{detectorDataKeys.map((name) => (
<Th>{name.replace('_', ' ')}</Th>
<Td key={`${name}-${detector}`}>{detectors[detector][name]}</Td>
))}
</Tr>
</Thead>
<Tbody>
{detectorNames.map((detector, i) => (
<Tr index={i}>
<Td>{detector}</Td>
{detectorDataKeys.map((name) => (
<Td key={`${name}-${detector}`}>{detectors[detector][name]}</Td>
))}
</Tr>
))}
</Tbody>
</Table>
</Box>
))}
</Tbody>
</Table>

<Box>
<Table className="w-full">
<Thead>
<Tr>
<Th>camera</Th>
<Table className="w-full">
<Thead>
<Tr>
<Th>camera</Th>
{cameraDataKeys.map((name) => (
<Th>{name.replace('_', ' ')}</Th>
))}
</Tr>
</Thead>
<Tbody>
{cameraNames.map((camera, i) => (
<Tr index={i}>
<Td>
<Link href={`/cameras/${camera}`}>{camera}</Link>
</Td>
{cameraDataKeys.map((name) => (
<Th>{name.replace('_', ' ')}</Th>
<Td key={`${name}-${camera}`}>{cameras[camera][name]}</Td>
))}
</Tr>
</Thead>
<Tbody>
{cameraNames.map((camera, i) => (
<Tr index={i}>
<Td>
<Link href={`/cameras/${camera}`}>{camera}</Link>
</Td>
{cameraDataKeys.map((name) => (
<Td key={`${name}-${camera}`}>{cameras[camera][name]}</Td>
))}
</Tr>
))}
</Tbody>
</Table>
</Box>
))}
</Tbody>
</Table>

<Box className="relative">
<div className="relative">
<Heading size="sm">Config</Heading>
<Button className="absolute top-4 right-8" onClick={handleCopyConfig}>
<Button className="absolute top-8 right-4" onClick={handleCopyConfig}>
Copy to Clipboard
</Button>
<pre className="overflow-auto font-mono text-gray-900 dark:text-gray-100 rounded bg-gray-100 dark:bg-gray-800 p-2 max-h-96">
{JSON.stringify(config, null, 2)}
</pre>
</Box>
</div>
</div>
);
}
Loading

0 comments on commit 5ed7a17

Please sign in to comment.