Skip to content

Commit

Permalink
Adjust Extension Toggle (#417)
Browse files Browse the repository at this point in the history
  • Loading branch information
jolierabideau authored Sep 22, 2023
2 parents d17127b + 27f9a4f commit 545e95e
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.extension-card-wide {
display: flex;
width: 100%;
justify-content: space-between;
}

.extension-card-square {
display: flex;
flex-direction: column;
min-width: 250px;
max-width: 300px;
width: 100%;
}

.card-action-square {
justify-content: space-between;
}

.extension-card-description {
flex-grow: 1;
// Only set important here because MUI forces padding on each Card element
// Didn't need all the padding at the top to match UX mockup, intended to change later
padding-top: 0 !important;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Avatar, Card, CardActions, CardContent, CardHeader, Typography } from '@mui/material';
import { PropsWithChildren, ReactNode, useMemo } from 'react';
import './extension-card.component.scss';

export type ExtensionCardProps = PropsWithChildren<{
/**
* The unique extension name
*/
extensionName: string;

/**
* The description of the extension
*/
extensionDescription: string;

/**
* File path to the icon
*/
iconFilePath?: string;

/**
* Content to provide for a header action that appears in the top right of the Card
*/
headerAction?: ReactNode;

/**
* Optional className to link to css
*/
className?: string;
}>;

export default function ExtensionCard({
extensionName,
extensionDescription,
iconFilePath,
headerAction,
className,
children,
}: ExtensionCardProps) {
const avatar = useMemo(
() => (
<Avatar variant="square" src={iconFilePath ?? undefined} alt={extensionDescription}>
{!iconFilePath ? extensionName[0] : null}
</Avatar>
),
[extensionDescription, extensionName, iconFilePath],
);

const isGallery = useMemo(() => className && className === 'square', [className]);

return (
<Card
className={isGallery ? 'extension-card-square' : 'extension-card-wide'}
variant="outlined"
>
<CardHeader
className="extension-card-content"
avatar={avatar}
title={extensionName}
titleTypographyProps={{ variant: 'h6' }}
action={headerAction}
subheader={!isGallery ? extensionDescription : null}
subheaderTypographyProps={{ variant: 'body2' }}
/>
{isGallery ? (
<CardContent className="extension-card-description">
<Typography variant="body2">{extensionDescription}</Typography>
</CardContent>
) : null}
<CardActions className={isGallery ? 'card-action-square' : 'card-action-wide'}>
{children}
</CardActions>
</Card>
);
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,59 @@
import { Switch } from 'papi-components';
import { Typography } from '@mui/material';
import { Button } from 'papi-components';
import './extension-list.component.scss';
import ExtensionToggle from './extension-toggle.component';
import { PropsWithChildren, useMemo } from 'react';
import ExtensionToggle, { ExtensionToggleProps } from './extension-toggle.component';

export type Extension = {
/**
* The name of the extension
*/
name: string;
description: string;
};

export interface ExtensionToggleHandler {
(extensionName: string): void;
}

type ExtensionListProps = {
/**
* Extensions to display Cards for
* The description of the extension
*/
extensions: Extension[];
description: string;

/**
* The array of toggled extension names is passed to control the isChecked flag on Switch
* Set if there is an update available, controls update button
*/
toggledExtensionNames: string[];
hasUpdateAvailable: boolean;

/**
* Handler to perform an action when the extension is toggled
* Set if the extension is currently installed
*/
handleExtensionToggle: ExtensionToggleHandler;
isInstalled: boolean;

/**
* Optional label
* File path to the extensions icon
*/
label?: string;
iconFilePath?: string;
};

type ExtensionListProps = Omit<ExtensionToggleProps, 'extensionName' | 'extensionDescription'> &
PropsWithChildren<{
/**
* Extensions to display Cards for
*/
extensions: Extension[];

/**
* Optional label
*/
label?: string;

/**
* Optional flag to set the list as a gallery, square cards instead of wide cards
*/
isGallery?: boolean;

/**
* Optional flag to set if you want an icon (Avatar) to appear for all the extensions in the list that have filePath set
*/
hasIcons?: boolean;
}>;

/**
* Extension List component that creates a series of ExtensionToggle's for a provided array of extensions.
* @param ExtensionListProps
Expand All @@ -40,24 +62,42 @@ type ExtensionListProps = {
export default function ExtensionList({
extensions,
toggledExtensionNames,
headerAction,
handleExtensionToggle,
label,
hasIcons,
isGallery,
children,
}: ExtensionListProps) {
const extensionToggleClassName = useMemo(() => (isGallery ? 'square' : 'wide'), [isGallery]);

return (
<div className="extension-list">
<div>
<Typography fontWeight="fontWeightBold" className="extensions-label" variant="subtitle2">
{label}
</Typography>
{extensions.map((ext) => (
<ExtensionToggle extensionName={ext.name} extensionDescription={ext.description}>
<div className="switch-container">
<Switch
isChecked={toggledExtensionNames.includes(ext.name)}
onChange={() => handleExtensionToggle(ext.name)}
/>
</div>
</ExtensionToggle>
))}
<div className="extension-list">
{extensions.map((ext) => (
<ExtensionToggle
className={extensionToggleClassName}
key={ext.name}
iconFilePath={hasIcons ? ext.iconFilePath : undefined}
extensionName={ext.name}
extensionDescription={ext.description}
toggledExtensionNames={toggledExtensionNames}
handleExtensionToggle={handleExtensionToggle}
headerAction={headerAction}
>
{isGallery ? (
<div className="action-buttons">
<Button isDisabled={toggledExtensionNames.includes(ext.name)}>Remove</Button>
<Button isDisabled={!ext.hasUpdateAvailable}>Update</Button>
</div>
) : null}
{children}
</ExtensionToggle>
))}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
.installed-extensions-list {
.extension-manager-dialog {
overflow-y: auto;
display: flex;
flex-direction: column;
align-items: flex-end;
padding: 5%;
gap: 10px;
flex-direction: column;
}

.extension-manager-label {
padding-bottom: 5%;
}

.extension-manager-actions {
padding-top: 5%;
align-self: flex-end;
}

.extension-manager-instance {
display: flex;
flex-direction: column;
gap: 30px;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SavedTabInfo, TabInfo } from '@shared/data/web-view.model';
import { useMemo, useState } from 'react';
import logger from '@shared/services/logger.service';
import { Typography } from '@mui/material';
import { Button } from 'papi-components';
import ExtensionList, { Extension } from './extension-list.component';
import './extension-manager-tab.component.scss';
Expand All @@ -9,15 +10,54 @@ export const TAB_TYPE_EXTENSION_MANAGER = 'extension-manager-dialog';

export function fetchExtensions(): Extension[] {
return [
{ name: 'Editor', description: 'Edit Scripture Text' } as Extension,
{ name: 'Resource Viewer', description: 'View Scripture resources' } as Extension,
{
name: 'Editor',
description: 'Edit Scripture Text',
hasUpdateAvailable: false,
isInstalled: true,
iconFilePath: undefined,
} as Extension,
{
name: 'Resource Viewer',
description: 'View Scripture resources',
hasUpdateAvailable: false,
isInstalled: true,
iconFilePath: undefined,
} as Extension,
{
name: 'Parallel Passages',
description: 'Compare parallel passages of Scripture',
hasUpdateAvailable: true,
isInstalled: true,
iconFilePath: undefined,
} as Extension,
{
name: 'Psalms layer-by-layer',
description: 'Provide resources on the Psalms from Cambridge Digital Bible Research',
hasUpdateAvailable: true,
isInstalled: true,
iconFilePath: undefined,
} as Extension,
{
name: 'Hello World',
description: 'Example Bundled Extension',
hasUpdateAvailable: true,
isInstalled: false,
iconFilePath: undefined,
} as Extension,
{
name: 'Hello Someone',
description: 'Example Bundled Extension',
hasUpdateAvailable: true,
isInstalled: false,
iconFilePath: undefined,
} as Extension,
{
name: 'Quick Verse',
description: 'Example Bundled Extension',
hasUpdateAvailable: false,
isInstalled: false,
iconFilePath: 'papi-extension://quick-verse/assets/letter-q.png',
} as Extension,
];
}
Expand All @@ -39,20 +79,33 @@ export default function ExtensionManagerTab() {
};

return (
<span className="installed-extensions-list">
<ExtensionList
extensions={installedExtensions}
toggledExtensionNames={toggledExtensions}
handleExtensionToggle={handleExtensionToggle}
label="Installed Extensions"
/>
<Button
className="close-extension-toggle"
onClick={() => logger.info(`Installed extensions: ${toggledExtensions}`)}
>
Close
</Button>
</span>
<div className="extension-manager-dialog">
<div className="extension-manager-label">
<Typography>Extension Toggle</Typography>
</div>
<div className="extension-manager-instance">
<ExtensionList
extensions={installedExtensions}
toggledExtensionNames={toggledExtensions}
handleExtensionToggle={handleExtensionToggle}
label="Installed Extensions"
/>
<ExtensionList
extensions={installedExtensions}
toggledExtensionNames={toggledExtensions}
handleExtensionToggle={handleExtensionToggle}
label="Installed Extensions Gallery Example"
isGallery
hasIcons
headerAction={<Button className="primary">Add</Button>}
/>
</div>
<div className="extension-manager-actions">
<Button onClick={() => logger.info(`Installed extensions: ${toggledExtensions}`)}>
Close
</Button>
</div>
</div>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.extension-card {
.switch-container {
display: flex;
width: 100%;
justify-content: space-between;
justify-content: flex-end;
align-items: center;
}
Loading

0 comments on commit 545e95e

Please sign in to comment.