feat: sidebar

This commit is contained in:
cha0s 2022-03-19 21:37:30 -05:00
parent 95a8335643
commit 71a86a586a
45 changed files with 614 additions and 156 deletions

View File

@ -2,36 +2,23 @@ import {
React,
hot,
} from '@flecks/react';
// import {
// Navigate,
// Route,
// Routes,
// } from '@flecks/react/router';
// import {useSelector} from '@flecks/redux';
// import {userIdSelector} from '@flecks/user';
import './global.scss';
import styles from './index.module.scss';
// import Dashboard from '../dashboard';
import Login from './login';
// import ProjectRoute from '../project/route';
import useLocalStorage from '../../hooks/use-local-storage';
import Resource from './resource';
import Sidebar from './sidebar';
// eslint-disable-next-line arrow-body-style
const Persea = () => {
// const isLoggedIn = useSelector(userIdSelector);
function Persea() {
const [expanded, setExpanded] = useLocalStorage('@persea/core/expanded', true);
return (
<div className={styles.persea}>
<Login />
{/* <Routes>
<Route exact path="/" element={<Navigate to={isLoggedIn ? '/dashboard' : '/login'} />} />
<Route path="/login" element={isLoggedIn ? <Navigate to="/" /> : <UserLocalLogin />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/project/*" element={<ProjectRoute />} />
</Routes> */}
<Sidebar expanded={expanded} setExpanded={setExpanded} />
<Resource expanded={expanded} />
</div>
);
};
}
Persea.displayName = 'Persea';

View File

@ -1,5 +1,5 @@
.persea {
display: flex;
height: 100vh;
width: 100vw;
}

View File

@ -0,0 +1,36 @@
import {classnames, PropTypes, React} from '@flecks/react';
import {useSelector} from '@flecks/redux';
import styles from './index.module.scss';
import {projectsSelector} from '../../../state/projects';
import Login from './login';
function Resource({expanded}) {
const {structure} = useSelector(projectsSelector);
const projects = Object.entries(structure);
return (
<div className={classnames(styles.resource, expanded && styles.expanded)}>
{
projects.length > 0
? <p>{projects.length}</p>
: (
<div className={styles.login}>
<p>You must log in to access your projects.</p>
<Login />
</div>
)
}
</div>
);
}
Resource.defaultProps = {};
Resource.displayName = 'Resource';
Resource.propTypes = {
expanded: PropTypes.bool.isRequired,
};
export default Resource;

View File

@ -0,0 +1,16 @@
.resource {
background-color: #1a1a1a;
height: 100%;
width: calc(100% - 3rem);
&.expanded {
width: calc(100% - 25rem);
}
.login {
p {
margin: 3em;
text-align: center;
}
}
}

View File

@ -6,10 +6,6 @@ import styles from './index.module.scss';
function Login() {
return (
<div className={styles.login}>
<div>
<h2>Sign in</h2>
<p>You must sign in to continue</p>
</div>
<UserLocalLogin />
</div>
);

View File

@ -1,16 +1,6 @@
.login {
margin: auto;
max-width: 60%;
padding-top: 5%;
> div {
text-align: center;
padding-bottom: 2em;
h2 {
padding-bottom: 1em;
}
}
form {
background-color: #272727;
@ -38,6 +28,7 @@
div {
align-items: center;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
[type="checkbox"] {
@ -48,8 +39,9 @@
}
label {
display: block;
display: inline-block;
flex-direction: row;
width: auto;
input, span {
margin: 0;
@ -57,7 +49,7 @@
}
a {
padding: 1em 1em 1em 0;
padding: 1em;
text-decoration: none;
white-space: nowrap;
}

View File

@ -0,0 +1,27 @@
import {PropTypes, React} from '@flecks/react';
import styles from './index.module.scss';
function Content({Content, name}) {
return (
<div className={styles.content}>
{Content && (
<>
<p className={styles.name}>{name}</p>
{Content}
</>
)}
</div>
);
}
Content.defaultProps = {};
Content.displayName = 'Content';
Content.propTypes = {
Content: PropTypes.node.isRequired,
name: PropTypes.string.isRequired,
};
export default Content;

View File

@ -0,0 +1,14 @@
.content {
background-color: #272727;
height: 100%;
overflow: auto;
width: calc(100% - 3rem);
}
.name {
color: #ccc;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
font-size: 0.8em;
padding: 1em;
text-transform: uppercase;
}

View File

@ -0,0 +1,41 @@
import {classnames, PropTypes, React} from '@flecks/react';
import styles from './index.module.scss';
function Icon({
className,
isActive,
name,
onClick,
}) {
return (
<label
className={styles.label}
key={name}
>
<span>{name}</span>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<button
className={classnames(styles.icon, className, isActive && styles.active)}
onClick={onClick}
type="button"
/>
</label>
);
}
Icon.defaultProps = {
className: '',
onClick: () => {},
};
Icon.displayName = 'Icon';
Icon.propTypes = {
className: PropTypes.string,
isActive: PropTypes.bool.isRequired,
name: PropTypes.string.isRequired,
onClick: PropTypes.func,
};
export default Icon;

View File

@ -0,0 +1,32 @@
.icon {
background-color: #333;
background-position: center;
background-repeat: no-repeat;
background-size: 70%;
border: 0;
border-left: 2px solid transparent;
height: 3rem;
margin: 0;
padding: 0;
width: 3rem;
&:focus {
box-shadow: none;
}
&:hover {
background-color: #333;
}
&.active {
border-left: 2px solid white;
}
}
.label {
padding: 0;
span {
display: none;
}
}

View File

@ -0,0 +1,21 @@
import {PropTypes, React} from '@flecks/react';
import styles from './index.module.scss';
function Icons({Icons}) {
return (
<div className={styles.icons}>
{Icons}
</div>
);
}
Icons.defaultProps = {};
Icons.displayName = 'Icons';
Icons.propTypes = {
Icons: PropTypes.arrayOf(PropTypes.node).isRequired,
};
export default Icons;

View File

@ -0,0 +1,5 @@
.icons {
background-color: #333;
width: 3rem;
height: 100%;
}

View File

@ -0,0 +1,58 @@
import {
classnames,
PropTypes,
React,
useFlecks,
} from '@flecks/react';
import styles from './index.module.scss';
import useLocalStorage from '../../../hooks/use-local-storage';
import ContentComponent from './content';
import IconComponent from './icon';
import IconsComponent from './icons';
function Sidebar({expanded, setExpanded}) {
const flecks = useFlecks();
const sidebarImplementations = flecks.invokeMerge('@persea/core.sidebar');
const [sidebar, setSidebar] = useLocalStorage(
'@persea/core/sidebar',
Object.keys(sidebarImplementations).shift(),
);
const Icons = Object.entries(sidebarImplementations)
.map(([key, {Icon}]) => (
<IconComponent
className={Icon}
isActive={expanded && sidebar === key}
key={key}
name={key}
onClick={() => {
if (key === sidebar) {
setExpanded(!expanded);
}
else if (!expanded) {
setExpanded(true);
}
setSidebar(key);
}}
/>
));
const {Content} = sidebarImplementations[sidebar];
return (
<div className={classnames(styles.sidebar, expanded && styles.expanded)}>
<IconsComponent Icons={Icons} />
<ContentComponent Content={expanded && <Content />} name={sidebar} />
</div>
);
}
Sidebar.defaultProps = {};
Sidebar.displayName = 'Sidebar';
Sidebar.propTypes = {
expanded: PropTypes.bool.isRequired,
setExpanded: PropTypes.func.isRequired,
};
export default Sidebar;

View File

@ -0,0 +1,10 @@
.sidebar {
display: flex;
height: 100%;
width: 3rem;
&.expanded {
max-width: 100%;
width: 25rem;
}
}

View File

@ -1,33 +0,0 @@
import {
classnames,
React,
} from '@flecks/react';
import locals from './index.module.scss';
function Bottom() {
const buttons = [
<button className={classnames(locals.organization)} type="button">
<svg height="512pt" viewBox="0 0 512 512" width="512pt" xmlns="http://www.w3.org/2000/svg">
<path d="m512 312v-160h-230v62h-152v-54h100v-160h-230v160h90v294h192v58h230v-160h-230v62h-152v-160h152v58zm-472-272h150v80h-150zm282 352h150v80h-150zm0-200h150v80h-150zm0 0" />
</svg>
</button>,
// eslint-disable-next-line jsx-a11y/control-has-associated-label
<button className={classnames(locals.resource, 'active')} type="button" />,
];
return (
<div className={locals.bottom}>
<div className={locals.buttons}>
{buttons}
</div>
</div>
);
}
Bottom.defaultProps = {};
Bottom.displayName = 'Bottom';
Bottom.propTypes = {};
export default Bottom;

View File

@ -1,46 +0,0 @@
.bottom {
height: 3rem;
width: 100%;
}
.buttons {
align-items: center;
display: flex;
height: 100%;
justify-content: space-around;
button {
background-color: transparent;
border: 2px transparent;
height: 2em;
padding: 0.5rem;
width: 2em;
&:focus, &:hover {
background-color: transparent;
box-shadow: none;
}
&:global(.active) {
border-bottom: 1px solid #009fb4;
}
svg {
fill: #999;
height: 100%;
width: 100%;
}
}
}
button.side {
padding: 0.35rem;
}
button.resource {
background-image: url('./map.png');
background-origin: content-box;
background-repeat: no-repeat;
background-size: cover;
padding: 0.25rem;
}

View File

@ -1 +0,0 @@
<svg id="Layer_1" enable-background="new 0 0 512 512" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g><path d="m113.147 53.166h-2.175v-22.456c0-16.934-13.777-30.71-30.788-30.71-16.934 0-30.71 13.776-30.71 30.71v22.456c-22.985-1.145-43.857 15.46-43.857 39.432v123.282c0 24.002 20.867 40.512 43.856 39.431v225.979c0 16.934 13.776 30.71 30.788 30.71 16.934 0 30.71-13.776 30.71-30.71v-225.979c22.21.781 41.606-16.895 41.606-39.431v-42.882c0-9.434-15.078-9.883-15.078.002 0 14.287.078 28.588.078 42.88 0 13.471-10.959 24.431-24.431 24.431h-68.098c-13.471 0-24.431-10.96-24.431-24.431v-123.282c0-13.472 10.959-24.432 24.431-24.432h68.099c13.471 0 24.431 10.96 24.431 24.432 0 13.466-.078 26.941-.078 40.402 0 9.459 15.078 9.869 15.078.004v-40.406c0-21.743-17.689-39.432-39.431-39.432zm-17.175 428.124c0 8.662-7.047 15.71-15.788 15.71-8.663 0-15.71-7.048-15.71-15.71v-225.979h31.498zm-31.498-450.58c0-8.662 7.047-15.71 15.788-15.71 8.663 0 15.71 7.048 15.71 15.71v22.456h-31.498z"/><path d="m290.612 255.094h-3.3v-224.384c0-16.934-13.776-30.71-30.788-30.71-16.934 0-30.71 13.776-30.71 30.71v224.384c-22.604-.988-42.731 16.189-42.731 39.431v123.283c0 23.349 20.118 40.263 42.731 39.432v24.051c0 16.934 13.777 30.71 30.788 30.71 16.934 0 30.71-13.776 30.71-30.71v-24.051c22.604.988 42.731-16.189 42.731-39.432v-123.284c0-21.742-17.689-39.43-39.431-39.43zm-49.799-224.384c0-8.662 7.048-15.71 15.788-15.71 8.663 0 15.71 7.048 15.71 15.71v224.384h-31.498zm31.499 450.58c0 8.662-7.047 15.71-15.788 15.71-8.663 0-15.71-7.048-15.71-15.71v-24.051h31.498zm42.731-63.482c0 13.472-10.96 24.432-24.431 24.432h-68.099c-13.471 0-24.431-10.96-24.431-24.432v-123.284c0-13.471 10.959-24.431 24.431-24.431h68.099c13.471 0 24.431 10.96 24.431 24.431z"/><path d="m466.952 139.191h-3.3v-108.481c0-16.934-13.776-30.71-30.788-30.71-16.934 0-30.71 13.776-30.71 30.71v108.481c-22.604-.988-42.731 16.19-42.731 39.432v123.282c0 23.349 20.118 40.263 42.731 39.432v139.953c0 16.934 13.777 30.71 30.788 30.71 16.934 0 30.71-13.776 30.71-30.71v-44.29c0-9.697-15-9.697-15 0v44.29c0 8.662-7.047 15.71-15.788 15.71-8.663 0-15.71-7.048-15.71-15.71v-139.953h31.498v52.663c0 9.697 15 9.697 15 0v-52.663c22.604.988 42.731-16.189 42.731-39.432v-123.282c0-21.742-17.689-39.432-39.431-39.432zm-49.799-108.481c0-8.662 7.048-15.71 15.788-15.71 8.663 0 15.71 7.048 15.71 15.71v108.481h-31.498zm74.23 271.195c0 13.472-10.96 24.432-24.431 24.432h-68.099c-13.471 0-24.431-10.96-24.431-24.432v-123.282c0-13.472 10.959-24.432 24.431-24.432h68.099c13.471 0 24.431 10.96 24.431 24.432z"/><path d="m102.347 100.484h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/><path d="m102.347 146.738h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/><path d="m102.347 192.993h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/><path d="m279.812 302.412h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/><path d="m279.812 348.666h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/><path d="m279.812 394.921h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/><path d="m456.151 186.51h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/><path d="m456.151 232.764h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/><path d="m456.151 279.019h-46.498c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h46.498c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5z"/></g></svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,18 @@
import {
useLayoutEffect,
useState,
} from '@flecks/react';
const useLocalStorage = (key, defaultValue) => {
const [value, setValue] = useState(defaultValue);
useLayoutEffect(() => {
setValue(JSON.parse(window.localStorage.getItem(key) || JSON.stringify(defaultValue)));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key]);
useLayoutEffect(() => {
window.localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};
export default useLocalStorage;

View File

@ -2,7 +2,9 @@ import {patchJsonResource} from '@avocado/resource-persea';
import {Hooks} from '@flecks/core';
import Persea from './components/persea';
import {projects} from './state';
import organization from './sidebar/organization';
import projectsSidebar from './sidebar/projects';
import {project, projects} from './state';
export * from './state';
@ -18,7 +20,12 @@ export default {
};
},
'@flecks/redux.slices': () => ({
project,
projects,
}),
'@persea/core.sidebar': () => ({
projects: projectsSidebar,
organization,
}),
},
};

View File

@ -0,0 +1,29 @@
import {React} from '@flecks/react';
import {useSelector} from '@flecks/redux';
import styles from './content.module.scss';
import {projectSelector, structureSelector} from '../../state';
import Organization from './organization';
function Content() {
const project = useSelector(projectSelector) || 'c41ddaac-89c2-46a4-b3e5-1d634a1a7c36';
const {label, resourcePaths} = useSelector((state) => structureSelector(state, project));
return (
<div className={styles.content}>
<Organization
label={label}
uuid={project}
resourcePaths={resourcePaths}
/>
</div>
);
}
Content.defaultProps = {};
Content.displayName = 'Content';
Content.propTypes = {};
export default Content;

View File

@ -0,0 +1,6 @@
.content {
}
.icon {
background-image: url('./icon_document.svg');
}

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g>
<g>
<polygon fill="#5981C1" stroke="#4766B0" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
36.766,12.169 9.352,12.169 9.352,60.169 48.504,60.169 48.504,23.907 "/>
<polygon fill="#5981C1" stroke="#4766B0" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
36.766,23.907 48.504,23.907 36.766,12.169 "/>
</g>
<g>
<polygon fill="#8CAEDC" stroke="#4766B0" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
45.103,3.831 17.689,3.831 17.689,51.831 56.841,51.831 56.841,15.569 "/>
<polygon fill="#5981C1" stroke="#4766B0" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
45.103,15.569 56.841,15.569 45.103,3.831 "/>
</g>
</g>
<g>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="33.161" y1="15.787" x2="37.931" y2="15.787"/>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="33.161" y1="20.288" x2="50.919" y2="20.288"/>
<rect x="23.644" y="15.787" fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="4.5" height="4.5"/>
</g>
<g>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="33.161" y1="25.581" x2="37.931" y2="25.581"/>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="33.161" y1="30.082" x2="50.919" y2="30.082"/>
<rect x="23.644" y="25.581" fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="4.5" height="4.5"/>
</g>
<g>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="33.161" y1="36.515" x2="37.931" y2="36.515"/>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="33.161" y1="41.015" x2="50.919" y2="41.015"/>
<rect x="23.644" y="36.515" fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="4.5" height="4.5"/>
</g>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="23.644" y1="9.7" x2="37.931" y2="9.7"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,8 @@
import styles from './content.module.scss';
import Content from './content';
export default {
Content,
Icon: styles?.icon,
};

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 312 B

View File

Before

Width:  |  Height:  |  Size: 832 B

After

Width:  |  Height:  |  Size: 832 B

View File

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 429 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -6,7 +6,6 @@ import {
PropTypes,
React,
useFlecks,
useState,
} from '@flecks/react';
import {
push,
@ -17,9 +16,11 @@ import {
useDispatch,
} from '@flecks/redux';
import OrganizationActions from './actions';
import './index.scss';
import useLocalStorage from '../../../hooks/use-local-storage';
import OrganizationActions from './actions';
const normalizeDisplayName = (displayName) => (
displayName.startsWith('HotExported') ? displayName.slice(11) : displayName
);
@ -33,7 +34,7 @@ const Organization = ({
const dispatch = useDispatch();
const flecks = useFlecks();
const location = useLocation();
const [collapsed, setCollapsed] = useState({});
const [collapsed, setCollapsed] = useLocalStorage('@persea/core/collapsed', {});
const treeFromResourcePath = (tree, uuid, [isFile, resourcePath]) => {
const parts = resourcePath.split('/');
parts.shift();

View File

@ -1,5 +1,5 @@
.organization {
background-color: #222;
// background-color: #222;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
@ -10,37 +10,41 @@
overflow: hidden;
text-overflow: ellipsis;
}
.label.inline {
justify-content: flex-start;
> div:first-child.icon {
background-size: cover;
flex-grow: unset;
margin-right: 0.125em;
width: 1em;
height: 1em;
&.AudioComponent {
background-image: url('./img/sound.png');
}
&.Binary {
background-image: url('./img/question.png');
}
&.EntityComponent {
background-image: url('./img/joystick.png');
}
&.ImageComponent {
background-image: url('./img/image.png');
}
&.RoomComponent {
background-image: url('./img/map.png');
}
&.ScriptComponent {
background-image: url('./img/script.png');
}
&.SoundComponent {
background-image: url('./img/music.png');
}
&.TextComponent {
background-image: url('./img/note.png');
.label {
background-color: transparent;
&.inline {
font-size: 0.85em;
justify-content: flex-start;
> div:first-child.icon {
background-size: cover;
flex-grow: unset;
margin-right: 0.125em;
width: 1em;
height: 1em;
&.AudioComponent {
background-image: url('./img/sound.png');
}
&.Binary {
background-image: url('./img/question.png');
}
&.EntityComponent {
background-image: url('./img/joystick.png');
}
&.ImageComponent {
background-image: url('./img/image.png');
}
&.RoomComponent {
background-image: url('./img/map.png');
}
&.ScriptComponent {
background-image: url('./img/script.png');
}
&.SoundComponent {
background-image: url('./img/music.png');
}
&.TextComponent {
background-image: url('./img/note.png');
}
}
}
}

View File

@ -0,0 +1,49 @@
import {React} from '@flecks/react';
import {useSelector} from '@flecks/redux';
import styles from './content.module.scss';
import {projectSelector, projectsSelector} from '../../state';
function Content() {
const project = useSelector(projectSelector) || 'c41ddaac-89c2-46a4-b3e5-1d634a1a7c36';
const {structure} = useSelector(projectsSelector);
const projects = Object.entries(structure);
return (
<div className={styles.content}>
{
projects.length > 0
? (
<ul>
{
projects
.map(([uuid, {label}]) => (
<li className={project === uuid && styles.active} key={uuid}>
<button
type="button"
>
<div className={styles.label}>{label}</div>
<div className={styles.uuid}>{uuid}</div>
</button>
</li>
))
}
</ul>
)
: (
<div className={styles.empty}>
<p>You don&apos;t have any projects.</p>
</div>
)
}
</div>
);
}
Content.defaultProps = {};
Content.displayName = 'Content';
Content.propTypes = {};
export default Content;

View File

@ -0,0 +1,44 @@
.content {
ul {
li {
border: 1px solid transparent;
&:hover {
background-color: rgba(255, 255, 255, 0.07);
}
&.active {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid var(--color-active);
&:hover {
background-color: rgba(255, 255, 255, 0.10);
}
}
label {
padding: 0.5em;
}
button {
background-color: transparent;
border: none;
margin: 0;
padding: 1em;
width: 100%;
.uuid {
font-size: 0.7em;
margin: 0.5rem auto 0 auto;
}
}
}
}
}
.empty {
p {
font-family: var(--message-font-family);
margin-top: 1em;
text-align: center;
}
}
.icon {
background-image: url('./icon_books.svg');
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g>
<g>
<rect x="6.125" y="8.75" fill="#8CAEDC" stroke="#4766B0" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="17.25" height="46.5"/>
<path fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M14.75,42.396L14.75,42.396c-2.131,0-3.875,1.744-3.875,3.875v0c0,2.131,1.744,3.875,3.875,3.875h0
c2.131,0,3.875-1.744,3.875-3.875v0C18.625,44.14,16.881,42.396,14.75,42.396z"/>
<g>
<rect x="10.875" y="14.417" fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="7.75" height="20.75"/>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="14.75" y1="18.46" x2="14.75" y2="31.124"/>
</g>
</g>
<g>
<rect x="23.375" y="8.75" fill="#5981C1" stroke="#4766B0" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="17.25" height="46.5"/>
<path fill="#8CAEDC" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M32,42.396L32,42.396c-2.131,0-3.875,1.744-3.875,3.875v0c0,2.131,1.744,3.875,3.875,3.875h0c2.131,0,3.875-1.744,3.875-3.875v0
C35.875,44.14,34.131,42.396,32,42.396z"/>
<g>
<rect x="28.125" y="14.417" fill="#8CAEDC" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="7.75" height="20.75"/>
<line fill="#8CAEDC" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="32" y1="18.46" x2="32" y2="31.124"/>
</g>
</g>
<g>
<rect x="40.625" y="8.75" fill="#8CAEDC" stroke="#4766B0" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="17.25" height="46.5"/>
<path fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M49.25,42.396L49.25,42.396c-2.131,0-3.875,1.744-3.875,3.875v0c0,2.131,1.744,3.875,3.875,3.875h0
c2.131,0,3.875-1.744,3.875-3.875v0C53.125,44.14,51.381,42.396,49.25,42.396z"/>
<g>
<rect x="45.375" y="14.417" fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" width="7.75" height="20.75"/>
<line fill="#5981C1" stroke="#4766B0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="49.25" y1="18.46" x2="49.25" y2="31.124"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,8 @@
import styles from './content.module.scss';
import Content from './content';
export default {
Content,
Icon: styles?.icon,
};

View File

@ -1,2 +1,5 @@
export * from './project';
export {default as project} from './project';
export * from './projects';
export {default as projects} from './projects';

View File

@ -0,0 +1,29 @@
import {
createSlice,
} from '@flecks/redux';
export const projectSelector = (state) => state.project;
const slice = createSlice({
name: 'persea/project',
initialState: null,
/* eslint-disable no-param-reassign */
reducers: {
setProject: (state, {payload}) => payload,
},
/* eslint-enable no-param-reassign */
});
slice.reducer.hydrateServer = async (req) => {
const {user} = req;
if (!user) {
return null;
}
const projects = await user.getProjects();
if (0 === projects.length) {
return null;
}
return projects[0].uuid;
};
export default slice.reducer;

View File

@ -100,9 +100,9 @@ slice.reducer.hydrateServer = async (req) => {
.map(async ([uuid, resourcePaths]) => {
let label;
try {
const buffer = await readFile(join(process.cwd(), 'projects', `${uuid}.json`));
const buffer = await readFile(join(process.cwd(), 'projects', uuid, 'package.json'));
const config = JSON.parse(buffer.toString());
label = config.name;
label = config.label || config.name;
}
catch (error) {
label = uuid;