Skip to content

Commit b3c3566

Browse files
authored
Merge branch 'develop' into chore/toast-tests
2 parents 2f35dbe + 723dc85 commit b3c3566

File tree

123 files changed

+2179
-2884
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

123 files changed

+2179
-2884
lines changed

.env.example

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ MAILGUN_KEY=<your-mailgun-api-key>
1919
ML5_LIBRARY_USERNAME=ml5
2020
ML5_LIBRARY_EMAIL=examples@ml5js.org
2121
ML5_LIBRARY_PASS=helloml5
22-
MOBILE_ENABLED=true
2322
MONGO_URL=mongodb://localhost:27017/p5js-web-editor
2423
PORT=8000
2524
PREVIEW_PORT=8002

README.md

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,10 @@ The p5.js Editor is a collaborative project created by many individuals, mostly
1212

1313
Learn more about [our community](https://p5js.org/community/) and read our community statement and [code of conduct](./.github/CODE_OF_CONDUCT.md). You can directly support our work with p5.js by [donating to the Processing Foundation](https://processingfoundation.org/support).
1414

15-
## Get Started
15+
## Getting Started
1616

1717
Make your first sketch in the [p5.js Editor](https://editor.p5js.org/)! Learn more about sketching with p5.js on the [Get Started](https://p5js.org/get-started/) and find everything you can do in the [Reference](https://p5js.org/reference/). You can also look at [examples](https://editor.p5js.org/p5/sketches) and remix them in the p5.js Editor.
1818

19-
## Setting Up the Development Environment
20-
21-
- Refer to [this documentation for setting up your environment](https://github.com/processing/p5.js-web-editor/blob/develop/contributor_docs/installation.md)
22-
23-
2419

2520
## Issues
2621

@@ -31,10 +26,45 @@ If you have found a bug in the p5.js Web Editor, you can file it under the ["iss
3126
* p5.sound: [https://github.com/processing/p5.js-sound/issues](https://github.com/processing/p5.js-sound/issues)
3227
* p5.js website: [https://github.com/processing/p5.js-website/issues](https://github.com/processing/p5.js-website/issues)
3328

29+
30+
### How Do I Know My Issue or Pull Request is Getting Reviewed?
31+
32+
To see which pull requests and issues are currently being reviewed, check the [PR Review Board](https://github.com/processing/p5.js-web-editor/projects/9) or the following Milestones: [PATCH Release](https://github.com/processing/p5.js-web-editor/milestone/10), [MINOR Release](https://github.com/processing/p5.js-web-editor/milestone/8).
33+
34+
Issues and Pull Requests categorized under the PATCH or MINOR Release Milestones will be prioritized since they are planned to be merged for the next release to Production. Please feel free to [comment on this pinned issue](https://github.com/processing/p5.js-web-editor/issues/2534) if you would like your issue to be considered for the next release!
35+
36+
37+
### When Will the Next Production Release Be?
38+
39+
We will aim to deploy on a 1-2 month basis. Here are some dates we’re working towards:
40+
41+
2.9.3 PATCH Release: By November 17, 2023
42+
43+
2.10.0 MINOR Release: By December 15, 2023
44+
45+
[You can read more about Semantic Versioning and the differences between a MINOR and PATCH release](https://semver.org/).
46+
47+
48+
## References for Contributing to the p5.js Web Editor
49+
50+
[Code of Conduct](https://editor.p5js.org/code-of-conduct)
51+
52+
[Contribution Guidelines for p5.js](https://p5js.org/contributor-docs/#/)
53+
54+
[Contribution Guidelines for the p5.js Web Editor](https://github.com/processing/p5.js-web-editor/tree/develop/contributor_docs)
55+
56+
[p5.js Community Statement](https://p5js.org/community/)
57+
58+
3459
## Acknowledgements
3560

3661
Support for this project has come from [Processing Foundation](https://processingfoundation.org/), [NYU ITP](https://tisch.nyu.edu/itp), [CS4All, NYC DOE](http://cs4all.nyc/), [COSA at DU](https://liberalarts.du.edu/emergent-digital-practices/open-source-arts), [STUDIO for Creative Inquiry](https://studioforcreativeinquiry.org/), [Grant for the Web](https://www.grantfortheweb.org/), [New Media Rights](https://www.newmediarights.org/), and many others.
3762

38-
Hosting and technical support has come from: <br />
39-
<a href="https://releasehub.com/" target="_blank"><img width="100" src="https://assets.website-files.com/603dd147c5b0a480611bd348/603dd147c5b0a469bc1bd451_logo--dark.svg" /></a>
63+
Hosting and technical support has come from:
64+
<br />
65+
<br />
66+
<a href="https://releasehub.com/" target="_blank"><img width="100" src="https://assets.website-files.com/603dd147c5b0a480611bd348/603dd147c5b0a469bc1bd451_logo--dark.svg" /></a>
67+
<br />
4068
<a href="https://www.browserstack.com/" target="_blank"><img width="100" src="https://user-images.githubusercontent.com/6063380/46976166-ab280a80-d096-11e8-983b-18dd38c8cc9b.png" /></a>
69+
<br />
70+
<a href="https://www.fastly.com/" target="_blank"><img width="100" src="https://cdn-assets-us.frontify.com/s3/frontify-enterprise-files-us/eyJwYXRoIjoiZmFzdGx5XC9hY2NvdW50c1wvYzJcLzQwMDEwMjNcL3Byb2plY3RzXC8xMVwvYXNzZXRzXC80ZVwvNzc0XC9lZTZmYzlkOWYzNWE1NjBkNjUzNjFkNGI0NGQ2MTNmZi0xNjIxNTIyODg4LnBuZyJ9:fastly:nVuY3PxyFqQMI6elJsMzxAGLH3IFlmiuMdacHAGRMkE?width=2400" /></a>

client/components/mobile/IconButton.jsx renamed to client/common/IconButton.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import styled from 'styled-components';
4-
import Button from '../../common/Button';
5-
import { remSize } from '../../theme';
4+
import Button from './Button';
5+
import { remSize } from '../theme';
66

77
const ButtonWrapper = styled(Button)`
88
width: ${remSize(48)};

client/modules/IDE/hooks/useKeyDownHandlers.js renamed to client/common/useKeyDownHandlers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import mapKeys from 'lodash/mapKeys';
1+
import { mapKeys } from 'lodash';
22
import PropTypes from 'prop-types';
33
import { useCallback, useEffect, useRef } from 'react';
44

client/common/useModalClose.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { useEffect, useRef } from 'react';
2+
import useKeyDownHandlers from './useKeyDownHandlers';
3+
4+
/**
5+
* Common logic for Modal, Overlay, etc.
6+
*
7+
* Pass in the `onClose` handler.
8+
*
9+
* Can optionally pass in a ref, in case the `onClose` function needs to use the ref.
10+
*
11+
* Calls the provided `onClose` function on:
12+
* - Press Escape key.
13+
* - Click outside the element.
14+
*
15+
* Returns a ref to attach to the outermost element of the modal.
16+
*
17+
* @param {() => void} onClose
18+
* @param {React.MutableRefObject<HTMLElement | null>} [passedRef]
19+
* @return {React.MutableRefObject<HTMLElement | null>}
20+
*/
21+
export default function useModalClose(onClose, passedRef) {
22+
const createdRef = useRef(null);
23+
const modalRef = passedRef || createdRef;
24+
25+
useEffect(() => {
26+
modalRef.current?.focus();
27+
28+
function handleClick(e) {
29+
// ignore clicks on the component itself
30+
if (modalRef.current && !modalRef.current.contains(e.target)) {
31+
onClose?.();
32+
}
33+
}
34+
35+
document.addEventListener('click', handleClick, false);
36+
37+
return () => {
38+
document.removeEventListener('click', handleClick, false);
39+
};
40+
}, [onClose, modalRef]);
41+
42+
useKeyDownHandlers({ escape: onClose });
43+
44+
return modalRef;
45+
}

client/components/Dropdown.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import styled from 'styled-components';
44
import { remSize, prop } from '../theme';
5-
import IconButton from './mobile/IconButton';
5+
import IconButton from '../common/IconButton';
66

7-
const DropdownWrapper = styled.ul`
7+
export const DropdownWrapper = styled.ul`
88
background-color: ${prop('Modal.background')};
99
border: 1px solid ${prop('Modal.border')};
1010
box-shadow: 0 0 18px 0 ${prop('shadowColor')};
@@ -52,6 +52,7 @@ const DropdownWrapper = styled.ul`
5252
& button span,
5353
& a {
5454
padding: ${remSize(8)} ${remSize(16)};
55+
font-size: ${remSize(12)};
5556
}
5657
5758
* {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import PropTypes from 'prop-types';
2+
import React, { forwardRef, useCallback, useRef, useState } from 'react';
3+
import useModalClose from '../../common/useModalClose';
4+
import DownArrowIcon from '../../images/down-filled-triangle.svg';
5+
import { DropdownWrapper } from '../Dropdown';
6+
7+
// TODO: enable arrow keys to navigate options from list
8+
9+
const DropdownMenu = forwardRef(
10+
(
11+
{ children, anchor, 'aria-label': ariaLabel, align, className, classes },
12+
ref
13+
) => {
14+
// Note: need to use a ref instead of a state to avoid stale closures.
15+
const focusedRef = useRef(false);
16+
17+
const [isOpen, setIsOpen] = useState(false);
18+
19+
const close = useCallback(() => setIsOpen(false), [setIsOpen]);
20+
21+
const anchorRef = useModalClose(close, ref);
22+
23+
const toggle = useCallback(() => {
24+
setIsOpen((prevState) => !prevState);
25+
}, [setIsOpen]);
26+
27+
const handleFocus = () => {
28+
focusedRef.current = true;
29+
};
30+
31+
const handleBlur = () => {
32+
focusedRef.current = false;
33+
setTimeout(() => {
34+
if (!focusedRef.current) {
35+
close();
36+
}
37+
}, 200);
38+
};
39+
40+
return (
41+
<div ref={anchorRef} className={className}>
42+
<button
43+
className={classes.button}
44+
aria-label={ariaLabel}
45+
tabIndex="0"
46+
onClick={toggle}
47+
onBlur={handleBlur}
48+
onFocus={handleFocus}
49+
>
50+
{anchor ?? <DownArrowIcon focusable="false" aria-hidden="true" />}
51+
</button>
52+
{isOpen && (
53+
<DropdownWrapper
54+
className={classes.list}
55+
align={align}
56+
onMouseUp={() => {
57+
setTimeout(close, 0);
58+
}}
59+
onBlur={handleBlur}
60+
onFocus={handleFocus}
61+
>
62+
{children}
63+
</DropdownWrapper>
64+
)}
65+
</div>
66+
);
67+
}
68+
);
69+
70+
DropdownMenu.propTypes = {
71+
/**
72+
* Provide <MenuItem> elements as children to control the contents of the menu.
73+
*/
74+
children: PropTypes.node.isRequired,
75+
/**
76+
* Can optionally override the contents of the button which opens the menu.
77+
* Defaults to <DownArrowIcon>
78+
*/
79+
anchor: PropTypes.node,
80+
'aria-label': PropTypes.string.isRequired,
81+
align: PropTypes.oneOf(['left', 'right']),
82+
className: PropTypes.string,
83+
classes: PropTypes.shape({
84+
button: PropTypes.string,
85+
list: PropTypes.string
86+
})
87+
};
88+
89+
DropdownMenu.defaultProps = {
90+
anchor: null,
91+
align: 'right',
92+
className: '',
93+
classes: {}
94+
};
95+
96+
export default DropdownMenu;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import ButtonOrLink from '../../common/ButtonOrLink';
4+
5+
// TODO: combine with NavMenuItem
6+
7+
function MenuItem({ hideIf, ...rest }) {
8+
if (hideIf) {
9+
return null;
10+
}
11+
12+
return (
13+
<li>
14+
<ButtonOrLink {...rest} />
15+
</li>
16+
);
17+
}
18+
19+
MenuItem.propTypes = {
20+
...ButtonOrLink.propTypes,
21+
onClick: PropTypes.func,
22+
value: PropTypes.string,
23+
/**
24+
* Provides a way to deal with optional items.
25+
*/
26+
hideIf: PropTypes.bool
27+
};
28+
29+
MenuItem.defaultProps = {
30+
onClick: null,
31+
value: null,
32+
hideIf: false
33+
};
34+
35+
export default MenuItem;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import { useMediaQuery } from 'react-responsive';
3+
import styled from 'styled-components';
4+
import { prop, remSize } from '../../theme';
5+
import DropdownMenu from './DropdownMenu';
6+
7+
import DownFilledTriangleIcon from '../../images/down-filled-triangle.svg';
8+
import MoreIconSvg from '../../images/more.svg';
9+
10+
const DotsHorizontal = styled(MoreIconSvg)`
11+
transform: rotate(90deg);
12+
`;
13+
14+
const TableDropdownIcon = () => {
15+
// TODO: centralize breakpoints
16+
const isMobile = useMediaQuery({ maxWidth: 770 });
17+
18+
return isMobile ? (
19+
<DotsHorizontal focusable="false" aria-hidden="true" />
20+
) : (
21+
<DownFilledTriangleIcon focusable="false" aria-hidden="true" />
22+
);
23+
};
24+
25+
const TableDropdown = styled(DropdownMenu).attrs({
26+
align: 'right',
27+
anchor: <TableDropdownIcon />
28+
})`
29+
& > button {
30+
width: ${remSize(25)};
31+
height: ${remSize(25)};
32+
padding: 0;
33+
& svg {
34+
max-width: 100%;
35+
max-height: 100%;
36+
}
37+
& polygon,
38+
& path {
39+
fill: ${prop('inactiveTextColor')};
40+
}
41+
}
42+
& ul {
43+
top: 63%;
44+
right: calc(100% - 26px);
45+
}
46+
`;
47+
48+
export default TableDropdown;

client/components/Nav/NavBar.jsx

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,18 @@
11
import PropTypes from 'prop-types';
2-
import React, {
3-
useCallback,
4-
useEffect,
5-
useMemo,
6-
useRef,
7-
useState
8-
} from 'react';
9-
import useKeyDownHandlers from '../../modules/IDE/hooks/useKeyDownHandlers';
2+
import React, { useCallback, useMemo, useRef, useState } from 'react';
3+
import useModalClose from '../../common/useModalClose';
104
import { MenuOpenContext, NavBarContext } from './contexts';
115

126
function NavBar({ children, className }) {
137
const [dropdownOpen, setDropdownOpen] = useState('none');
148

159
const timerRef = useRef(null);
1610

17-
const nodeRef = useRef(null);
11+
const handleClose = useCallback(() => {
12+
setDropdownOpen('none');
13+
}, [setDropdownOpen]);
1814

19-
useEffect(() => {
20-
function handleClick(e) {
21-
if (!nodeRef.current) {
22-
return;
23-
}
24-
if (nodeRef.current.contains(e.target)) {
25-
return;
26-
}
27-
setDropdownOpen('none');
28-
}
29-
document.addEventListener('mousedown', handleClick, false);
30-
return () => {
31-
document.removeEventListener('mousedown', handleClick, false);
32-
};
33-
}, [nodeRef, setDropdownOpen]);
34-
35-
useKeyDownHandlers({
36-
escape: () => setDropdownOpen('none')
37-
});
15+
const nodeRef = useModalClose(handleClose);
3816

3917
const clearHideTimeout = useCallback(() => {
4018
if (timerRef.current) {
@@ -71,7 +49,10 @@ function NavBar({ children, className }) {
7149
onFocus: clearHideTimeout
7250
}),
7351
createMenuItemHandlers: (dropdown) => ({
74-
onMouseUp: () => {
52+
onMouseUp: (e) => {
53+
if (e.button === 2) {
54+
return;
55+
}
7556
setDropdownOpen('none');
7657
},
7758
onBlur: handleBlur,

0 commit comments

Comments
 (0)