Skip to content

Commit

Permalink
Merge pull request facebook#108 from bvaughn/reach-ui-menubutton
Browse files Browse the repository at this point in the history
Use @reach MenuButton for owner stack menu [WIP]
  • Loading branch information
bvaughn authored Apr 9, 2019
2 parents 4bde8f6 + dbf6942 commit f1fc4b0
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 74 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ node_modules
shells/browser/chrome/build
shells/browser/firefox/build
shells/dev/build
vendor

package-lock.json
yarn.lock
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ node_modules
shells/browser/chrome/build
shells/browser/firefox/build
shells/dev/build
vendor

package-lock.json
yarn.lock
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,17 @@
],
"**/*.js": "eslint --max-warnings 0"
},
"resolutions": {
"@reach/portal": "file:./vendor/@reach/portal"
},
"dependencies": {
"@babel/core": "^7.1.6",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-transform-flow-strip-types": "^7.1.6",
"@babel/preset-env": "^7.1.6",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@reach/menu-button": "^0.1.9",
"adm-zip": "^0.4.7",
"babel-core": "^7.0.0-bridge",
"babel-eslint": "^9.0.0",
Expand Down Expand Up @@ -99,7 +103,6 @@
"react-color": "^2.11.7",
"react-dom": "^16.8.4",
"react-is": "^16.8.4",
"react-portal": "^3.1.0",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.5.1",
"scheduler": "^0.13",
Expand Down
57 changes: 45 additions & 12 deletions src/devtools/views/Components/OwnersStack.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
}

.Component,
.Component[data-reach-menu-item],
.SelectedComponent {
padding: 0.25rem;
margin-right: 0.5rem;
Expand All @@ -17,19 +18,27 @@
background: none;
cursor: pointer;
text-align: left;
user-select: none;
}
.Component:hover {

.Component:hover,
.Component[data-reach-menu-item]:hover {
background-color: var(--color-hover-background);
}
.Component:focus {
.Component:focus,
.Component[data-reach-menu-item]:focus {
outline: none;
background-color: var(--color-hover-background);
}

.SelectedComponent {
.Component[data-reach-menu-item][data-selected],
.Component[data-reach-menu-item][data-selected]:hover,
.SelectedComponent,
.SelectedComponent:hover {
background-color: var(--color-selected-background);
color: var(--color-selected-foreground);
}
.Component[data-reach-menu-item][data-selected]:focus,
.SelectedComponent:focus {
outline: none;
}
Expand All @@ -39,10 +48,6 @@
flex: 1 0 auto;
}

.Toggle {
margin-right: 0.5rem;
}

.VRule {
flex: 0 0 auto;
height: 20px;
Expand All @@ -51,18 +56,46 @@
margin: 0 0.5rem;
}

.Modal {
position: absolute;
top: calc(100% + 0.25rem);
left: 2.5rem;
z-index: 1;
.MenuButton {
border-radius: 0.25rem;
display: inline-flex;
align-items: center;
padding: 0.25rem;
cursor: pointer;
flex: 0 0 auto;
border: none;
background: var(--color-button-background);
color: var(--color-button);
margin-right: 0.5rem;
}
.MenuButton:hover {
background: var(--color-button-background-hover);
color: var(--color-button-hover);
}
.MenuButton[aria-expanded='true'],
.MenuButton[aria-expanded='true']:active {
background: var(--color-button-background-focus);
color: var(--color-button-focus);
outline: none;
}
.MenuButton:focus-within {
box-shadow: 0 0 0 2px var(--color-button-background-focus) inset;
outline: none;
}

.Modal[data-reach-menu-list] {
display: inline-flex;
flex-direction: column;
background-color: var(--color-background);
color: var(--color-text-color);
padding: 0.5rem;
padding-right: 0;
border: 1px solid var(--color-border);
border-radius: 0.25rem;
max-height: 10rem;
overflow: auto;

/* Reach UI tries to set its own :( */
font-family: var(--font-family-monospace);
font-size: var(--font-size-monospace-normal);
}
65 changes: 17 additions & 48 deletions src/devtools/views/Components/OwnersStack.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
// @flow
import React, {
Fragment,
useCallback,
useContext,
useLayoutEffect,
useRef,
useState,
} from 'react';
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import Toggle from '../Toggle';
import { TreeContext } from './TreeContext';
import { StoreContext } from '../context';
import { useIsOverflowing, useModalDismissSignal } from '../hooks';
import { useIsOverflowing } from '../hooks';

import type { Element } from './types';

Expand Down Expand Up @@ -92,53 +91,23 @@ function ElementsDropdown({
const store = useContext(StoreContext);
const { selectOwner } = useContext(TreeContext);

const [isDropdownVisible, setIsDropdownVisible] = useState(false);

const handleDropdownButtonClick = useCallback(() => {
setIsDropdownVisible(!isDropdownVisible);
}, [isDropdownVisible, setIsDropdownVisible]);

const handleElementClick = useCallback(
(id: number) => {
selectOwner(id);
setIsDropdownVisible(false);
},
[selectOwner, setIsDropdownVisible]
);

const modalRef = useRef<HTMLDivElement | null>(null);
const dismissModal = useCallback(() => setIsDropdownVisible(false));

useModalDismissSignal(modalRef, dismissModal);

return (
<Fragment>
<Toggle
className={styles.Toggle}
isChecked={isDropdownVisible}
onChange={handleDropdownButtonClick}
title="Open elements dropdown"
>
<Menu>
<MenuButton className={styles.MenuButton} title="Open elements dropdown">
<ButtonIcon type="more" />
</Toggle>
{isDropdownVisible && (
<div className={styles.Modal} ref={modalRef}>
{ownerStack.map((id, index) => (
<button
key={id}
className={
ownerStackIndex === index
? styles.SelectedComponent
: styles.Component
}
onClick={() => handleElementClick(id)}
>
{((store.getElementByID(id): any): Element).displayName}
</button>
))}
</div>
)}
</Fragment>
</MenuButton>
<MenuList className={styles.Modal}>
{ownerStack.map((id, index) => (
<MenuItem
key={id}
className={styles.Component}
onSelect={() => selectOwner(id)}
>
{((store.getElementByID(id): any): Element).displayName}
</MenuItem>
))}
</MenuList>
</Menu>
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/devtools/views/Components/Tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default function Tree(props: Props) {
}

const handleKeyDown = (event: KeyboardEvent) => {
if ((event: any).target.tagName === 'INPUT') {
if ((event: any).target.tagName === 'INPUT' || event.defaultPrevented) {
return;
}

Expand Down
1 change: 1 addition & 0 deletions src/devtools/views/DevTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ReactLogo from './ReactLogo';

import styles from './DevTools.css';

import '@reach/menu-button/styles.css';
import './root.css';

import type { Bridge } from '../../types';
Expand Down
1 change: 1 addition & 0 deletions vendor/@reach/portal/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
index.js
44 changes: 44 additions & 0 deletions vendor/@reach/portal/es/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from "react";
import { createPortal } from "react-dom";
import Component from "@reach/component-component";

var Portal = function Portal(_ref) {
var children = _ref.children,
_ref$type = _ref.type,
type = _ref$type === undefined ? "reach-portal" : _ref$type;
return React.createElement(Component, {
getRefs: function getRefs() {
return { mountNode: null, portalNode: null };
},
didMount: function didMount(_ref2) {
var refs = _ref2.refs,
forceUpdate = _ref2.forceUpdate;

// It's possible that the content we are portal has, itself, been portaled.
// In that case, it's important to append to the correct document element.
var ownerDocument = refs.mountNode.ownerDocument;
refs.portalNode = ownerDocument.createElement(type);
ownerDocument.body.appendChild(refs.portalNode);
forceUpdate();
},
willUnmount: function willUnmount(_ref3) {
var portalNode = _ref3.refs.portalNode;

portalNode.ownerDocument.body.removeChild(portalNode);
},
render: function render(_ref4) {
var refs = _ref4.refs;
var portalNode = refs.portalNode;

if (!portalNode) {
return React.createElement("div", { ref: function ref(div) {
return refs.mountNode = div;
} });
} else {
return createPortal(children, portalNode);
}
}
});
};

export default Portal;
35 changes: 35 additions & 0 deletions vendor/@reach/portal/examples/basic.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";
import Portal from "../src/index";

export let name = "Basic";

export let Example = () => (
<div
style={{
height: 40,
overflow: "auto"
}}
>
<div style={{ border: "solid 5px", padding: 20, marginLeft: 170 }}>
This is in the normal react root, with an overflow hidden parent, clips
the box.
</div>
<Portal>
<div
style={{
position: "absolute",
top: 0,
left: 20,
width: 100,
border: "solid 5px",
padding: 20,
background: "#f0f0f0"
}}
>
This is in the portal, rendered in the DOM at the document root so the
CSS doesn't screw things up, but we render it in the react hierarchy
where it makes sense.
</div>
</Portal>
</div>
);
56 changes: 56 additions & 0 deletions vendor/@reach/portal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use strict";

exports.__esModule = true;

var _react = require("react");

var _react2 = _interopRequireDefault(_react);

var _reactDom = require("react-dom");

var _componentComponent = require("@reach/component-component");

var _componentComponent2 = _interopRequireDefault(_componentComponent);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var Portal = function Portal(_ref) {
var children = _ref.children,
_ref$type = _ref.type,
type = _ref$type === undefined ? "reach-portal" : _ref$type;
return _react2.default.createElement(_componentComponent2.default, {
getRefs: function getRefs() {
return { mountNode: null, portalNode: null };
},
didMount: function didMount(_ref2) {
var refs = _ref2.refs,
forceUpdate = _ref2.forceUpdate;

// It's possible that the content we are portal has, itself, been portaled.
// In that case, it's important to append to the correct document element.
var ownerDocument = refs.mountNode.ownerDocument;
refs.portalNode = ownerDocument.createElement(type);
ownerDocument.body.appendChild(refs.portalNode);
forceUpdate();
},
willUnmount: function willUnmount(_ref3) {
var portalNode = _ref3.refs.portalNode;

portalNode.ownerDocument.body.removeChild(portalNode);
},
render: function render(_ref4) {
var refs = _ref4.refs;
var portalNode = refs.portalNode;

if (!portalNode) {
return _react2.default.createElement("div", { ref: function ref(div) {
return refs.mountNode = div;
} });
} else {
return (0, _reactDom.createPortal)(children, portalNode);
}
}
});
};

exports.default = Portal;
Loading

0 comments on commit f1fc4b0

Please sign in to comment.