Skip to content

Commit 42bcbf5

Browse files
authored
add persistent view toggle (codesandbox#4009)
1 parent f4a3997 commit 42bcbf5

File tree

6 files changed

+104
-3
lines changed

6 files changed

+104
-3
lines changed

packages/app/src/app/overmind/namespaces/dashboard/actions.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@ import {
99
import Fuse from 'fuse.js';
1010
import { OrderBy, sandboxesTypes } from './state';
1111

12+
const VIEW_MODE_DASHBOARD = 'VIEW_MODE_DASHBOARD';
13+
1214
// DELETE WHEN NEW DASHBOARD ONLINE
1315
export const dashboardMounted: AsyncAction = async (context, value) => {
1416
await withLoadApp()(context, value);
1517
};
1618

19+
export const newDashboardMounted: Action = ({ state, effects }) => {
20+
const localStorageViewMode = effects.browser.storage.get(VIEW_MODE_DASHBOARD);
21+
if (localStorageViewMode) {
22+
state.dashboard.viewMode = localStorageViewMode;
23+
}
24+
};
1725
export const sandboxesSelected: Action<{
1826
sandboxIds: string[];
1927
}> = ({ state }, { sandboxIds }) => {
@@ -78,6 +86,14 @@ export const searchChanged: Action<{ search: string }> = (
7886
state.dashboard.filters.search = search;
7987
};
8088

89+
export const viewModeChanged: Action<{ mode: 'grid' | 'list' }> = (
90+
{ state, effects },
91+
{ mode }
92+
) => {
93+
state.dashboard.viewMode = mode;
94+
effects.browser.storage.set(VIEW_MODE_DASHBOARD, mode);
95+
};
96+
8197
export const createSandboxClicked: AsyncAction<{
8298
body: { collectionId: string };
8399
sandboxId: string;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
3+
export const GridIcon = props => (
4+
<svg width={15} height={14} fill="none" viewBox="0 0 15 14" {...props}>
5+
<path
6+
fill={props.active ? '#fff' : '#757575'}
7+
fillRule="evenodd"
8+
d="M6.709 0H.65v6H6.71V0zm0 8H.65v6H6.71V8zm2.02 0h6.057v6H8.728V8zm6.057-8H8.728v6h6.058V0z"
9+
clipRule="evenodd"
10+
/>
11+
</svg>
12+
);
13+
14+
export const ListIcon = props => (
15+
<svg width={15} height={15} fill="none" viewBox="0 0 10 10" {...props}>
16+
<path
17+
fill={props.active ? '#fff' : '#757575'}
18+
fillRule="evenodd"
19+
d="M2.5 0H0v2.5h2.5V0zm0 3.75H0v2.5h2.5v-2.5zM0 7.5h2.5V10H0V7.5zm10-3.75H3.75v2.5H10v-2.5zM3.75 7.5H10V10H3.75V7.5zM10 0H3.75v2.5H10V0z"
20+
clipRule="evenodd"
21+
/>
22+
</svg>
23+
);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React, { FunctionComponent } from 'react';
2+
import { useOvermind } from 'app/overmind';
3+
import { Text, Menu, Stack } from '@codesandbox/components';
4+
import { GridIcon, ListIcon } from './icons';
5+
6+
const STATES: { name: string; key: 'grid' | 'list'; icon: any }[] = [
7+
{
8+
name: 'Grid View',
9+
key: 'grid',
10+
icon: GridIcon,
11+
},
12+
{
13+
name: 'List View',
14+
key: 'list',
15+
icon: ListIcon,
16+
},
17+
];
18+
19+
export const ViewOptions: FunctionComponent = () => {
20+
const {
21+
actions: {
22+
dashboard: { viewModeChanged },
23+
},
24+
state: {
25+
dashboard: { viewMode },
26+
},
27+
} = useOvermind();
28+
29+
return (
30+
<>
31+
<Menu>
32+
<Menu.Button>
33+
{viewMode === 'grid' ? <GridIcon /> : <ListIcon />}
34+
</Menu.Button>
35+
<Menu.List>
36+
{STATES.map(viewState => (
37+
<Menu.Item
38+
field={viewState.key}
39+
onSelect={() => viewModeChanged({ mode: viewState.key })}
40+
>
41+
<Stack gap={4} align="center" justify="space-between">
42+
<Text variant={viewMode === viewState.key ? 'body' : 'muted'}>
43+
{viewState.name}
44+
</Text>
45+
{viewState.icon({
46+
width: 10,
47+
active: viewMode === viewState.key,
48+
})}
49+
</Stack>
50+
</Menu.Item>
51+
))}
52+
</Menu.List>
53+
</Menu>
54+
</>
55+
);
56+
};
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import React from 'react';
22
import { Stack } from '@codesandbox/components';
33
import { FilterOptions } from './FilterOptions';
4+
import { ViewOptions } from './ViewOptions';
45
import { SortOptions } from './SortOptions';
56

67
export const Filters = ({ possibleTemplates }) => (
78
<Stack gap={4}>
89
<FilterOptions possibleTemplates={possibleTemplates} />
9-
1010
<SortOptions />
11+
<ViewOptions />
1112
</Stack>
1213
);

packages/app/src/app/pages/NewDashboard/Components/Sandbox/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const SandboxComponent = props => {
1212

1313
if (dashboard.viewMode === 'list' || props.match.path.includes('deleted')) {
1414
return (
15-
<Element style={{ gridColumnStart: 4 }}>
15+
<Element style={{ gridColumn: '1/-1' }}>
1616
<SandboxItem {...props} />
1717
</Element>
1818
);

packages/app/src/app/pages/NewDashboard/Content/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useEffect } from 'react';
22
import { Route, Switch, Redirect } from 'react-router-dom';
33
import { Element } from '@codesandbox/components';
44
import css from '@styled-system/css';
@@ -14,8 +14,13 @@ import { Search } from './routes/Search';
1414
export const Content = () => {
1515
const {
1616
state: { dashboard },
17+
actions,
1718
} = useOvermind();
1819

20+
useEffect(() => {
21+
actions.dashboard.newDashboardMounted();
22+
}, [actions.dashboard]);
23+
1924
return (
2025
<Element
2126
as="main"

0 commit comments

Comments
 (0)