mirror of
https://github.com/morten-olsen/refocus.dev.git
synced 2026-02-08 00:46:25 +01:00
feat: desktop version
This commit is contained in:
@@ -11,3 +11,4 @@ export * from './masonry';
|
||||
export * from './code-editor';
|
||||
export * from './popover';
|
||||
export * from './form';
|
||||
export * from './loader';
|
||||
|
||||
37
packages/ui/src/base/loader/index.tsx
Normal file
37
packages/ui/src/base/loader/index.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import styled from 'styled-components';
|
||||
import { LuLoader2 } from 'react-icons/lu';
|
||||
|
||||
type LoaderProps = {
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const LoaderWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
const LoaderIcon = styled.div`
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
border: 5px solid #ccc;
|
||||
border-top-color: #000;
|
||||
animation: spin 1s infinite linear;
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Loader: React.FC<LoaderProps> = ({ children }) => {
|
||||
return (
|
||||
<LoaderWrapper>
|
||||
<LoaderIcon>{children || <LuLoader2 />}</LoaderIcon>
|
||||
</LoaderWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export { Loader };
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
} from '@refocus/sdk';
|
||||
import { IoAddCircleOutline } from 'react-icons/io5';
|
||||
import styled from 'styled-components';
|
||||
import { View } from '../../base';
|
||||
import { Button, Dialog, Form, View } from '../../base';
|
||||
import { Board } from '../board';
|
||||
import { Tabs } from '../../base/tabs';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
const Wrapper = styled(View)`
|
||||
height: 100vh;
|
||||
@@ -36,40 +36,68 @@ const Title: React.FC<{ id: string }> = ({ id }) => {
|
||||
|
||||
const App: React.FC = () => {
|
||||
const boards = useBoards();
|
||||
const [boardName, setBoardName] = useState('');
|
||||
const [addOpen, setAddOpen] = useState(false);
|
||||
const selected = useSelectedBoard();
|
||||
const selectBoard = useSelectBoard();
|
||||
const addBoardAction = useAddBoard();
|
||||
const removeBoard = useRemoveBoard();
|
||||
|
||||
const addBoard = useCallback(() => {
|
||||
const name = prompt('Board name?');
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
addBoardAction(name);
|
||||
}, [addBoardAction]);
|
||||
const addBoard = useCallback(
|
||||
(name: string) => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
setAddOpen(false);
|
||||
addBoardAction(name);
|
||||
},
|
||||
[addBoardAction],
|
||||
);
|
||||
|
||||
return (
|
||||
<Wrapper $fc>
|
||||
<View $f={1}>
|
||||
<Tabs value={selected} onValueChange={selectBoard}>
|
||||
<Tabs.List>
|
||||
{Object.entries(boards).map(([id, board]) => (
|
||||
{Object.entries(boards).map(([id]) => (
|
||||
<Tabs.Trigger key={id} value={id}>
|
||||
<Title id={id} />
|
||||
<Tabs.Close onClick={() => removeBoard(id)} />
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
<View
|
||||
onClick={addBoard}
|
||||
$fr
|
||||
$justify="center"
|
||||
$items="center"
|
||||
$p="md"
|
||||
$bg="highlight100"
|
||||
>
|
||||
<IoAddCircleOutline size={16} />
|
||||
</View>
|
||||
<Dialog open={addOpen} onOpenChange={setAddOpen}>
|
||||
<Dialog.Trigger>
|
||||
<View
|
||||
$fr
|
||||
$justify="center"
|
||||
$items="center"
|
||||
$p="md"
|
||||
$bg="highlight100"
|
||||
>
|
||||
<IoAddCircleOutline size={16} />
|
||||
</View>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay />
|
||||
<Dialog.Content>
|
||||
<Form>
|
||||
<Form.Field label="Board Name">
|
||||
<Form.Input
|
||||
value={boardName}
|
||||
onChange={(e) => setBoardName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Buttons>
|
||||
<Button
|
||||
title="Add Board"
|
||||
onClick={() => addBoard(boardName)}
|
||||
/>
|
||||
</Form.Buttons>
|
||||
</Form>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog>
|
||||
</Tabs.List>
|
||||
{Object.entries(boards).map(([id, board]) => (
|
||||
<Tabs.Content key={id} value={id}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DashboardProvider, Widget } from '@refocus/sdk';
|
||||
import { DashboardProvider, Widget, Notification } from '@refocus/sdk';
|
||||
import { UIProvider } from './theme/provider';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { GithubLogin } from './github';
|
||||
@@ -8,9 +8,14 @@ import { SlackLogin } from './slack';
|
||||
type FocusProviderProps = {
|
||||
children: React.ReactNode;
|
||||
widgets: Widget<any>[];
|
||||
onNotificationsUpdate?: (notifications: Notification[]) => void;
|
||||
};
|
||||
|
||||
const FocusProvider: React.FC<FocusProviderProps> = ({ children, widgets }) => {
|
||||
const FocusProvider: React.FC<FocusProviderProps> = ({
|
||||
children,
|
||||
widgets,
|
||||
onNotificationsUpdate,
|
||||
}) => {
|
||||
const save = useCallback((data: any) => {
|
||||
localStorage.setItem('boards', JSON.stringify(data));
|
||||
}, []);
|
||||
@@ -42,6 +47,7 @@ const FocusProvider: React.FC<FocusProviderProps> = ({ children, widgets }) => {
|
||||
save={save}
|
||||
widgets={widgets}
|
||||
logins={logins}
|
||||
onNotificationsUpdate={onNotificationsUpdate}
|
||||
>
|
||||
{children}
|
||||
</DashboardProvider>
|
||||
|
||||
Reference in New Issue
Block a user