diff --git a/packages/core/src/components/persea/index.jsx b/packages/core/src/components/persea/index.jsx
index e4a1a38..c0d0bc8 100644
--- a/packages/core/src/components/persea/index.jsx
+++ b/packages/core/src/components/persea/index.jsx
@@ -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 (
-
- {/*
- } />
- : } />
- } />
- } />
- */}
+
+
);
-};
+}
Persea.displayName = 'Persea';
diff --git a/packages/core/src/components/persea/index.module.scss b/packages/core/src/components/persea/index.module.scss
index b1586c3..0384176 100644
--- a/packages/core/src/components/persea/index.module.scss
+++ b/packages/core/src/components/persea/index.module.scss
@@ -1,5 +1,5 @@
-
.persea {
+ display: flex;
height: 100vh;
width: 100vw;
}
diff --git a/packages/core/src/components/persea/resource/index.jsx b/packages/core/src/components/persea/resource/index.jsx
new file mode 100644
index 0000000..589baf4
--- /dev/null
+++ b/packages/core/src/components/persea/resource/index.jsx
@@ -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 (
+
+ {
+ projects.length > 0
+ ?
{projects.length}
+ : (
+
+
You must log in to access your projects.
+
+
+ )
+ }
+
+ );
+}
+
+Resource.defaultProps = {};
+
+Resource.displayName = 'Resource';
+
+Resource.propTypes = {
+ expanded: PropTypes.bool.isRequired,
+};
+
+export default Resource;
diff --git a/packages/core/src/components/persea/resource/index.module.scss b/packages/core/src/components/persea/resource/index.module.scss
new file mode 100644
index 0000000..efbb350
--- /dev/null
+++ b/packages/core/src/components/persea/resource/index.module.scss
@@ -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;
+ }
+ }
+}
diff --git a/packages/core/src/components/persea/login/index.jsx b/packages/core/src/components/persea/resource/login/index.jsx
similarity index 78%
rename from packages/core/src/components/persea/login/index.jsx
rename to packages/core/src/components/persea/resource/login/index.jsx
index 6c0872c..49030c0 100644
--- a/packages/core/src/components/persea/login/index.jsx
+++ b/packages/core/src/components/persea/resource/login/index.jsx
@@ -6,10 +6,6 @@ import styles from './index.module.scss';
function Login() {
return (
-
-
Sign in
-
You must sign in to continue
-
);
diff --git a/packages/core/src/components/persea/login/index.module.scss b/packages/core/src/components/persea/resource/login/index.module.scss
similarity index 82%
rename from packages/core/src/components/persea/login/index.module.scss
rename to packages/core/src/components/persea/resource/login/index.module.scss
index 6c34019..f3ca7ff 100644
--- a/packages/core/src/components/persea/login/index.module.scss
+++ b/packages/core/src/components/persea/resource/login/index.module.scss
@@ -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;
}
diff --git a/packages/core/src/components/persea/sidebar/content/index.jsx b/packages/core/src/components/persea/sidebar/content/index.jsx
new file mode 100644
index 0000000..a3aab29
--- /dev/null
+++ b/packages/core/src/components/persea/sidebar/content/index.jsx
@@ -0,0 +1,27 @@
+import {PropTypes, React} from '@flecks/react';
+
+import styles from './index.module.scss';
+
+function Content({Content, name}) {
+ return (
+
+ {Content && (
+ <>
+
{name}
+ {Content}
+ >
+ )}
+
+ );
+}
+
+Content.defaultProps = {};
+
+Content.displayName = 'Content';
+
+Content.propTypes = {
+ Content: PropTypes.node.isRequired,
+ name: PropTypes.string.isRequired,
+};
+
+export default Content;
diff --git a/packages/core/src/components/persea/sidebar/content/index.module.scss b/packages/core/src/components/persea/sidebar/content/index.module.scss
new file mode 100644
index 0000000..f75a84b
--- /dev/null
+++ b/packages/core/src/components/persea/sidebar/content/index.module.scss
@@ -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;
+}
diff --git a/packages/core/src/components/persea/sidebar/icon/index.jsx b/packages/core/src/components/persea/sidebar/icon/index.jsx
new file mode 100644
index 0000000..811f8b1
--- /dev/null
+++ b/packages/core/src/components/persea/sidebar/icon/index.jsx
@@ -0,0 +1,41 @@
+import {classnames, PropTypes, React} from '@flecks/react';
+
+import styles from './index.module.scss';
+
+function Icon({
+ className,
+ isActive,
+ name,
+ onClick,
+}) {
+ return (
+
+ );
+}
+
+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;
diff --git a/packages/core/src/components/persea/sidebar/icon/index.module.scss b/packages/core/src/components/persea/sidebar/icon/index.module.scss
new file mode 100644
index 0000000..0ea8327
--- /dev/null
+++ b/packages/core/src/components/persea/sidebar/icon/index.module.scss
@@ -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;
+ }
+}
diff --git a/packages/core/src/components/persea/sidebar/icons/index.jsx b/packages/core/src/components/persea/sidebar/icons/index.jsx
new file mode 100644
index 0000000..bea8a16
--- /dev/null
+++ b/packages/core/src/components/persea/sidebar/icons/index.jsx
@@ -0,0 +1,21 @@
+import {PropTypes, React} from '@flecks/react';
+
+import styles from './index.module.scss';
+
+function Icons({Icons}) {
+ return (
+
+ {Icons}
+
+ );
+}
+
+Icons.defaultProps = {};
+
+Icons.displayName = 'Icons';
+
+Icons.propTypes = {
+ Icons: PropTypes.arrayOf(PropTypes.node).isRequired,
+};
+
+export default Icons;
diff --git a/packages/core/src/components/persea/sidebar/icons/index.module.scss b/packages/core/src/components/persea/sidebar/icons/index.module.scss
new file mode 100644
index 0000000..802a68a
--- /dev/null
+++ b/packages/core/src/components/persea/sidebar/icons/index.module.scss
@@ -0,0 +1,5 @@
+.icons {
+ background-color: #333;
+ width: 3rem;
+ height: 100%;
+}
diff --git a/packages/core/src/components/persea/sidebar/index.jsx b/packages/core/src/components/persea/sidebar/index.jsx
new file mode 100644
index 0000000..b5fbcc4
--- /dev/null
+++ b/packages/core/src/components/persea/sidebar/index.jsx
@@ -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}]) => (
+ {
+ if (key === sidebar) {
+ setExpanded(!expanded);
+ }
+ else if (!expanded) {
+ setExpanded(true);
+ }
+ setSidebar(key);
+ }}
+ />
+ ));
+ const {Content} = sidebarImplementations[sidebar];
+ return (
+
+
+ } name={sidebar} />
+
+ );
+}
+
+Sidebar.defaultProps = {};
+
+Sidebar.displayName = 'Sidebar';
+
+Sidebar.propTypes = {
+ expanded: PropTypes.bool.isRequired,
+ setExpanded: PropTypes.func.isRequired,
+};
+
+export default Sidebar;
diff --git a/packages/core/src/components/persea/sidebar/index.module.scss b/packages/core/src/components/persea/sidebar/index.module.scss
new file mode 100644
index 0000000..6f79671
--- /dev/null
+++ b/packages/core/src/components/persea/sidebar/index.module.scss
@@ -0,0 +1,10 @@
+.sidebar {
+ display: flex;
+ height: 100%;
+ width: 3rem;
+
+ &.expanded {
+ max-width: 100%;
+ width: 25rem;
+ }
+}
diff --git a/packages/core/src/components/project/bottom/index.jsx b/packages/core/src/components/project/bottom/index.jsx
deleted file mode 100644
index 711cb4b..0000000
--- a/packages/core/src/components/project/bottom/index.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import {
- classnames,
- React,
-} from '@flecks/react';
-
-import locals from './index.module.scss';
-
-function Bottom() {
- const buttons = [
- ,
- // eslint-disable-next-line jsx-a11y/control-has-associated-label
- ,
- ];
- return (
-
- );
-}
-
-Bottom.defaultProps = {};
-
-Bottom.displayName = 'Bottom';
-
-Bottom.propTypes = {};
-
-export default Bottom;
diff --git a/packages/core/src/components/project/bottom/index.module.scss b/packages/core/src/components/project/bottom/index.module.scss
deleted file mode 100644
index a7241b1..0000000
--- a/packages/core/src/components/project/bottom/index.module.scss
+++ /dev/null
@@ -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;
-}
diff --git a/packages/core/src/components/project/bottom/settings.svg b/packages/core/src/components/project/bottom/settings.svg
deleted file mode 100644
index 127f981..0000000
--- a/packages/core/src/components/project/bottom/settings.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/core/src/components/project/organization/img/map.png b/packages/core/src/components/project/organization/img/map.png
deleted file mode 100644
index 1f2d253..0000000
Binary files a/packages/core/src/components/project/organization/img/map.png and /dev/null differ
diff --git a/packages/core/src/hooks/use-local-storage.js b/packages/core/src/hooks/use-local-storage.js
new file mode 100644
index 0000000..283e49d
--- /dev/null
+++ b/packages/core/src/hooks/use-local-storage.js
@@ -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;
diff --git a/packages/core/src/index.js b/packages/core/src/index.js
index 8066dbb..0c8bc06 100644
--- a/packages/core/src/index.js
+++ b/packages/core/src/index.js
@@ -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,
+ }),
},
};
diff --git a/packages/core/src/sidebar/organization/content.jsx b/packages/core/src/sidebar/organization/content.jsx
new file mode 100644
index 0000000..5c5a062
--- /dev/null
+++ b/packages/core/src/sidebar/organization/content.jsx
@@ -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 (
+
+
+
+ );
+}
+
+Content.defaultProps = {};
+
+Content.displayName = 'Content';
+
+Content.propTypes = {};
+
+export default Content;
diff --git a/packages/core/src/sidebar/organization/content.module.scss b/packages/core/src/sidebar/organization/content.module.scss
new file mode 100644
index 0000000..0b23268
--- /dev/null
+++ b/packages/core/src/sidebar/organization/content.module.scss
@@ -0,0 +1,6 @@
+.content {
+}
+
+.icon {
+ background-image: url('./icon_document.svg');
+}
diff --git a/packages/core/src/sidebar/organization/icon_document.svg b/packages/core/src/sidebar/organization/icon_document.svg
new file mode 100644
index 0000000..e4107d5
--- /dev/null
+++ b/packages/core/src/sidebar/organization/icon_document.svg
@@ -0,0 +1,48 @@
+
+
+
diff --git a/packages/core/src/sidebar/organization/index.js b/packages/core/src/sidebar/organization/index.js
new file mode 100644
index 0000000..a62fb0e
--- /dev/null
+++ b/packages/core/src/sidebar/organization/index.js
@@ -0,0 +1,8 @@
+import styles from './content.module.scss';
+
+import Content from './content';
+
+export default {
+ Content,
+ Icon: styles?.icon,
+};
diff --git a/packages/core/src/components/project/organization/actions.jsx b/packages/core/src/sidebar/organization/organization/actions.jsx
similarity index 100%
rename from packages/core/src/components/project/organization/actions.jsx
rename to packages/core/src/sidebar/organization/organization/actions.jsx
diff --git a/packages/core/src/components/project/organization/img/collapse-all.svg b/packages/core/src/sidebar/organization/organization/img/collapse-all.svg
similarity index 100%
rename from packages/core/src/components/project/organization/img/collapse-all.svg
rename to packages/core/src/sidebar/organization/organization/img/collapse-all.svg
diff --git a/packages/core/src/components/project/organization/img/image.png b/packages/core/src/sidebar/organization/organization/img/image.png
similarity index 100%
rename from packages/core/src/components/project/organization/img/image.png
rename to packages/core/src/sidebar/organization/organization/img/image.png
diff --git a/packages/core/src/components/project/organization/img/joystick.png b/packages/core/src/sidebar/organization/organization/img/joystick.png
similarity index 100%
rename from packages/core/src/components/project/organization/img/joystick.png
rename to packages/core/src/sidebar/organization/organization/img/joystick.png
diff --git a/packages/core/src/components/project/bottom/map.png b/packages/core/src/sidebar/organization/organization/img/map.png
similarity index 100%
rename from packages/core/src/components/project/bottom/map.png
rename to packages/core/src/sidebar/organization/organization/img/map.png
diff --git a/packages/core/src/components/project/organization/img/music.png b/packages/core/src/sidebar/organization/organization/img/music.png
similarity index 100%
rename from packages/core/src/components/project/organization/img/music.png
rename to packages/core/src/sidebar/organization/organization/img/music.png
diff --git a/packages/core/src/components/project/organization/img/new-file.svg b/packages/core/src/sidebar/organization/organization/img/new-file.svg
similarity index 100%
rename from packages/core/src/components/project/organization/img/new-file.svg
rename to packages/core/src/sidebar/organization/organization/img/new-file.svg
diff --git a/packages/core/src/components/project/organization/img/new-folder.svg b/packages/core/src/sidebar/organization/organization/img/new-folder.svg
similarity index 100%
rename from packages/core/src/components/project/organization/img/new-folder.svg
rename to packages/core/src/sidebar/organization/organization/img/new-folder.svg
diff --git a/packages/core/src/components/project/organization/img/note.png b/packages/core/src/sidebar/organization/organization/img/note.png
similarity index 100%
rename from packages/core/src/components/project/organization/img/note.png
rename to packages/core/src/sidebar/organization/organization/img/note.png
diff --git a/packages/core/src/components/project/organization/img/question.png b/packages/core/src/sidebar/organization/organization/img/question.png
similarity index 100%
rename from packages/core/src/components/project/organization/img/question.png
rename to packages/core/src/sidebar/organization/organization/img/question.png
diff --git a/packages/core/src/components/project/organization/img/script.png b/packages/core/src/sidebar/organization/organization/img/script.png
similarity index 100%
rename from packages/core/src/components/project/organization/img/script.png
rename to packages/core/src/sidebar/organization/organization/img/script.png
diff --git a/packages/core/src/components/project/organization/img/sound.png b/packages/core/src/sidebar/organization/organization/img/sound.png
similarity index 100%
rename from packages/core/src/components/project/organization/img/sound.png
rename to packages/core/src/sidebar/organization/organization/img/sound.png
diff --git a/packages/core/src/components/project/organization/index.jsx b/packages/core/src/sidebar/organization/organization/index.jsx
similarity index 96%
rename from packages/core/src/components/project/organization/index.jsx
rename to packages/core/src/sidebar/organization/organization/index.jsx
index 5ce83e9..cc45523 100644
--- a/packages/core/src/components/project/organization/index.jsx
+++ b/packages/core/src/sidebar/organization/organization/index.jsx
@@ -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();
diff --git a/packages/core/src/components/project/organization/index.scss b/packages/core/src/sidebar/organization/organization/index.scss
similarity index 51%
rename from packages/core/src/components/project/organization/index.scss
rename to packages/core/src/sidebar/organization/organization/index.scss
index e8cbd90..86d04dd 100644
--- a/packages/core/src/components/project/organization/index.scss
+++ b/packages/core/src/sidebar/organization/organization/index.scss
@@ -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');
+ }
}
}
}
diff --git a/packages/core/src/sidebar/projects/content.jsx b/packages/core/src/sidebar/projects/content.jsx
new file mode 100644
index 0000000..62aa061
--- /dev/null
+++ b/packages/core/src/sidebar/projects/content.jsx
@@ -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 (
+
+ {
+ projects.length > 0
+ ? (
+
+ {
+ projects
+ .map(([uuid, {label}]) => (
+ -
+
+
+ ))
+ }
+
+ )
+ : (
+
+
You don't have any projects.
+
+ )
+ }
+
+ );
+}
+
+Content.defaultProps = {};
+
+Content.displayName = 'Content';
+
+Content.propTypes = {};
+
+export default Content;
diff --git a/packages/core/src/sidebar/projects/content.module.scss b/packages/core/src/sidebar/projects/content.module.scss
new file mode 100644
index 0000000..6dd53fd
--- /dev/null
+++ b/packages/core/src/sidebar/projects/content.module.scss
@@ -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');
+}
diff --git a/packages/core/src/sidebar/projects/icon_books.svg b/packages/core/src/sidebar/projects/icon_books.svg
new file mode 100644
index 0000000..140dbd2
--- /dev/null
+++ b/packages/core/src/sidebar/projects/icon_books.svg
@@ -0,0 +1,49 @@
+
+
+
diff --git a/packages/core/src/sidebar/projects/index.js b/packages/core/src/sidebar/projects/index.js
new file mode 100644
index 0000000..a62fb0e
--- /dev/null
+++ b/packages/core/src/sidebar/projects/index.js
@@ -0,0 +1,8 @@
+import styles from './content.module.scss';
+
+import Content from './content';
+
+export default {
+ Content,
+ Icon: styles?.icon,
+};
diff --git a/packages/core/src/state/index.js b/packages/core/src/state/index.js
index 7d747f8..a8de8a8 100644
--- a/packages/core/src/state/index.js
+++ b/packages/core/src/state/index.js
@@ -1,2 +1,5 @@
+export * from './project';
+export {default as project} from './project';
+
export * from './projects';
export {default as projects} from './projects';
diff --git a/packages/core/src/state/project.js b/packages/core/src/state/project.js
new file mode 100644
index 0000000..7f0f817
--- /dev/null
+++ b/packages/core/src/state/project.js
@@ -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;
diff --git a/packages/core/src/state/projects.js b/packages/core/src/state/projects.js
index 8e967ce..9a2a5bd 100644
--- a/packages/core/src/state/projects.js
+++ b/packages/core/src/state/projects.js
@@ -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;