aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-01-04 16:29:39 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-02-08 21:42:25 +0100
commit2d26e9f2ea26f9cc89e9aac8c09e132a5311f6a5 (patch)
tree1faab6e1af7ce89a2c797589a5aeea8d14b06496
parentfix: Make sure the BrowserView has integer coords (diff)
downloadsophie-2d26e9f2ea26f9cc89e9aac8c09e132a5311f6a5.tar.gz
sophie-2d26e9f2ea26f9cc89e9aac8c09e132a5311f6a5.tar.zst
sophie-2d26e9f2ea26f9cc89e9aac8c09e132a5311f6a5.zip
feat: Service switcher buttons
Currently, they do nothing. Signed-off-by: Kristóf Marussy <kristof@marussy.com>
-rw-r--r--packages/renderer/src/components/ServiceIcon.tsx53
-rw-r--r--packages/renderer/src/components/ServiceSwitcher.tsx88
-rw-r--r--packages/renderer/src/components/Sidebar.tsx13
-rw-r--r--packages/renderer/src/stores/RendererStore.ts10
4 files changed, 161 insertions, 3 deletions
diff --git a/packages/renderer/src/components/ServiceIcon.tsx b/packages/renderer/src/components/ServiceIcon.tsx
new file mode 100644
index 0000000..e02da71
--- /dev/null
+++ b/packages/renderer/src/components/ServiceIcon.tsx
@@ -0,0 +1,53 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import { styled } from '@mui/material/styles';
22import React from 'react';
23
24const ServiceIconRoot = styled('div', {
25 name: 'ServiceIcon',
26 slot: 'Root',
27})(({ theme }) => ({
28 width: 34,
29 height: 34,
30 borderRadius: theme.shape.borderRadius,
31 background: 'currentColor',
32 display: 'flex',
33 justifyContent: 'center',
34 alignItems: 'center',
35}));
36
37const ServiceIconText = styled('div', {
38 name: 'ServiceIcon',
39 slot: 'Text',
40})(({ theme }) => ({
41 display: 'inline-block',
42 flex: 0,
43 fontSize: theme.typography.pxToRem(24),
44 color: theme.palette.primary.contrastText,
45}));
46
47export default function ServiceIcon({ name }: { name: string }): JSX.Element {
48 return (
49 <ServiceIconRoot>
50 <ServiceIconText>{name.length > 0 ? name[0] : '?'}</ServiceIconText>
51 </ServiceIconRoot>
52 );
53}
diff --git a/packages/renderer/src/components/ServiceSwitcher.tsx b/packages/renderer/src/components/ServiceSwitcher.tsx
new file mode 100644
index 0000000..b454451
--- /dev/null
+++ b/packages/renderer/src/components/ServiceSwitcher.tsx
@@ -0,0 +1,88 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import Tab from '@mui/material/Tab';
22import Tabs from '@mui/material/Tabs';
23import { alpha, styled } from '@mui/material/styles';
24import { observer } from 'mobx-react-lite';
25import React, { useState } from 'react';
26
27import ServiceIcon from './ServiceIcon';
28import { useStore } from './StoreProvider';
29
30const ServiceSwitcherRoot = styled(Tabs, {
31 name: 'ServiceSwitcher',
32 slot: 'Root',
33})(({ theme }) => ({
34 '.MuiTabs-indicator': {
35 width: 3,
36 ...(theme.direction === 'ltr'
37 ? {
38 left: 0,
39 right: 'auto',
40 }
41 : {
42 left: 'auto',
43 right: 0,
44 }),
45 },
46}));
47
48const ServiceSwitcherTab = styled(Tab, {
49 name: 'ServiceSwitcher',
50 slot: 'Tab',
51})(({ theme }) => ({
52 minWidth: 0,
53 transition: theme.transitions.create('background-color', {
54 duration: theme.transitions.duration.shortest,
55 }),
56 '&.Mui-selected': {
57 backgroundColor:
58 theme.palette.mode === 'dark'
59 ? alpha(theme.palette.text.primary, 0.16)
60 : alpha(theme.palette.primary.light, 0.3),
61 },
62}));
63
64export default observer(() => {
65 const { services } = useStore();
66 // TODO Move this to the `SharedStore`.
67 const [selectedService, setSelectedService] = useState<string | boolean>(
68 false,
69 );
70
71 return (
72 <ServiceSwitcherRoot
73 variant="scrollable"
74 orientation="vertical"
75 value={selectedService}
76 onChange={(_event, newValue: string) => setSelectedService(newValue)}
77 >
78 {services.map((service) => (
79 <ServiceSwitcherTab
80 key={service.id}
81 value={service.id}
82 icon={<ServiceIcon name={service.name} />}
83 aria-label={service.name}
84 />
85 ))}
86 </ServiceSwitcherRoot>
87 );
88});
diff --git a/packages/renderer/src/components/Sidebar.tsx b/packages/renderer/src/components/Sidebar.tsx
index 44a47b0..ed78fc6 100644
--- a/packages/renderer/src/components/Sidebar.tsx
+++ b/packages/renderer/src/components/Sidebar.tsx
@@ -19,23 +19,30 @@
19 */ 19 */
20 20
21import Box from '@mui/material/Box'; 21import Box from '@mui/material/Box';
22import { alpha } from '@mui/material/styles';
22import React from 'react'; 23import React from 'react';
23 24
25import ServiceSwitcher from './ServiceSwitcher';
24import ToggleDarkModeButton from './ToggleDarkModeButton'; 26import ToggleDarkModeButton from './ToggleDarkModeButton';
25 27
26export default function Sidebar(): JSX.Element { 28export default function Sidebar(): JSX.Element {
27 return ( 29 return (
28 <Box 30 <Box
31 component="aside"
29 sx={(theme) => ({ 32 sx={(theme) => ({
30 background: theme.palette.divider,
31 flex: 0, 33 flex: 0,
32 display: 'flex', 34 display: 'flex',
33 flexDirection: 'column', 35 flexDirection: 'column',
34 alignItems: 'center', 36 alignItems: 'center',
35 justifyContent: 'flex-end', 37 justifyContent: 'space-between',
36 padding: 1, 38 paddingBottom: 1,
39 backgroundClip: 'padding-box',
40 background: alpha(theme.palette.text.primary, 0.09),
41 borderInlineEnd: `1px solid ${theme.palette.divider}`,
42 minWidth: 67,
37 })} 43 })}
38 > 44 >
45 <ServiceSwitcher />
39 <ToggleDarkModeButton /> 46 <ToggleDarkModeButton />
40 </Box> 47 </Box>
41 ); 48 );
diff --git a/packages/renderer/src/stores/RendererStore.ts b/packages/renderer/src/stores/RendererStore.ts
index 4cc5163..f1915c9 100644
--- a/packages/renderer/src/stores/RendererStore.ts
+++ b/packages/renderer/src/stores/RendererStore.ts
@@ -20,6 +20,8 @@
20 20
21import { 21import {
22 BrowserViewBounds, 22 BrowserViewBounds,
23 Config,
24 Service,
23 sharedStore, 25 sharedStore,
24 SophieRenderer, 26 SophieRenderer,
25 ThemeSource, 27 ThemeSource,
@@ -37,6 +39,14 @@ export const rendererStore = types
37 .model('RendererStore', { 39 .model('RendererStore', {
38 shared: types.optional(sharedStore, {}), 40 shared: types.optional(sharedStore, {}),
39 }) 41 })
42 .views((self) => ({
43 get config(): Config {
44 return self.shared.config;
45 },
46 get services(): Service[] {
47 return this.config.services;
48 },
49 }))
40 .actions((self) => ({ 50 .actions((self) => ({
41 setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void { 51 setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void {
42 getEnv(self).dispatchMainAction({ 52 getEnv(self).dispatchMainAction({