Icon component
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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
@@ -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,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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const Nav = ({ page }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Plugins.StatusBar.setStyle({
|
Plugins.StatusBar.setStyle({
|
||||||
style: 'DARK',
|
style: 'DARK',
|
||||||
});
|
}).catch(() => {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
Generated
+6
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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 />
|
||||||
|
|||||||
@@ -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%;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user