Skip to content

Commit

Permalink
Fixed the way dropdowns look in Chrome
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentlaucsb committed Jan 18, 2020
1 parent e04b0ba commit 27596a4
Show file tree
Hide file tree
Showing 14 changed files with 304 additions and 266 deletions.
6 changes: 3 additions & 3 deletions src/components/controls/HtmlIdAdder.tsx
@@ -1,7 +1,7 @@
import * as React from "react";
import Popover from 'react-tiny-popover';
import { Button } from "./Buttons";
import IconicMenuItem from "./menus/MenuItem";
import ToolbarButton from "./toolbar/ToolbarButton";

interface htmlIdAdderProps {
htmlId?: string;
Expand Down Expand Up @@ -83,9 +83,9 @@ export default function HtmlIdAdder(props: htmlIdAdderProps) {
isOpen={isOpen}
position="bottom"
content={expanded}>
<IconicMenuItem
<ToolbarButton
action={() => setOpen(!isOpen)}
icon="ui-tag"
onClick={() => setOpen(!isOpen)}
text="Add ID/Classes"
/>
</Popover>
Expand Down
3 changes: 2 additions & 1 deletion src/components/controls/TopEditingBar.tsx
Expand Up @@ -6,11 +6,12 @@ import Grid from "../Grid";
import Row from "../Row";
import Section from "../Section";
import { Action, IdType, NodeProperty, ResumeNode, AddChild } from "../utility/Types";
import Toolbar, { ToolbarSection, ToolbarItemData } from "./toolbar/ToolbarMaker";
import Toolbar, { ToolbarSection } from "./toolbar/ToolbarMaker";
import Column from "../Column";
import toolbarOptions from "../schema/ToolbarOptions";
import HtmlIdAdder from "./HtmlIdAdder";
import ResumeHotKeys from "./ResumeHotkeys";
import { ToolbarItemData } from "./toolbar/ToolbarButton";

interface AddOptionProps {
options: string | Array<string>;
Expand Down
23 changes: 14 additions & 9 deletions src/components/controls/TopNavBar.tsx
@@ -1,12 +1,12 @@
import React from "react";
import FileLoader from "./FileLoader";
import FileSaver from "./FileSaver";
import PureMenu, { PureMenuItem, PureMenuLink, PureDropdown } from "./menus/PureMenu";
import IconicMenuItem from "./menus/MenuItem";
import PureMenu, { PureMenuItem, PureDropdown } from "./menus/PureMenu";
import Modal from "./Modal";
import GitHubLight from "../../icons/GitHub-Mark-Light-120px-plus.png";
import { Action, EditorMode } from "../utility/Types";
import { Button } from "./Buttons";
import ToolbarButton, { ToolbarButtonProps } from "./toolbar/ToolbarButton";

export interface TopNavBarProps {
isEditing: boolean;
Expand All @@ -29,6 +29,11 @@ export interface TopNavBarProps {
export default function TopNavBar(props: TopNavBarProps) {
let [isOpen, setOpen] = React.useState(false);
const Item = PureMenuItem;
const IconicItem = (props: ToolbarButtonProps) => (
<PureMenuItem>
<ToolbarButton {...props} dropdownChild={true} />
</PureMenuItem>
);

let [modalContent, setModal] = React.useState(<></>);
let [title, setTitle] = React.useState("");
Expand All @@ -53,13 +58,13 @@ export default function TopNavBar(props: TopNavBarProps) {
<div id="brand">
<h1 onClick={props.toggleLanding}>Experiencer</h1>
<PureMenu id="top-menu" horizontal>
<PureDropdown trigger={<Button>File</Button>}>
<IconicMenuItem icon="paper" onClick={() => props.new()} text="New" />
<IconicMenuItem icon="folder-open" onClick={openLoader} text="Load" />
<IconicMenuItem disabled={!props.isEditing} onClick={props.saveLocal} text="Save" />
<IconicMenuItem disabled={!props.isEditing} icon="save" onClick={openSaver} text="Save As" />
<IconicMenuItem disabled={!props.isEditing} icon="file-html5" onClick={props.exportHtml} text="Export to HTML/CSS" />
<IconicMenuItem disabled={!props.isEditing} icon="printer" onClick={props.print} text="Print" />
<PureDropdown className="toolbar-dropdown" trigger={<Button>File</Button>}>
<IconicItem icon="paper" action={() => props.new()} text="New" />
<IconicItem icon="folder-open" action={openLoader} text="Load" />
<IconicItem disabled={!props.isEditing} action={props.saveLocal} text="Save" />
<IconicItem disabled={!props.isEditing} icon="save" action={openSaver} text="Save As" />
<IconicItem disabled={!props.isEditing} icon="file-html5" action={props.exportHtml} text="Export to HTML/CSS" />
<IconicItem disabled={!props.isEditing} icon="printer" action={props.print} text="Print" />
</PureDropdown>
<Item onClick={props.toggleHelp}>
<Button>Help</Button>
Expand Down
48 changes: 0 additions & 48 deletions src/components/controls/menus/MenuItem.tsx

This file was deleted.

63 changes: 63 additions & 0 deletions src/components/controls/toolbar/ToolbarButton.tsx
@@ -0,0 +1,63 @@
import React from "react";
import { Button } from "../Buttons";

export interface BasicToolbarItemData {
icon?: string;
items?: BasicToolbarItemData[];
text?: string;
}

export interface ToolbarItemData extends BasicToolbarItemData {
action?: (() => void) | ((event: React.MouseEvent) => void);

/** Whether or not text should be hidden when displayed on toolbar */
condensedButton?: boolean;

content?: React.ReactElement;
items?: ToolbarItemData[];
shortcut?: string;
};

export interface ToolbarButtonProps {
action?: (() => void) | ((event: React.MouseEvent) => void);
condensedButton?: boolean;
disabled?: boolean;
icon?: string;
dropdownChild ?: boolean;
text?: string;
shortcut ?: string;
}

/**
* A basic toolbar button
* @param props
*/
export default function ToolbarButton(props: ToolbarButtonProps) {
const text = props.condensedButton && !props.dropdownChild ?
<></> : <span className="button-text">{props.text}</span>
const icon = props.icon ? <i className={`icofont-${props.icon}`} /> : <></>
const shortcut = props.shortcut ? <span className="button-shortcut">{props.shortcut}</span> : <></>

let Container = Button;
if (props.dropdownChild) {
/** Thank you Google Chrome for allowing me to
* craft this beautiful hack because you won't allow
* buttons to be grid containers
*/
Container = (props) => {
return (
<Button {...props}>
<div>
{props.children}
</div>
</Button>
);
};
}

return (
<Container disabled={props.disabled} onClick={props.action}>
{icon} {text} {shortcut}
</Container>
);
}
43 changes: 43 additions & 0 deletions src/components/controls/toolbar/ToolbarItemFactory.tsx
@@ -0,0 +1,43 @@
import React from "react";

import ToolbarButton, { ToolbarItemData } from "./ToolbarButton";
import { PureDropdown, PureMenuItem } from "../menus/PureMenu";

export interface ToolbarItemProps extends ToolbarItemData {
dropdownChild?: boolean;
}

/**
* Factory function for rendering toolbar items
* @param props
*/
export default function ToolbarItemFactory(props: ToolbarItemProps) {
if (props.content) {
return <PureMenuItem>{props.content}</PureMenuItem>
}

if (!props.icon && !props.text) {
return <></>
}

/** Group of buttons */
if (props.items) {
return (
<PureDropdown
trigger={<ToolbarButton icon={props.icon} text={props.text} />}>
{props.items.map((value, index) =>
<ToolbarItemFactory key={index} dropdownChild={true} {...value} />
)}
</PureDropdown>
);
}

return (
<PureMenuItem>
<ToolbarButton
{...props}
disabled={!props.action && !props.items}
/>
</PureMenuItem>
);
}
119 changes: 5 additions & 114 deletions src/components/controls/toolbar/ToolbarMaker.tsx
@@ -1,127 +1,18 @@
import React from "react";
import { Button } from "../Buttons";
import PureMenu, { PureDropdown, PureMenuItem } from "../menus/PureMenu";
import IconicMenuItem from "../menus/MenuItem";

export interface BasicToolbarItemData {
icon?: string;
items?: Array<ToolbarItemData>;
text?: string;
}

export interface ToolbarItemData extends BasicToolbarItemData {
action?: (() => void) | ((event: React.MouseEvent) => void);

/** Whether or not text should be hidden when displayed on toolbar */
condensedButton?: boolean;

content?: React.ReactElement;
shortcut?: string;
};
import { ToolbarItemData } from "./ToolbarButton";
import PureMenu from "../menus/PureMenu";
import ToolbarSectionDropdown from "./ToolbarSectionDropdown";
import ToolbarItemFactory from "./ToolbarItemFactory";

export interface ToolbarSection {
/** Icon that appears on collapsed toolbar */
icon?: string;

items: Array<ToolbarItemData>;
}

export type ToolbarData = Map<string, ToolbarSection>;

function ToolbarItem(props: ToolbarItemData) {
const text = props.condensedButton ? undefined : props.text;

return (
<IconicMenuItem
disabled={!props.action && !props.items}
onClick={props.action}
icon={props.icon}
shortcut={props.shortcut}
text={text}
/>
);
}

/**
* Factory function for rendering toolbar items
* @param props
*/
function ToolbarItemFactory(props: ToolbarItemData) {
if (props.content) {
return <>{props.content}</>
}

if (!props.icon && !props.text) {
return <></>
}

const className = props.text ? "toolbar-button-has-text" : "toolbar-button";
const icon = props.icon ? <i className={`icofont-${props.icon}`} /> : <></>
const text = props.text && !props.condensedButton ? props.text : "";

if (props.items) {
return (
<PureDropdown
trigger={<Button className={className}>{icon} {text}</Button>}>
{props.items.map((value, index) =>
<ToolbarItemFactory key={index} {...value} />
)}
</PureDropdown>
);
}

return <ToolbarItem {...props} />
}

interface SectionDropdownProps extends BasicToolbarItemData {
items: ToolbarItemData[];
text: string;
}

/**
* Toolbar section dropdown menu that appears when menu is overflowing
* @param props
*/
function SectionDropdown(props: SectionDropdownProps) {
let [activeIndex, setActive] = React.useState(-1);

let items = props.items;
let heading = <></>
if (activeIndex >= 0) {
items = props.items[activeIndex].items as ToolbarItemData[]
heading = <PureMenuItem onClick={(event) => event.stopPropagation()}>
<h3>
<Button onClick={() => setActive(-1)}><i className="icofont-rounded-left" /></Button>
<span>{props.items[activeIndex].text}</span>
</h3>
</PureMenuItem>
}

const className = props.text ? "toolbar-button-has-text" : "toolbar-button";
const icon = props.icon ? <i className={`icofont-${props.icon}`} /> : <></>
return <PureDropdown
ulProps={{ className: "toolbar-collapsed-section" }}
trigger={<Button className={className}>{icon} {props.text}</Button>}>
{heading}
{items.map((item, index: number) => {
// If item contains a group of actions, then turn it
// into a trigger that when clicked, replaces the entire
// dropdown's contents with its own buttons
if (item.items) {
return <ToolbarItem {...item} action={
(event: React.MouseEvent) => {
setActive(index);
event.stopPropagation();
}}
condensedButton={false}
/>
}

return <ToolbarItemFactory {...item} condensedButton={false} />
})}
</PureDropdown>
}

export interface ToolbarProps {
data: ToolbarData;

Expand All @@ -139,7 +30,7 @@ export default function Toolbar(props: ToolbarProps) {
return (
<PureMenu horizontal>
{Array.from(props.data).map(([key, section]) =>
<SectionDropdown key={key} text={key} icon={section.icon} items={section.items} />
<ToolbarSectionDropdown key={key} text={key} icon={section.icon} items={section.items} />
)}
</PureMenu>
);
Expand Down

0 comments on commit 27596a4

Please sign in to comment.