Skip to content

Commit

Permalink
Showing 10 changed files with 156 additions and 104 deletions.
24 changes: 14 additions & 10 deletions src/search/module-selector.tsx
Original file line number Diff line number Diff line change
@@ -6,24 +6,21 @@
import React, { FC, useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { Container, Row, Text, Icon, Dropdown } from '@zextras/carbonio-design-system';
import { AppRoute } from '../../types';
import { useAppStore } from '../store/app';
import { useSearchStore } from './search-store';
import { SEARCH_APP_ID } from '../constants';
import { pushHistory } from '../history/hooks';
import { useCurrentRoute, pushHistory } from '../history/hooks';

const SelectorContainer = styled(Container)<{ open: boolean }>`
border-right: 1px solid ${({ theme }): string => theme.palette.gray4.regular};
cursor: 'pointer';
cursor: pointer;
background: ${({ theme, open }): string => theme.palette[open ? 'gray5' : 'gray6'].regular};
&:hover {
background: ${({ theme, open }): string => theme.palette[open ? 'gray5' : 'gray6'].hover};
}
`;

export const ModuleSelector: FC<{ activeRoute: AppRoute; disabled: boolean }> = ({
activeRoute
}) => {
const ModuleSelectorComponent: FC<{ app: string | undefined }> = ({ app }) => {
const modules = useAppStore((s) => s.views.search);
const { module, updateModule } = useSearchStore();
const fullModule = useMemo(
@@ -49,13 +46,13 @@ export const ModuleSelector: FC<{ activeRoute: AppRoute; disabled: boolean }> =
);

useEffect(() => {
if (activeRoute?.app !== SEARCH_APP_ID) {
if (!fullModule || fullModule?.app !== activeRoute?.app) {
updateModule((modules.find((m) => m.app === activeRoute?.app) ?? modules[0])?.route);
if (app !== SEARCH_APP_ID) {
if (!fullModule || fullModule?.app !== app) {
updateModule((modules.find((m) => m.app === app) ?? modules[0])?.route);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeRoute, modules, updateModule]);
}, [app, modules, updateModule]);

if (!fullModule) {
return null;
@@ -90,3 +87,10 @@ export const ModuleSelector: FC<{ activeRoute: AppRoute; disabled: boolean }> =
</Dropdown>
);
};

const MemoModuleSelector = React.memo(ModuleSelectorComponent);

export const ModuleSelector = (): JSX.Element => {
const activeRoute = useCurrentRoute();
return <MemoModuleSelector app={activeRoute?.app} />;
};
12 changes: 4 additions & 8 deletions src/search/search-bar.tsx
Original file line number Diff line number Diff line change
@@ -22,14 +22,14 @@ import { useLocalStorage } from '../shell/hooks';
import { SEARCH_APP_ID } from '../constants';

import { useSearchStore } from './search-store';
import { QueryChip, SearchBarProps } from '../../types';
import { QueryChip } from '../../types';
import { ModuleSelector } from './module-selector';

const OutlinedIconButton = styled(IconButton)`
border: 1px solid
${({ theme, disabled }): string =>
disabled ? theme.palette.primary.disabled : theme.palette.primary.regular};
display: 'block';
display: block;
& svg {
border: none;
}
@@ -59,11 +59,7 @@ type SearchLocalStorage = Array<{
app: string;
id: string;
}>;
export const SearchBar: FC<SearchBarProps> = ({
activeRoute
// primaryAction,
// secondaryActions
}) => {
export const SearchBar: FC = () => {
const [searchIsEnabled, setSearchIsEnabled] = useState(false);
const inputRef = useRef<HTMLInputElement>();
const [t] = useTranslation();
@@ -375,7 +371,7 @@ export const SearchBar: FC<SearchBarProps> = ({
<Container minWidth="512px" width="fill">
<Container orientation="horizontal" width="fill">
<Container width="fit">
<ModuleSelector activeRoute={activeRoute} disabled={searchDisabled} />
<ModuleSelector />
</Container>
<StyledContainer orientation="horizontal">
<StyledChipInput
66 changes: 38 additions & 28 deletions src/shell/creation-button.tsx
Original file line number Diff line number Diff line change
@@ -9,39 +9,18 @@ import { reduce, groupBy } from 'lodash';
import { MultiButton, Button, Dropdown } from '@zextras/carbonio-design-system';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
import { Location } from 'history';
import { useActions } from '../store/integrations/hooks';
import { ACTION_TYPES } from '../constants';
import { Action, AppRoute } from '../../types';
import { useAppList } from '../store/app';
import { useCurrentRoute } from '../history/hooks';

const useSecondaryActions = (
actions: Array<Action>,
activeRoute?: AppRoute
): Array<Action | { type: string; id: string }> => {
const apps = useAppList();

const byApp = useMemo(() => groupBy(actions, 'group'), [actions]);
return useMemo(
() => [
...(byApp[activeRoute?.app ?? ''] ?? []),
...reduce(
apps,
(acc, app, i) => {
if (app.name !== activeRoute?.app && byApp[app.name]?.length > 0) {
acc.push({ type: 'divider', label: '', id: `divider-${i}` }, ...byApp[app.name]);
}
return acc;
},
[] as Array<Action | { type: string; id: string }>
)
],
[activeRoute?.app, apps, byApp]
);
};

export const CreationButton: FC<{ activeRoute?: AppRoute }> = ({ activeRoute }) => {
export const CreationButtonComponent: FC<{ activeRoute: AppRoute; location: Location }> = ({
activeRoute,
location
}) => {
const [t] = useTranslation();
const location = useLocation();
const actions = useActions({ activeRoute, location }, ACTION_TYPES.NEW);
const [open, setOpen] = useState(false);
const primaryAction = useMemo(
@@ -51,7 +30,22 @@ export const CreationButton: FC<{ activeRoute?: AppRoute }> = ({ activeRoute })
),
[actions, activeRoute?.app, activeRoute?.id]
);
const secondaryActions = useSecondaryActions(actions, activeRoute);
const apps = useAppList();
const byApp = useMemo(() => groupBy(actions, 'group'), [actions]);

const secondaryActions = [
...(byApp[activeRoute?.app ?? ''] ?? []),
...reduce(
apps,
(acc, app, i) => {
if (app.name !== activeRoute?.app && byApp[app.name]?.length > 0) {
acc.push({ type: 'divider', label: '', id: `divider-${i}` }, ...byApp[app.name]);
}
return acc;
},
[] as Array<Action | { type: string; id: string }>
)
];

const onClose = useCallback(() => {
setOpen(false);
@@ -80,3 +74,19 @@ export const CreationButton: FC<{ activeRoute?: AppRoute }> = ({ activeRoute })
</Dropdown>
);
};

const MemoCreationButton = React.memo(CreationButtonComponent);

export const CreationButton: FC = () => {
const locationFull = useLocation() as Location;
const activeRoute = useCurrentRoute() as AppRoute;

const truncateLocation = (location: Location): Location => ({
...location,
pathname: location?.pathname?.split('/').slice(0, 2).join('/'),
key: ''
});

const location = useMemo(() => truncateLocation(locationFull), [locationFull]);
return <MemoCreationButton activeRoute={activeRoute} location={location} />;
};
16 changes: 3 additions & 13 deletions src/shell/shell-header.tsx
Original file line number Diff line number Diff line change
@@ -19,13 +19,11 @@ import Logo from '../svg/carbonio.svg';
import { SearchBar } from '../search/search-bar';
import { CreationButton } from './creation-button';
import { useAppStore } from '../store/app';
import { AppRoute } from '../../types';

const ShellHeader: FC<{
activeRoute: AppRoute;
mobileNavIsOpen: boolean;
onMobileMenuClick: () => void;
}> = ({ activeRoute, mobileNavIsOpen, onMobileMenuClick, children }) => {
}> = ({ mobileNavIsOpen, onMobileMenuClick, children }) => {
const screenMode = useScreenMode();
const searchEnabled = useAppStore((s) => s.views.search.length > 0);
return (
@@ -58,17 +56,9 @@ const ShellHeader: FC<{
<Logo height="32px" />
</Container>
<Padding horizontal="large">
<CreationButton activeRoute={activeRoute} />
<CreationButton />
</Padding>
<Responsive mode="desktop">
{searchEnabled && (
<SearchBar
activeRoute={activeRoute}
// primaryAction={primaryAction}
// secondaryActions={secondaryActions}
/>
)}
</Responsive>
<Responsive mode="desktop">{searchEnabled && <SearchBar />}</Responsive>
</Container>
<Container orientation="horizontal" width="25%" mainAlignment="flex-end">
<Responsive mode="desktop">{children}</Responsive>
50 changes: 44 additions & 6 deletions src/shell/shell-mobile-nav.jsx → src/shell/shell-mobile-nav.tsx
Original file line number Diff line number Diff line change
@@ -4,26 +4,51 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import React from 'react';
import React, { ComponentType, FC, FunctionComponent } from 'react';
import { reduce, find } from 'lodash';
import { Accordion, Collapse, Container, Padding } from '@zextras/carbonio-design-system';
import { useHistory } from 'react-router-dom';
import { AppRoute, SecondaryBarComponentProps } from '../../types';
import { useAppStore } from '../store/app';
import AppContextProvider from '../boot/app/app-context-provider';
import { getCurrentRoute } from '../history/hooks';

const SidebarComponent = ({ item }) =>
type SidebarComponentProps = {
item: ShellMobileNavComponent;
};

const SidebarComponent: FC<SidebarComponentProps> = ({ item }) =>
item.secondary ? (
<AppContextProvider pkg={item.id}>
<item.secondary />
<item.secondary expanded={!!item.expanded} />
</AppContextProvider>
) : null;

export default function ShellMobileNav({ mobileNavIsOpen, menuTree }) {
type ShellMobileNavComponentProps = {
mobileNavIsOpen: boolean;
menuTree: AppRoute;
};

type ShellMobileNavComponent = {
id: string;
label: string;
icon: string;
secondary?: ComponentType<SecondaryBarComponentProps>;
onClick?: () => void;
CustomComponent?: FunctionComponent<SidebarComponentProps> | null;
items?: Array<ShellMobileNavComponent> | [];
expanded?: boolean;
};

const ShellMobileNavComponent: FC<ShellMobileNavComponentProps> = ({
mobileNavIsOpen,
menuTree
}) => {
const history = useHistory();
const views = useAppStore((s) =>
reduce(
s.routes,
(acc, val) => {
(acc: Array<ShellMobileNavComponent>, val) => {
const primary = find(s.views.primaryBar, (item) => item.id === val.id);
const secondary = find(s.views.secondaryBar, (item) => item.id === val.id);
if (primary && primary.visible) {
@@ -85,4 +110,17 @@ export default function ShellMobileNav({ mobileNavIsOpen, menuTree }) {
</Collapse>
</Container>
);
}
};

const ShellMobileNavMemo = React.memo(ShellMobileNavComponent);

type ShellMobileNavProps = {
mobileNavIsOpen: boolean;
};

const ShellMobileNav: FC<ShellMobileNavProps> = ({ mobileNavIsOpen }) => {
const menuTree = getCurrentRoute() as AppRoute;
return <ShellMobileNavMemo menuTree={menuTree} mobileNavIsOpen={mobileNavIsOpen} />;
};

export default ShellMobileNav;
10 changes: 4 additions & 6 deletions src/shell/shell-navigation-bar.tsx
Original file line number Diff line number Diff line change
@@ -11,14 +11,12 @@ import ShellSecondaryBar from './shell-secondary-bar';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import ShellMobileNav from './shell-mobile-nav';
import { AppRoute } from '../../types';

type ShellNavigationBarProps = {
mobileNavIsOpen: boolean;
activeRoute: AppRoute;
};

const ShellNavigationBar: FC<ShellNavigationBarProps> = ({ mobileNavIsOpen, activeRoute }) => (
const ShellNavigationBar: FC<ShellNavigationBarProps> = ({ mobileNavIsOpen }) => (
<Container
orientation="horizontal"
background="gray5"
@@ -28,11 +26,11 @@ const ShellNavigationBar: FC<ShellNavigationBarProps> = ({ mobileNavIsOpen, acti
crossAlignment="flex-start"
>
<Responsive mode="desktop">
<ShellPrimaryBar activeRoute={activeRoute} />
<ShellSecondaryBar activeRoute={activeRoute} />
<ShellPrimaryBar />
<ShellSecondaryBar />
</Responsive>
<Responsive mode="mobile">
<ShellMobileNav mobileNavIsOpen={mobileNavIsOpen} menuTree={activeRoute} />
<ShellMobileNav mobileNavIsOpen={mobileNavIsOpen} />
</Responsive>
</Container>
);
10 changes: 9 additions & 1 deletion src/shell/shell-primary-bar.tsx
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import BadgeWrap from './badge-wrap';
import AppContextProvider from '../boot/app/app-context-provider';
import { checkRoute } from '../utility-bar/utils';
import { IS_STANDALONE } from '../constants';
import { useCurrentRoute } from '../history/hooks';

const ContainerWithDivider = styled(Container)`
border-right: 1px solid ${({ theme }): string => theme.palette.gray3.regular};
@@ -86,7 +87,7 @@ const PrimaryBarAccessoryElement: FC<PrimaryBarAccessoryItemProps> = ({ view })
</Tooltip>
);

const ShellPrimaryBar: FC<{ activeRoute: AppRoute }> = ({ activeRoute }) => {
const ShellPrimaryBarComponent: FC<{ activeRoute: AppRoute }> = ({ activeRoute }) => {
const primaryBarViews = useAppStore((s) => s.views.primaryBar);
const [routes, setRoutes] = useState<Record<string, string>>({});
const history = useHistory();
@@ -163,4 +164,11 @@ const ShellPrimaryBar: FC<{ activeRoute: AppRoute }> = ({ activeRoute }) => {
);
};

const MemoShellPrimaryBarComponent = React.memo(ShellPrimaryBarComponent);

const ShellPrimaryBar: FC = () => {
const activeRoute = useCurrentRoute() as AppRoute;
return <MemoShellPrimaryBarComponent activeRoute={activeRoute} />;
};

export default ShellPrimaryBar;
14 changes: 9 additions & 5 deletions src/shell/shell-secondary-bar.tsx
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import { useAppStore } from '../store/app';
import { AppRoute } from '../../types';
import { useUtilityBarStore } from '../utility-bar';
import { checkRoute } from '../utility-bar/utils';
import { useCurrentRoute } from '../history/hooks';

const SidebarContainer = styled(Container)`
min-width: 48px;
@@ -24,11 +25,7 @@ const SidebarContainer = styled(Container)`
overflow-x: hidden;
`;

type SecondaryBarProps = {
activeRoute: AppRoute;
};

const ShellSecondaryBar: FC<SecondaryBarProps> = ({ activeRoute }) => {
const ShellSecondaryBarComponent: FC<{ activeRoute: AppRoute }> = ({ activeRoute }) => {
const isOpen = useUtilityBarStore((s) => s.secondaryBarState);
const setIsOpen = useUtilityBarStore((s) => s.setSecondaryBarState);
const onCollapserClick = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
@@ -84,4 +81,11 @@ const ShellSecondaryBar: FC<SecondaryBarProps> = ({ activeRoute }) => {
);
};

const MemoShellSecondaryBar = React.memo(ShellSecondaryBarComponent);

const ShellSecondaryBar: FC = () => {
const activeRoute = useCurrentRoute() as AppRoute;
return <MemoShellSecondaryBar activeRoute={activeRoute} />;
};

export default ShellSecondaryBar;
51 changes: 31 additions & 20 deletions src/shell/shell-view.tsx
Original file line number Diff line number Diff line change
@@ -55,34 +55,35 @@ function DarkReaderListener(): null {
return null;
}

const useLoginRedirection = (activeRoute?: AppRoute): void => {
const useLoginRedirection = (allowUnauthenticated?: string): void => {
const auth = useAccountStore((s) => s.authenticated);
useEffect(() => {
if (IS_STANDALONE && !auth && activeRoute && !activeRoute.standalone?.allowUnauthenticated) {
if (IS_STANDALONE && !auth && !allowUnauthenticated) {
goToLogin();
}
}, [activeRoute, auth]);
}, [allowUnauthenticated, auth]);
};

export const Shell: FC = () => {
const ShellComponent: FC<{ allowUnauthenticated?: string; hideShellHeader?: string }> = ({
allowUnauthenticated,
hideShellHeader
}) => {
const [mobileNavOpen, setMobileNavOpen] = useState(false);
const activeRoute = useCurrentRoute() as AppRoute;
useLoginRedirection(activeRoute);
useLoginRedirection(allowUnauthenticated);
return (
<Background>
<DarkReaderListener />
{/* <MainAppRerouter /> */}
{!(IS_STANDALONE && activeRoute?.standalone?.hideShellHeader) && (
{!(IS_STANDALONE && hideShellHeader) && (
<ShellHeader
activeRoute={activeRoute}
mobileNavIsOpen={mobileNavOpen}
onMobileMenuClick={(): void => setMobileNavOpen(!mobileNavOpen)}
>
<ShellUtilityBar />
</ShellHeader>
)}
<Row crossAlignment="unset" style={{ position: 'relative', flexGrow: '1' }}>
<ShellNavigationBar activeRoute={activeRoute} mobileNavIsOpen={mobileNavOpen} />
<ShellNavigationBar mobileNavIsOpen={mobileNavOpen} />
<AppViewContainer />
<ShellUtilityPanel />
</Row>
@@ -93,16 +94,26 @@ export const Shell: FC = () => {
);
};

const ShellView: FC = () => (
<ShellContextProvider>
<ModalManager>
<SnackbarManager>
<PreviewManager>
<Shell />
</PreviewManager>
</SnackbarManager>
</ModalManager>
</ShellContextProvider>
);
const MemoShell = React.memo(ShellComponent);

const ShellView: FC = () => {
const activeRoute = useCurrentRoute() as AppRoute;
const allowUnauthenticated = activeRoute?.standalone?.allowUnauthenticated as string | undefined;
const hideShellHeader = activeRoute?.standalone?.hideShellHeader as string | undefined;
return (
<ShellContextProvider>
<ModalManager>
<SnackbarManager>
<PreviewManager>
<MemoShell
allowUnauthenticated={allowUnauthenticated}
hideShellHeader={hideShellHeader}
/>
</PreviewManager>
</SnackbarManager>
</ModalManager>
</ShellContextProvider>
);
};

export default ShellView;
7 changes: 0 additions & 7 deletions types/search/index.d.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { AppRoute } from '../apps';
import { QueryChip } from './items';

export * from './items';
@@ -17,12 +16,6 @@ export type SearchState = {
updateModule: (module: string) => void;
};

export type SearchBarProps = {
activeRoute: AppRoute;
primaryAction?: unknown;
secondaryActions?: unknown;
};

// export type SelectLabelFactoryProps = {
// selected: [{ label: string; value: string }];
// open: boolean;

0 comments on commit 5531f4c

Please sign in to comment.