From 2d26e9f2ea26f9cc89e9aac8c09e132a5311f6a5 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Tue, 4 Jan 2022 16:29:39 +0100 Subject: feat: Service switcher buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, they do nothing. Signed-off-by: Kristóf Marussy --- packages/renderer/src/components/ServiceIcon.tsx | 53 +++++++++++++ .../renderer/src/components/ServiceSwitcher.tsx | 88 ++++++++++++++++++++++ packages/renderer/src/components/Sidebar.tsx | 13 +++- packages/renderer/src/stores/RendererStore.ts | 10 +++ 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 packages/renderer/src/components/ServiceIcon.tsx create mode 100644 packages/renderer/src/components/ServiceSwitcher.tsx 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 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { styled } from '@mui/material/styles'; +import React from 'react'; + +const ServiceIconRoot = styled('div', { + name: 'ServiceIcon', + slot: 'Root', +})(({ theme }) => ({ + width: 34, + height: 34, + borderRadius: theme.shape.borderRadius, + background: 'currentColor', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', +})); + +const ServiceIconText = styled('div', { + name: 'ServiceIcon', + slot: 'Text', +})(({ theme }) => ({ + display: 'inline-block', + flex: 0, + fontSize: theme.typography.pxToRem(24), + color: theme.palette.primary.contrastText, +})); + +export default function ServiceIcon({ name }: { name: string }): JSX.Element { + return ( + + {name.length > 0 ? name[0] : '?'} + + ); +} 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 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import Tab from '@mui/material/Tab'; +import Tabs from '@mui/material/Tabs'; +import { alpha, styled } from '@mui/material/styles'; +import { observer } from 'mobx-react-lite'; +import React, { useState } from 'react'; + +import ServiceIcon from './ServiceIcon'; +import { useStore } from './StoreProvider'; + +const ServiceSwitcherRoot = styled(Tabs, { + name: 'ServiceSwitcher', + slot: 'Root', +})(({ theme }) => ({ + '.MuiTabs-indicator': { + width: 3, + ...(theme.direction === 'ltr' + ? { + left: 0, + right: 'auto', + } + : { + left: 'auto', + right: 0, + }), + }, +})); + +const ServiceSwitcherTab = styled(Tab, { + name: 'ServiceSwitcher', + slot: 'Tab', +})(({ theme }) => ({ + minWidth: 0, + transition: theme.transitions.create('background-color', { + duration: theme.transitions.duration.shortest, + }), + '&.Mui-selected': { + backgroundColor: + theme.palette.mode === 'dark' + ? alpha(theme.palette.text.primary, 0.16) + : alpha(theme.palette.primary.light, 0.3), + }, +})); + +export default observer(() => { + const { services } = useStore(); + // TODO Move this to the `SharedStore`. + const [selectedService, setSelectedService] = useState( + false, + ); + + return ( + setSelectedService(newValue)} + > + {services.map((service) => ( + } + aria-label={service.name} + /> + ))} + + ); +}); 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 @@ */ import Box from '@mui/material/Box'; +import { alpha } from '@mui/material/styles'; import React from 'react'; +import ServiceSwitcher from './ServiceSwitcher'; import ToggleDarkModeButton from './ToggleDarkModeButton'; export default function Sidebar(): JSX.Element { return ( ({ - background: theme.palette.divider, flex: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', - justifyContent: 'flex-end', - padding: 1, + justifyContent: 'space-between', + paddingBottom: 1, + backgroundClip: 'padding-box', + background: alpha(theme.palette.text.primary, 0.09), + borderInlineEnd: `1px solid ${theme.palette.divider}`, + minWidth: 67, })} > + ); 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 @@ import { BrowserViewBounds, + Config, + Service, sharedStore, SophieRenderer, ThemeSource, @@ -37,6 +39,14 @@ export const rendererStore = types .model('RendererStore', { shared: types.optional(sharedStore, {}), }) + .views((self) => ({ + get config(): Config { + return self.shared.config; + }, + get services(): Service[] { + return this.config.services; + }, + })) .actions((self) => ({ setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void { getEnv(self).dispatchMainAction({ -- cgit v1.2.3-54-g00ecf