Skip to content

Commit

Permalink
[179] Add support for local actions in the palette
Browse files Browse the repository at this point in the history
Bug: #179
Signed-off-by: Stéphane Bégaudeau <[email protected]>
  • Loading branch information
sbegaudeau committed Jun 27, 2023
1 parent 248442f commit 0882864
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 35 deletions.
47 changes: 47 additions & 0 deletions frontend/svalyn-studio-app/src/palette/DefaultPaletteActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2023 Stéphane Bégaudeau.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import CorporateFareIcon from '@mui/icons-material/CorporateFare';
import HomeIcon from '@mui/icons-material/Home';
import HubIcon from '@mui/icons-material/Hub';
import { PaletteNavigationAction } from './Palette.types';

export const goToHome: PaletteNavigationAction = {
type: 'navigation-action',
id: 'go-to-home',
icon: <HomeIcon fontSize="small" />,
label: 'Home',
to: '/',
};

export const goToDomains: PaletteNavigationAction = {
type: 'navigation-action',
id: 'go-to-domains',
icon: <HubIcon fontSize="small" />,
label: 'Domains',
to: '/domains',
};

export const goToNewOrganization: PaletteNavigationAction = {
type: 'navigation-action',
id: 'go-to-new-organization',
icon: <CorporateFareIcon fontSize="small" />,
label: 'New organization',
to: '/new/organization',
};
35 changes: 7 additions & 28 deletions frontend/svalyn-studio-app/src/palette/Palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import CorporateFareIcon from '@mui/icons-material/CorporateFare';
import HomeIcon from '@mui/icons-material/Home';
import HubIcon from '@mui/icons-material/Hub';
import SearchIcon from '@mui/icons-material/Search';
import Box from '@mui/material/Box';
import Dialog from '@mui/material/Dialog';
Expand All @@ -36,36 +33,14 @@ import { useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { PaletteAction, PaletteProps, PaletteState } from './Palette.types';

export const Palette = ({ open, onClose }: PaletteProps) => {
export const Palette = ({ actions, open, onClose }: PaletteProps) => {
const inputRef = useRef<HTMLInputElement>(null);
const listRef = useRef<HTMLUListElement>(null);

const navigate = useNavigate();

const goToHome: PaletteAction = {
id: 'go-to-home',
icon: <HomeIcon fontSize="small" />,
label: 'Home',
handle: () => navigate(`/`),
};
const goToDomains: PaletteAction = {
id: 'go-to-domains',
icon: <HubIcon fontSize="small" />,
label: 'Domains',
handle: () => navigate(`/domains`),
};
const goToNewOrganization: PaletteAction = {
id: 'go-to-new-organization',
icon: <CorporateFareIcon fontSize="small" />,
label: 'New organization',
handle: () => navigate(`/new/organization`),
};

const defaultPaletteActions: PaletteAction[] = [goToHome, goToDomains, goToNewOrganization];

const [state, setState] = useState<PaletteState>({
query: '',
actions: defaultPaletteActions,
actions,
selectedActionId: null,
});

Expand Down Expand Up @@ -111,7 +86,11 @@ export const Palette = ({ open, onClose }: PaletteProps) => {
};

const handleOnActionClick = (action: PaletteAction) => {
action.handle();
if (action.type === 'simple-action') {
action.handle();
} else if (action.type === 'navigation-action') {
navigate(action.to);
}
onClose();
};

Expand Down
14 changes: 13 additions & 1 deletion frontend/svalyn-studio-app/src/palette/Palette.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

export interface PaletteProps {
actions: PaletteAction[];
open: boolean;
onClose: () => void;
}
Expand All @@ -28,9 +29,20 @@ export interface PaletteState {
selectedActionId: string | null;
}

export interface PaletteAction {
export interface PaletteNavigationAction {
type: 'navigation-action';
id: string;
icon: JSX.Element;
label: string;
to: string;
}

export interface PaletteSimpleAction {
type: 'simple-action';
id: string;
icon: JSX.Element;
label: string;
handle: () => void;
}

export type PaletteAction = PaletteNavigationAction | PaletteSimpleAction;
1 change: 1 addition & 0 deletions frontend/svalyn-studio-app/src/palette/PaletteContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ import { PaletteContextValue } from './PaletteContext.types';

export const PaletteContext = React.createContext<PaletteContextValue>({
openPalette: () => {},
setActions: () => {},
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { PaletteAction } from './Palette.types';

export interface PaletteContextValue {
openPalette: () => void;
setActions: (actions: PaletteAction[]) => void;
}
9 changes: 7 additions & 2 deletions frontend/svalyn-studio-app/src/palette/PaletteProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
*/

import { useEffect, useState } from 'react';
import { goToDomains, goToHome, goToNewOrganization } from './DefaultPaletteActions';
import { Palette } from './Palette';
import { PaletteAction } from './Palette.types';
import { PaletteContext } from './PaletteContext';
import { PaletteContextValue } from './PaletteContext.types';
import { PaletteProviderProps, PaletteProviderState } from './PaletteProvider.types';

export const PaletteProvider = ({ children }: PaletteProviderProps) => {
const [state, setState] = useState<PaletteProviderState>({ open: false });
const actions: PaletteAction[] = [goToHome, goToDomains, goToNewOrganization];

const [state, setState] = useState<PaletteProviderState>({ actions, open: false });

useEffect(() => {
const keyDownEventListener = (event: KeyboardEvent) => {
Expand All @@ -43,12 +47,13 @@ export const PaletteProvider = ({ children }: PaletteProviderProps) => {

const paletteContextValue: PaletteContextValue = {
openPalette: () => setState((prevState) => ({ ...prevState, open: true })),
setActions: (actions: PaletteAction[]) => setState((prevState) => ({ ...prevState, actions })),
};

return (
<PaletteContext.Provider value={paletteContextValue}>
{children}
{state.open ? <Palette open={state.open} onClose={handleClose} /> : null}
{state.open ? <Palette actions={state.actions} open={state.open} onClose={handleClose} /> : null}
</PaletteContext.Provider>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { PaletteAction } from './Palette.types';

export interface PaletteProviderProps {
children?: React.ReactNode;
}

export interface PaletteProviderState {
actions: PaletteAction[];
open: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@ import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { generatePath, matchPath, useLocation, useNavigate } from 'react-router-dom';
import { OrganizationDashboard } from './dashboard/OrganizationDashboard';
import { OrganizationMembers } from './members/OrganizationMembers';
import { goToDomains, goToHome, goToNewOrganization } from '../../palette/DefaultPaletteActions';
import { PaletteSimpleAction } from '../../palette/Palette.types';
import { PaletteContext } from '../../palette/PaletteContext';
import { PaletteContextValue } from '../../palette/PaletteContext.types';
import { NewProjectDialog } from './NewProjectDialog';
import { OrganizationViewTabPanelProps, OrganizationViewTabPanelState } from './OrganizationViewTabPanel.types';
import { OrganizationDashboard } from './dashboard/OrganizationDashboard';
import { OrganizationMembers } from './members/OrganizationMembers';
import { OrganizationSettings } from './settings/OrganizationSettings';
import { OrganizationTags } from './tags/OrganizationTags';

Expand Down Expand Up @@ -104,6 +108,20 @@ export const OrganizationViewTabPanel = ({ organization }: OrganizationViewTabPa
setState((prevState) => ({ ...prevState, newProjectDialogOpen: true }));
const closeNewProjectDialog = () => setState((prevState) => ({ ...prevState, newProjectDialogOpen: false }));

const { setActions }: PaletteContextValue = useContext<PaletteContextValue>(PaletteContext);
useEffect(() => {
const goToNewProject: PaletteSimpleAction = {
type: 'simple-action',
id: 'create-project',
icon: <ClassIcon fontSize="small" />,
label: 'New project',
handle: () => setState((prevState) => ({ ...prevState, newProjectDialogOpen: true })),
};
setActions([goToHome, goToDomains, goToNewProject]);

return () => setActions([goToHome, goToDomains, goToNewOrganization]);
}, []);

return (
<>
<div>
Expand Down
25 changes: 24 additions & 1 deletion frontend/svalyn-studio-app/src/views/project/ProjectView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@
*/

import { gql, useQuery } from '@apollo/client';
import CorporateFareIcon from '@mui/icons-material/CorporateFare';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import { useEffect, useState } from 'react';
import { useContext, useEffect, useState } from 'react';
import { Link as RouterLink, matchPath, useLocation, useParams } from 'react-router-dom';
import { Navbar } from '../../navbars/Navbar';
import { goToDomains, goToHome, goToNewOrganization } from '../../palette/DefaultPaletteActions';
import { PaletteNavigationAction } from '../../palette/Palette.types';
import { PaletteContext } from '../../palette/PaletteContext';
import { PaletteContextValue } from '../../palette/PaletteContext.types';
import { ErrorSnackbar } from '../../snackbar/ErrorSnackbar';
import { NotFoundView } from '../notfound/NotFoundView';
import { ProjectDrawer } from './ProjectDrawer';
Expand All @@ -42,6 +47,8 @@ const getProjectQuery = gql`
name
description
organization {
identifier
name
role
}
}
Expand Down Expand Up @@ -80,6 +87,9 @@ export const ProjectView = () => {
const { projectIdentifier } = useParams();
const variables: GetProjectVariables = { identifier: projectIdentifier ?? '' };
const { loading, data, error } = useQuery<GetProjectData, GetProjectVariables>(getProjectQuery, { variables });

const { setActions }: PaletteContextValue = useContext<PaletteContextValue>(PaletteContext);

useEffect(() => {
if (!loading) {
if (data) {
Expand All @@ -88,14 +98,27 @@ export const ProjectView = () => {
} = data;
if (project) {
setState((prevState) => ({ ...prevState, project }));

const goToOrganization: PaletteNavigationAction = {
type: 'navigation-action',
id: 'go-to-organization',
icon: <CorporateFareIcon fontSize="small" />,
label: project.organization.name,
to: `/orgs/${project.organization.identifier}`,
};
setActions([goToHome, goToDomains, goToOrganization]);
}
}
if (error) {
setState((prevState) => ({ ...prevState, message: error.message }));
}
}

return () => setActions([goToHome, goToDomains, goToNewOrganization]);
}, [loading, data, error]);

useEffect(() => {}, []);

const handleCloseSnackbar = () => setState((prevState) => ({ ...prevState, message: null }));

if (!loading && state.project === null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export interface Project {
}

export interface Organization {
identifier: string;
name: string;
role: MembershipRole;
}

Expand Down

0 comments on commit 0882864

Please sign in to comment.