Cleaning up
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import Link from '../../components/Link';
|
||||
|
||||
import Store from '../../store';
|
||||
import * as selectors from '../../store/selectors';
|
||||
|
||||
@@ -12,7 +10,6 @@ import {
|
||||
IonItem,
|
||||
IonLabel,
|
||||
} from '@ionic/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const ListEntry = ({ list, ...props }) => (
|
||||
<IonItem href={`/tabs/lists/${list.id}`} className="list-entry">
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
const Backdrop = ({ open, onClose }) => {
|
||||
return (
|
||||
<div
|
||||
onClick={onClose}
|
||||
className={classNames('fixed z-10 inset-0 bg-black transition-opacity w-full h-full', {
|
||||
'pointer-events-none': !open,
|
||||
'opacity-10': open,
|
||||
'opacity-0': !open,
|
||||
})}
|
||||
></div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Backdrop;
|
||||
@@ -1,15 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
|
||||
const Button = ({ children, className, ...props }) => (
|
||||
<button
|
||||
{...props}
|
||||
className={classNames(
|
||||
'inline-block text-xs font-medium leading-6 text-center uppercase transition rounded-lg ripple focus:outline-none',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
export default Button;
|
||||
@@ -1,3 +0,0 @@
|
||||
const EdgeDrag = () => null;
|
||||
|
||||
export default EdgeDrag;
|
||||
@@ -1,19 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
|
||||
const Icon = ({ icon, ...props }) => {
|
||||
const svg = icon.replace('data:image/svg+xml;utf8,', '');
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
className={classNames('ui-icon', {
|
||||
'ion-icon': true,
|
||||
'ion-color': true,
|
||||
[props.className]: true,
|
||||
})}
|
||||
>
|
||||
<div className="icon-inner" dangerouslySetInnerHTML={{ __html: svg }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Icon;
|
||||
@@ -1,3 +0,0 @@
|
||||
const List = ({ children, ...props }) => <div {...props}>{children}</div>;
|
||||
|
||||
export default List;
|
||||
@@ -1,9 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
|
||||
const ListItem = ({ children, className, ...props }) => (
|
||||
<div className={classNames('p-4', className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ListItem;
|
||||
@@ -1,91 +0,0 @@
|
||||
import { Plugins } from '@capacitor/core';
|
||||
import classNames from 'classnames';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useDrag } from 'react-use-gesture';
|
||||
|
||||
const { DarkMode } = Plugins;
|
||||
|
||||
const Menu = ({ open, onClose, children, className, ...props }) => {
|
||||
const ref = useRef();
|
||||
const [x, setX] = useState(-100000);
|
||||
const [rect, setRect] = useState(null);
|
||||
const [dragging, setDragging] = useState(false);
|
||||
|
||||
useEffect(async () => {
|
||||
try {
|
||||
let darkmodeConfig = await DarkMode.isDarkModeOn();
|
||||
console.log({ open, darkMode: darkmodeConfig.isDarkModeOn });
|
||||
Plugins.StatusBar.setStyle({
|
||||
style: open && !darkmodeConfig.isDarkModeOn ? 'LIGHT' : 'DARK',
|
||||
}).catch(() => {});
|
||||
} catch (e) {}
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
const rect = ref.current?.getBoundingClientRect();
|
||||
setRect(rect);
|
||||
setX(-rect.width);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setX(0);
|
||||
} else if (rect) {
|
||||
setX(-rect.width);
|
||||
}
|
||||
}, [rect, open]);
|
||||
|
||||
const bind = useDrag(
|
||||
({ down, movement: [mx] }) => {
|
||||
setX(mx > 0 ? 0 : mx);
|
||||
|
||||
if (down) {
|
||||
setDragging(true);
|
||||
} else {
|
||||
setDragging(false);
|
||||
}
|
||||
|
||||
// If the drag ended, snap the menu back
|
||||
if (!down) {
|
||||
const mid = -rect.width;
|
||||
if (x < mid / 2) {
|
||||
// Close
|
||||
setX(-rect.width);
|
||||
onClose();
|
||||
} else {
|
||||
// Re-open
|
||||
setX(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
axis: 'x',
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
{...bind()}
|
||||
ref={ref}
|
||||
style={{
|
||||
paddingTop: `calc(env(safe-area-inset-top, 0px) + 8px)`,
|
||||
paddingBottom: `calc(env(safe-area-inset-bottom, 0px) + 8px)`,
|
||||
touchAction: 'pan-x',
|
||||
transform: `translateX(${x}px)`,
|
||||
}}
|
||||
className={classNames(
|
||||
'fixed z-40 transform-gpu translate w-48 h-full bg-gray-100 dark:bg-gray-800',
|
||||
className,
|
||||
{
|
||||
'transition-transform': !dragging,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Menu;
|
||||
@@ -1,94 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { useDrag } from 'react-use-gesture';
|
||||
import Store from '../../store';
|
||||
import { SafeAreaContext } from './SafeArea';
|
||||
|
||||
// A Modal window that slides in from offscreen and can be closed
|
||||
// by dragging.
|
||||
const Modal = ({ open, onClose, children }) => {
|
||||
const ref = useRef();
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const [rect, setRect] = useState(null);
|
||||
const [y, setY] = useState(100000);
|
||||
|
||||
const { top: safeAreaTop } = useContext(SafeAreaContext);
|
||||
|
||||
const _open = useCallback(() => {
|
||||
setY(safeAreaTop);
|
||||
}, [safeAreaTop]);
|
||||
|
||||
const _close = useCallback(() => {
|
||||
if (!rect) {
|
||||
return;
|
||||
}
|
||||
setY(rect.height + safeAreaTop);
|
||||
}, [safeAreaTop, rect]);
|
||||
|
||||
// Get the layout rectangle for the modal
|
||||
useEffect(() => {
|
||||
const rect = ref.current?.getBoundingClientRect();
|
||||
setRect(rect);
|
||||
_close();
|
||||
}, [safeAreaTop]);
|
||||
|
||||
// If open changes, open/close the modal
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
_open();
|
||||
} else {
|
||||
_close();
|
||||
}
|
||||
}, [rect, open, _open, _close]);
|
||||
|
||||
const bind = useDrag(
|
||||
({ down, movement: [mx, my] }) => {
|
||||
setY(my < 0 ? safeAreaTop : my + safeAreaTop);
|
||||
|
||||
if (down) {
|
||||
setDragging(true);
|
||||
} else {
|
||||
setDragging(false);
|
||||
}
|
||||
|
||||
// If the drag ended, snap the menu back open or close it
|
||||
if (!down) {
|
||||
const mid = rect.height;
|
||||
if (y > mid / 2) {
|
||||
// Close
|
||||
_close();
|
||||
onClose();
|
||||
} else {
|
||||
// Re-open
|
||||
_open();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
axis: 'y',
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
{...bind()}
|
||||
className={classNames(
|
||||
'fixed z-40 top-5 transform transform-gpu w-full h-full bg-white rounded-t-xl',
|
||||
{
|
||||
'ease-in-out duration-300 transition-transformation': !dragging,
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
'--safe-area-top': `env(safe-area-inset-top, 0px)`,
|
||||
height: `calc(100% - env(safe-area-inset-top, 0px) - 1.25rem)`,
|
||||
touchAction: 'pan-y',
|
||||
transform: `translateY(${y}px)`,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Modal;
|
||||
@@ -1,203 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Plugins } from '@capacitor/core';
|
||||
import * as actions from '../../store/actions';
|
||||
|
||||
const Nav = ({ page }) => {
|
||||
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
||||
|
||||
const title = typeof page?.title === 'function' ? page?.title() : page?.title || '';
|
||||
|
||||
useEffect(() => {
|
||||
Plugins.StatusBar.setStyle({
|
||||
style: 'DARK',
|
||||
}).catch(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<nav
|
||||
className="bg-gray-800 w-full flex-0 flex items-end flex-row z-10"
|
||||
style={{
|
||||
height: `calc(env(safe-area-inset-bottom, 0px) + 64px)`,
|
||||
}}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8 flex-1">
|
||||
<div className="relative flex items-center justify-between h-16">
|
||||
<div
|
||||
className="absolute inset-y-0 left-0 flex items-center sm:hidden"
|
||||
onClick={() => actions.setMenuOpen(true)}
|
||||
>
|
||||
{/* Mobile menu button*/}
|
||||
<button
|
||||
className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span className="sr-only">Open main menu</span>
|
||||
{/* Icon when menu is closed. */}
|
||||
{/*
|
||||
Heroicon name: menu
|
||||
|
||||
Menu open: "hidden", Menu closed: "block"
|
||||
*/}
|
||||
<svg
|
||||
className="block h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
</svg>
|
||||
{/* Icon when menu is open. */}
|
||||
{/*
|
||||
Heroicon name: x
|
||||
|
||||
Menu open: "block", Menu closed: "hidden"
|
||||
*/}
|
||||
<svg
|
||||
className="hidden h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 flex items-center justify-center sm:items-stretch sm:justify-start">
|
||||
<div className="flex-shrink-0 flex items-center">
|
||||
<h1 className="text-gray-50">{title}</h1>
|
||||
</div>
|
||||
<div className="hidden sm:block sm:ml-6">
|
||||
<div className="flex space-x-4">
|
||||
{/* Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" */}
|
||||
<a
|
||||
href="#"
|
||||
className="bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium"
|
||||
>
|
||||
Dashboard
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
|
||||
>
|
||||
Team
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
|
||||
>
|
||||
Projects
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
|
||||
>
|
||||
Calendar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
|
||||
<button
|
||||
className="bg-gray-800 p-1 rounded-full text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
|
||||
onClick={() => actions.setNotificationsOpen(true)}
|
||||
>
|
||||
<span className="sr-only">View notifications</span>
|
||||
{/* Heroicon name: bell */}
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Profile dropdown */}
|
||||
<div className="ml-3 relative">
|
||||
<div>
|
||||
<button
|
||||
className="bg-gray-800 flex text-sm rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
|
||||
id="user-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={() => setShowProfileMenu(!showProfileMenu)}
|
||||
>
|
||||
<span className="sr-only">Open user menu</span>
|
||||
<img
|
||||
className="h-8 w-8 rounded-full"
|
||||
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
alt=""
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{/*
|
||||
Profile dropdown panel, show/hide based on dropdown state.
|
||||
|
||||
Entering: "transition ease-out duration-100"
|
||||
From: "transform opacity-0 scale-95"
|
||||
To: "transform opacity-100 scale-100"
|
||||
Leaving: "transition ease-in duration-75"
|
||||
From: "transform opacity-100 scale-100"
|
||||
To: "transform opacity-0 scale-95"
|
||||
*/}
|
||||
<div
|
||||
className={`${
|
||||
showProfileMenu ? '' : 'hidden'
|
||||
} origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5`}
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="user-menu"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-400"
|
||||
role="menuitem"
|
||||
>
|
||||
Your Profile
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-400"
|
||||
role="menuitem"
|
||||
>
|
||||
Settings
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-400"
|
||||
role="menuitem"
|
||||
>
|
||||
Sign out
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
@@ -1,5 +0,0 @@
|
||||
# UI Components
|
||||
|
||||
These components are a mini-tailwind UI framework for building native mobile apps with web technologies.
|
||||
|
||||
These components are meant to be modified and customized to fit your app, and provide many common mobile UI patterns.
|
||||
@@ -1,43 +0,0 @@
|
||||
import { createContext, useEffect, useState } from 'react';
|
||||
|
||||
export const SafeAreaContext = createContext({ top: 0, bottom: 0 });
|
||||
|
||||
// This provider reads and stores the computed safe area
|
||||
// on devices with notches/etc. (iPhone X, for example)
|
||||
//
|
||||
// This is done by reading the CSS Properties --safe-area-top and --safe-area-bottom
|
||||
// and then storing them as state values.
|
||||
//
|
||||
// These values are useful for JS-driven interactions, such as a modal that
|
||||
// will drag in and out but needs to offset for the safe region.
|
||||
export const SafeAreaProvider = ({ children }) => {
|
||||
const [safeArea, setSafeArea] = useState({ top: 0, right: 0, bottom: 0, left: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
// I don't know why, but we can't get the value of this CSS variable
|
||||
// until a bit of a delay, maybe something with Next?
|
||||
setTimeout(() => {
|
||||
const safeAreaTop = parseInt(
|
||||
window.getComputedStyle(document.documentElement).getPropertyValue('--safe-area-top')
|
||||
);
|
||||
const safeAreaBottom = parseInt(
|
||||
window.getComputedStyle(document.documentElement).getPropertyValue('--safe-area-bottom')
|
||||
);
|
||||
const safeAreaLeft = parseInt(
|
||||
window.getComputedStyle(document.documentElement).getPropertyValue('--safe-area-left')
|
||||
);
|
||||
const safeAreaRight = parseInt(
|
||||
window.getComputedStyle(document.documentElement).getPropertyValue('--safe-area-right')
|
||||
);
|
||||
|
||||
setSafeArea({
|
||||
top: safeAreaTop,
|
||||
right: safeAreaRight,
|
||||
bottom: safeAreaBottom,
|
||||
left: safeAreaLeft,
|
||||
});
|
||||
}, 500);
|
||||
}, []);
|
||||
|
||||
return <SafeAreaContext.Provider value={safeArea}>{children}</SafeAreaContext.Provider>;
|
||||
};
|
||||
Reference in New Issue
Block a user