Page MenuHomePhabricator

D3374.diff
No OneTemporary

D3374.diff

diff --git a/web/chat/thread-menu.css b/web/chat/thread-menu.css
--- a/web/chat/thread-menu.css
+++ b/web/chat/thread-menu.css
@@ -1,58 +1,6 @@
-button.topBarMenuButton {
- background-color: transparent;
- border: none;
- cursor: pointer;
- color: var(--thread-top-bar-menu-color);
-}
-
-div.topBarMenuActionList {
- position: absolute;
- right: 10px;
- top: 55px;
- z-index: 1;
- display: flex;
- flex-direction: column;
- background-color: var(--thread-menu-bg);
- border-radius: 4px;
- padding: 4px 0;
-}
-
-button.topBarMenuAction {
- z-index: 1;
- background-color: transparent;
- padding: 12px 16px;
- color: var(--thread-menu-color);
- background-color: var(--thread-menu-bg);
- font-size: var(--m-font-16);
- line-height: 1.5;
- border: none;
- cursor: pointer;
- display: flex;
- align-items: center;
-}
-
-button.topBarMenuAction:hover {
- color: var(--thread-menu-color-hover);
-}
-
-div.topBarMenuActionIcon {
- font-size: var(--l-font-18);
- display: flex;
- justify-content: center;
- margin-right: 8px;
- width: 20px;
-}
-
-button.topBarMenuActionDangerous {
- color: var(--thread-menu-color-dangerous);
-}
-button.topBarMenuActionDangerous:hover {
- color: var(--thread-menu-color-dangerous-hover);
-}
-
hr.separator {
height: 1px;
- background: var(--thread-menu-separator-color);
+ background: var(--menu-separator-color);
margin: 10px 16px;
max-width: 130px;
border: none;
diff --git a/web/chat/thread-menu.react.js b/web/chat/thread-menu.react.js
--- a/web/chat/thread-menu.react.js
+++ b/web/chat/thread-menu.react.js
@@ -31,13 +31,14 @@
useDispatchActionPromise,
} from 'lib/utils/action-utils';
+import MenuItem from '../components/menu-item.react';
+import Menu from '../components/menu.react';
import SidebarListModal from '../modals/chat/sidebar-list-modal.react';
import { useModalContext } from '../modals/modal-provider.react';
import ConfirmLeaveThreadModal from '../modals/threads/confirm-leave-thread-modal.react';
import ThreadSettingsModal from '../modals/threads/thread-settings-modal.react';
import { useSelector } from '../redux/redux-utils';
import SWMansionIcon from '../SWMansionIcon.react';
-import ThreadMenuItem from './thread-menu-item.react';
import css from './thread-menu.css';
type ThreadMenuProps = {
@@ -45,8 +46,6 @@
};
function ThreadMenu(props: ThreadMenuProps): React.Node {
- const [isOpen, setIsOpen] = React.useState(false);
-
const { setModal, clearModal } = useModalContext();
const { threadInfo } = props;
@@ -58,7 +57,7 @@
const settingsItem = React.useMemo(() => {
return (
- <ThreadMenuItem
+ <MenuItem
key="settings"
text="Settings"
icon={faCog}
@@ -71,7 +70,7 @@
if (threadInfo.type === threadTypes.PERSONAL) {
return null;
}
- return <ThreadMenuItem key="members" text="Members" icon={faUserFriends} />;
+ return <MenuItem key="members" text="Members" icon={faUserFriends} />;
}, [threadInfo.type]);
const childThreads = useSelector(
@@ -94,7 +93,7 @@
return null;
}
return (
- <ThreadMenuItem
+ <MenuItem
key="sidebars"
text="Sidebars"
icon={faArrowRight}
@@ -117,11 +116,7 @@
return null;
}
return (
- <ThreadMenuItem
- key="subchannels"
- text="Subchannels"
- icon={faCommentAlt}
- />
+ <MenuItem key="subchannels" text="Subchannels" icon={faCommentAlt} />
);
}, [canCreateSubchannels, hasSubchannels]);
@@ -130,7 +125,7 @@
return null;
}
return (
- <ThreadMenuItem
+ <MenuItem
key="newSubchannel"
text="Create new subchannel"
icon={faPlusCircle}
@@ -170,7 +165,7 @@
return null;
}
return (
- <ThreadMenuItem
+ <MenuItem
key="leave"
text="Leave Thread"
icon={faSignOutAlt}
@@ -182,7 +177,7 @@
const menuItems = React.useMemo(() => {
const notificationsItem = (
- <ThreadMenuItem key="notifications" text="Notifications" icon={faBell} />
+ <MenuItem key="notifications" text="Notifications" icon={faBell} />
);
const separator = <hr key="separator" className={css.separator} />;
@@ -211,45 +206,11 @@
createSubchannelsItem,
leaveThreadItem,
]);
-
- const closeMenuCallback = React.useCallback(() => {
- document.removeEventListener('click', closeMenuCallback);
- if (isOpen) {
- setIsOpen(false);
- }
- }, [isOpen]);
-
- React.useEffect(() => {
- if (!document || !isOpen) {
- return undefined;
- }
- document.addEventListener('click', closeMenuCallback);
- return () => document.removeEventListener('click', closeMenuCallback);
- }, [closeMenuCallback, isOpen]);
-
- const switchMenuCallback = React.useCallback(() => {
- setIsOpen(isMenuOpen => !isMenuOpen);
- }, []);
-
- if (menuItems.length === 0) {
- return null;
- }
-
- let menuActionList = null;
- if (isOpen) {
- menuActionList = (
- <div className={css.topBarMenuActionList}>{menuItems}</div>
- );
- }
-
- return (
- <>
- <button className={css.topBarMenuButton} onClick={switchMenuCallback}>
- <SWMansionIcon icon="menu-vertical" size={24} />
- </button>
- {menuActionList}
- </>
+ const icon = React.useMemo(
+ () => <SWMansionIcon icon="menu-vertical" size={20} />,
+ [],
);
+ return <Menu icon={icon}>{menuItems}</Menu>;
}
export default ThreadMenu;
diff --git a/web/chat/thread-menu-item.react.js b/web/components/menu-item.react.js
rename from web/chat/thread-menu-item.react.js
rename to web/components/menu-item.react.js
--- a/web/chat/thread-menu-item.react.js
+++ b/web/components/menu-item.react.js
@@ -5,24 +5,24 @@
import classNames from 'classnames';
import * as React from 'react';
-import css from './thread-menu.css';
+import css from './menu.css';
-type ThreadMenuItemProps = {
+type MenuItemProps = {
+onClick?: () => mixed,
+icon: IconDefinition,
+text: string,
+dangerous?: boolean,
};
-function ThreadMenuItem(props: ThreadMenuItemProps): React.Node {
+function MenuItem(props: MenuItemProps): React.Node {
const { onClick, icon, text, dangerous } = props;
- const itemClasses = classNames(css.topBarMenuAction, {
- [css.topBarMenuActionDangerous]: dangerous,
+ const itemClasses = classNames(css.menuAction, {
+ [css.menuActionDangerous]: dangerous,
});
return (
<button className={itemClasses} onClick={onClick}>
- <div className={css.topBarMenuActionIcon}>
+ <div className={css.menuActionIcon}>
<FontAwesomeIcon icon={icon} className={css.promptIcon} />
</div>
<div>{text}</div>
@@ -30,8 +30,8 @@
);
}
-const MemoizedThreadMenuItem: React.ComponentType<ThreadMenuItemProps> = React.memo(
- ThreadMenuItem,
+const MemoizedMenuItem: React.ComponentType<MenuItemProps> = React.memo(
+ MenuItem,
);
-export default MemoizedThreadMenuItem;
+export default MemoizedMenuItem;
diff --git a/web/chat/thread-menu.css b/web/components/menu.css
copy from web/chat/thread-menu.css
copy to web/components/menu.css
--- a/web/chat/thread-menu.css
+++ b/web/components/menu.css
@@ -1,28 +1,28 @@
-button.topBarMenuButton {
+button.menuButton {
background-color: transparent;
border: none;
cursor: pointer;
color: var(--thread-top-bar-menu-color);
}
-div.topBarMenuActionList {
+div.menuActionList {
position: absolute;
right: 10px;
top: 55px;
z-index: 1;
display: flex;
flex-direction: column;
- background-color: var(--thread-menu-bg);
+ background-color: var(--menu-bg);
border-radius: 4px;
padding: 4px 0;
}
-button.topBarMenuAction {
+button.menuAction {
z-index: 1;
background-color: transparent;
padding: 12px 16px;
- color: var(--thread-menu-color);
- background-color: var(--thread-menu-bg);
+ color: var(--menu-color);
+ background-color: var(--menu-bg);
font-size: var(--m-font-16);
line-height: 1.5;
border: none;
@@ -31,11 +31,11 @@
align-items: center;
}
-button.topBarMenuAction:hover {
- color: var(--thread-menu-color-hover);
+button.menuAction:hover {
+ color: var(--menu-color-hover);
}
-div.topBarMenuActionIcon {
+div.menuActionIcon {
font-size: var(--l-font-18);
display: flex;
justify-content: center;
@@ -43,16 +43,16 @@
width: 20px;
}
-button.topBarMenuActionDangerous {
- color: var(--thread-menu-color-dangerous);
+button.menuActionDangerous {
+ color: var(--menu-color-dangerous);
}
-button.topBarMenuActionDangerous:hover {
- color: var(--thread-menu-color-dangerous-hover);
+button.menuActionDangerous:hover {
+ color: var(--menu-color-dangerous-hover);
}
hr.separator {
height: 1px;
- background: var(--thread-menu-separator-color);
+ background: var(--menu-separator-color);
margin: 10px 16px;
max-width: 130px;
border: none;
diff --git a/web/components/menu.react.js b/web/components/menu.react.js
new file mode 100644
--- /dev/null
+++ b/web/components/menu.react.js
@@ -0,0 +1,55 @@
+// @flow
+
+import * as React from 'react';
+
+import css from './menu.css';
+
+type MenuProps = {
+ +icon: React.Node,
+ +children?: React.Node,
+};
+
+function Menu(props: MenuProps): React.Node {
+ const [isOpen, setIsOpen] = React.useState(false);
+
+ const { icon, children } = props;
+
+ const closeMenuCallback = React.useCallback(() => {
+ document.removeEventListener('click', closeMenuCallback);
+ if (isOpen) {
+ setIsOpen(false);
+ }
+ }, [isOpen]);
+
+ React.useEffect(() => {
+ if (!document || !isOpen) {
+ return undefined;
+ }
+ document.addEventListener('click', closeMenuCallback);
+ return () => document.removeEventListener('click', closeMenuCallback);
+ }, [closeMenuCallback, isOpen]);
+
+ const switchMenuCallback = React.useCallback(() => {
+ setIsOpen(isMenuOpen => !isMenuOpen);
+ }, []);
+
+ if (React.Children.count(children) === 0) {
+ return null;
+ }
+
+ let menuActionList = null;
+ if (isOpen) {
+ menuActionList = <div className={css.menuActionList}>{children}</div>;
+ }
+
+ return (
+ <div>
+ <button className={css.menuButton} onClick={switchMenuCallback}>
+ {icon}
+ </button>
+ {menuActionList}
+ </div>
+ );
+}
+
+export default Menu;
diff --git a/web/theme.css b/web/theme.css
--- a/web/theme.css
+++ b/web/theme.css
@@ -104,12 +104,12 @@
--thread-ancestor-separator-color: var(--shades-white-60);
--text-message-default-background: var(--shades-black-80);
--message-action-tooltip-bg: var(--shades-black-90);
- --thread-menu-bg: var(--shades-black-90);
- --thread-menu-separator-color: var(--shades-black-80);
- --thread-menu-color: var(--shades-black-60);
- --thread-menu-color-hover: var(--shades-white-100);
- --thread-menu-color-dangerous: var(--error-primary);
- --thread-menu-color-dangerous-hover: var(--error-light-50);
+ --menu-bg: var(--shades-black-90);
+ --menu-separator-color: var(--shades-black-80);
+ --menu-color: var(--shades-black-60);
+ --menu-color-hover: var(--shades-white-100);
+ --menu-color-dangerous: var(--error-primary);
+ --menu-color-dangerous-hover: var(--error-light-50);
--app-list-icon-read-only-color: var(--shades-black-60);
--app-list-icon-enabled-color: var(--success-primary);
--app-list-icon-disabled-color: var(--shades-white-80);

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 19, 5:17 AM (21 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2675458
Default Alt Text
D3374.diff (11 KB)

Event Timeline