Icon component

This commit is contained in:
Max Lynch
2020-12-22 17:27:45 -06:00
parent 5e2bd30d72
commit 91ca416c9f
12 changed files with 132 additions and 34 deletions
+2 -2
View File
@@ -21,8 +21,8 @@ const PostCard = ({ title, type, text, author, image }) => (
const Home = ({ selected }) => { const Home = ({ selected }) => {
return ( return (
<Content visible={selected}> <Content visible={selected}>
{homeItems.map(i => ( {homeItems.map((i, index) => (
<PostCard {...i} /> <PostCard {...i} key={index} />
))} ))}
</Content> </Content>
); );
+1 -1
View File
@@ -3,7 +3,7 @@ import classNames from 'classnames';
const Button = ({ children, className, ...props }) => ( const Button = ({ children, className, ...props }) => (
<button <button
{...props} {...props}
class={classNames( className={classNames(
'inline-block text-xs font-medium leading-6 text-center uppercase transition rounded-lg ripple focus:outline-none', 'inline-block text-xs font-medium leading-6 text-center uppercase transition rounded-lg ripple focus:outline-none',
className className
)} )}
+19
View File
@@ -0,0 +1,19 @@
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;
+9 -11
View File
@@ -11,17 +11,15 @@ const Menu = ({ open, onClose, children, className, ...props }) => {
const [dragging, setDragging] = useState(false); const [dragging, setDragging] = useState(false);
useEffect(() => { useEffect(() => {
try { if (open) {
if (open) { Plugins.StatusBar.setStyle({
Plugins.StatusBar.setStyle({ style: 'LIGHT',
style: 'LIGHT', }).catch(() => {});
}); } else {
} else { Plugins.StatusBar.setStyle({
Plugins.StatusBar.setStyle({ style: 'DARK',
style: 'DARK', }).catch(() => {});
}); }
}
} catch (e) {}
}, [open]); }, [open]);
useLayoutEffect(() => { useLayoutEffect(() => {
+4 -5
View File
@@ -4,18 +4,17 @@ import { useDrag } from 'react-use-gesture';
import Store from '../../store'; import Store from '../../store';
import { SafeAreaContext } from './SafeArea'; import { SafeAreaContext } from './SafeArea';
// A Modal window that slides in from offscreen and can be closed
// by dragging.
const Modal = ({ open, onClose, children }) => { const Modal = ({ open, onClose, children }) => {
const ref = useRef(); const ref = useRef();
const [dragging, setDragging] = useState(false); const [dragging, setDragging] = useState(false);
const [rect, setRect] = useState(null); const [rect, setRect] = useState(null);
const [y, setY] = useState(100000); const [y, setY] = useState(100000);
const { top } = useContext(SafeAreaContext); const { top: safeAreaTop } = useContext(SafeAreaContext);
const safeAreaTop = top;
const _open = useCallback(() => { const _open = useCallback(() => {
console.log('Opening modal!', safeAreaTop);
setY(safeAreaTop); setY(safeAreaTop);
}, [safeAreaTop]); }, [safeAreaTop]);
@@ -75,7 +74,7 @@ const Modal = ({ open, onClose, children }) => {
ref={ref} ref={ref}
{...bind()} {...bind()}
className={classNames( className={classNames(
'fixed z-40 top-5 transform transform-gpu ranslate w-full h-full bg-white rounded-t-xl', '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, 'ease-in-out duration-300 transition-transformation': !dragging,
} }
+1 -1
View File
@@ -9,7 +9,7 @@ const Nav = ({ page }) => {
useEffect(() => { useEffect(() => {
Plugins.StatusBar.setStyle({ Plugins.StatusBar.setStyle({
style: 'DARK', style: 'DARK',
}); }).catch(() => {});
}, []); }, []);
return ( return (
+8
View File
@@ -2,6 +2,14 @@ import { createContext, useEffect, useState } from 'react';
export const SafeAreaContext = createContext({ top: 0, bottom: 0 }); 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 }) => { export const SafeAreaProvider = ({ children }) => {
const [safeAreaTop, setSafeAreaTop] = useState(0); const [safeAreaTop, setSafeAreaTop] = useState(0);
const [safeAreaBottom, setSafeAreaBottom] = useState(0); const [safeAreaBottom, setSafeAreaBottom] = useState(0);
+18 -7
View File
@@ -1,13 +1,24 @@
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from './Icon';
const Tab = ({ title, icon, selected, selectedIcon, onClick }) => ( const Tab = ({ title, icon, selected, selectedIcon, onClick }) => (
<a onClick={onClick} href="#" className={classNames('px-6 rounded-md text-sm text-center font-medium cursor-pointer', { <a
'text-gray-500': !selected, onClick={onClick}
'text-gray-800': selected href="#"
})}> className={classNames('px-6 rounded-md text-sm text-center font-medium cursor-pointer', {
{icon && <ion-icon name={selected ? selectedIcon : icon } className="cursor-pointer" style={{ fontSize: '18px' }}/>} 'text-gray-500': !selected,
'text-gray-800': selected,
})}
>
{icon && (
<Icon
icon={selected ? selectedIcon : icon}
className="cursor-pointer"
style={{ fontSize: '18px' }}
/>
)}
<label className="block cursor-pointer">{title}</label> <label className="block cursor-pointer">{title}</label>
</a> </a>
) );
export default Tab; export default Tab;
+6
View File
@@ -2769,6 +2769,12 @@
} }
} }
}, },
"ionicons": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.2.3.tgz",
"integrity": "sha512-87qtgBkieKVFagwYA9Cf91B3PCahQbEOMwMt8bSvlQSgflZ4eE5qI4MGj2ZlIyadeX0dgo+0CzZsy3ow0CsBAg==",
"dev": true
},
"is-accessor-descriptor": { "is-accessor-descriptor": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+1
View File
@@ -22,6 +22,7 @@
"tailwindcss": "^2.0.2" "tailwindcss": "^2.0.2"
}, },
"devDependencies": { "devDependencies": {
"ionicons": "^5.2.3",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"pullstate": "^1.20.5", "pullstate": "^1.20.5",
"react-spring": "^8.0.27", "react-spring": "^8.0.27",
+23 -7
View File
@@ -1,5 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Virtuoso } from 'react-virtuoso'; import { Virtuoso } from 'react-virtuoso';
import { flash, flashOutline, cog, cogOutline, home, homeOutline } from 'ionicons/icons';
import Store from '../store'; import Store from '../store';
@@ -18,21 +19,22 @@ import { SafeAreaProvider } from '../components/ui/SafeArea';
import Home from '../components/pages/Home'; import Home from '../components/pages/Home';
import Feed from '../components/pages/Feed'; import Feed from '../components/pages/Feed';
import Settings from '../components/pages/Settings'; import Settings from '../components/pages/Settings';
import { useDrag } from 'react-use-gesture';
const pages = [ const pages = [
{ id: 'home', title: 'Home', icon: 'home-outline', selectedIcon: 'home', component: Home }, { id: 'home', title: 'Home', icon: homeOutline, selectedIcon: home, component: Home },
{ {
id: 'feed', id: 'feed',
title: 'Feed', title: 'Feed',
icon: 'flash-outline', icon: flashOutline,
selectedIcon: 'person', selectedIcon: flash,
component: Feed, component: Feed,
}, },
{ {
id: 'settings', id: 'settings',
title: 'Settings', title: 'Settings',
icon: 'cog-outline', icon: cogOutline,
selectedIcon: 'cog', selectedIcon: cog,
component: Settings, component: Settings,
}, },
]; ];
@@ -42,7 +44,7 @@ const CurrentPage = ({ page }) => {
<div className="flex-1 z-0 overflow-hidden relative"> <div className="flex-1 z-0 overflow-hidden relative">
{pages.map(p => { {pages.map(p => {
const Page = p.component; const Page = p.component;
return <Page selected={page.id === p.id} />; return <Page selected={page.id === p.id} key={p.id} />;
})} })}
</div> </div>
); );
@@ -133,10 +135,24 @@ export default function Index() {
}); });
}; };
const bind = useDrag(
({ down, movement: [mx] }) => {
console.log('DRAGGING SIDE', mx);
},
{
axis: 'x',
}
);
// This is an example app layout. We've got a hidden menu that will be toggled // This is an example app layout. We've got a hidden menu that will be toggled
// //
return ( return (
<App> <App
{...bind()}
style={{
touchAction: 'pan-x',
}}
>
<SafeAreaProvider> <SafeAreaProvider>
<Menu open={showMenu} onClose={closeMenu}> <Menu open={showMenu} onClose={closeMenu}>
<MenuContent /> <MenuContent />
+40
View File
@@ -12,3 +12,43 @@ body {
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
} }
/**
* Styles for the Icon UI component.
*
* TODO: Move these into a styled component or similar system if desired
*/
.ui-icon {
display: inline-block;
width: 1em;
height: 1em;
contain: strict;
fill: currentColor;
box-sizing: content-box !important;
}
.ui-icon .ionicon {
stroke: currentColor;
}
.ui-icon .ionicon-fill-none {
fill: none;
}
.ui-icon .ionicon-stroke-width {
stroke-width: 32px;
stroke-width: var(--ionicon-stroke-width, 32px);
}
.ui-icon .icon-inner,
.ui-icon .ionicon,
.ui-icon svg {
display: block;
height: 100%;
width: 100%;
}