-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using NexusUI with React - Solution #65
Comments
I tryed to use React too, I'm interested in this! |
Hey there @hepiyellow , I'll do my best to answer this, albeit with very little knowledge of react. It sounds like you might be able to use
Let me know if this is a viable solution and I'll do my best to figure out alternative solutions if it's not! |
Well, The solution is to call I am just not sure yet if and how I "un-transform" a element which has been unmounted. (unmounted=removed from the DOM without a document re-load) |
I think the best way to do this, currently, is to use So, if your slider looks like this....
Then you could call this when you want to remove it:
|
would you share your code, please? |
I am still investigating this, so can't share code yet. There are more issues ahead. I will share some code or explain the whole solution once I have it working. (might take a few days) |
thank you, I'm not in a hurry so take your time. |
Ok, main issue solved. And turns out there no need for any change in NexusUI. Even no need to change 'nx' to 'data-nx'. Here is my minimal React component wrapper for a NexusUI canvas (using typescript): type NxType = string; //'dial' | 'slider' ,etc...
export interface INexusUICanvasProps {
type: NxType
attrs?: {
[index: string]: any
}
}
export default class NexusUICanvas extends Component<INexusUICanvasProps, {}> {
mountedCanvas: any;
componentDidMount() {
this.mountedCanvas.setAttribute('nx', this.props.type);
if (this.props.attrs) {
Object.keys(this.props.attrs).forEach((attrName) => {
this.mountedCanvas.setAttribute(attrName, this.props.attrs[attrName]);
});
}
(window as any).nx.transform(this.mountedCanvas);
}
componentWillUnmount() {
(window as any).nx.widgets[this.mountedCanvas.id].destroy();
}
render() {
return (
<canvas ref={this.refCallback.bind(this)} />
)
}
refCallback(domElement: any) {
this.mountedCanvas = domElement;
}
} The solution to the issue I mentioned before is not to do anything in the So actually the It seems that the whole lifecycle of Nexus canvas is working. |
I'm not sure. I have a few guesses. In preliminary tests (in vanilla js), I did not experience this issue. The following code produced a styled dial with a label:
The dial you are seeing is also an older style. I wonder if you are using an older version of NexusUI? Your react strategy -- using nx.transform, widget.destroy, etc -- will work in the newest version as well, I believe. Lastly, just a note that there is an optional second argument for nx.transform that denotes the widget's type. That might be more elegant than manually giving your canvas an nx attribute.
Good luck and also thank you for your work getting nexusUI working with React! I am terribly sorry that I was not more help earlier in the process. I graduated from the university where this was created as a research project, and I'm not sure they have found anyone to replace me on authorship yet. We will add a note to the website indicating as much. I hope to do a refactor later this year, in which case I would love to discuss ways to increase compatibility with React. |
Yep. I was using an old version of NexusUI. Thanks (-: About using |
One last issue, I'll check tomorrow, Any pointers on that? BTW: How do I change the label's font color? |
Initializing a UI component could take a moment, yes. I haven't tracked
down the source of that delay. I'll run some tests to see if I can
replicate it in vanilla. About how many UI components are we talking about
creating here -- 1, ~10, or ~100?
…On Wed, Jan 18, 2017 at 3:42 PM, hepiyellow ***@***.***> wrote:
One last issue, I'll check tomorrow,
is that is see all my nexusUI interfaces animate (less than 0.5 sec) from
position 0,0 to their final position (along with the whole other DOM
elements) every time they are created (and transformed).
Any pointers on that?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#65 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACe3uAoeAvxaDv0rdlzmJ1HRSC6D7rwMks5rTnkqgaJpZM4Ll8xt>
.
--
Ben Taylor
Digital Media Programming
@ Goucher College Digital Arts
whitechord.org
|
Putting the init issue aside. var elemToKill = document.getElementById(this.canvasID)
if (elemToKill) {
elemToKill.parentNode.removeChild(elemToKill);
} This might lead to a React error:
I encounter this case, and removing those line solves it. I also see in the var id = this.canvasID
delete nx.widgets[id];
delete window[id]; Are you adding the canvas IDs as properties on the global window object? it that safe? |
To "de-nexusUI" a canvas, without deleting it, you will probably need to
create a custom destroy method (or edit the existing destroy method). That
is not really a use case we planned for.
Re: global variables, yes I agree it is a bad practice. It stems from
legacy design considerations within the project. This toolkit was initially
designed for teaching young students how to code in a simple/fun way, using
music. There has been much resistance, from experienced developers who have
adopted this project, to that idea. I fully agree that this approach should
be changed in the next update.
For now, if you want to *not* use global variables, you can use
`nx.globalWidgets = false`
…On Thu, Jan 19, 2017 at 11:35 AM, hepiyellow ***@***.***> wrote:
Putting the init issue aside.
I found a new issue with using widget.destroy(). It seems that it also
removes the canvas from the DOM:
var elemToKill = document.getElementById(this.canvasID)if (elemToKill) {
elemToKill.parentNode.removeChild(elemToKill);
}
This might lead to a React error:
Uncaught TypeError: Cannot read property 'replaceChild' of null
at Function.replaceChildWithTree (DOMLazyTree.js:70)
I encounter this case, and removing those line solves it.
I think nexusUI (when working with React) should not intervene with DOM
structure.
I also see in the widget.destroy() function, these lines:
var id = this.canvasID
delete nx.widgets[id];
delete window[id];
Are you adding the canvas IDs as properties on the global window object?
it that safe?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#65 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACe3uFKUMZGqk9OpgVpbgpn3aeH6KTMIks5rT5C4gaJpZM4Ll8xt>
.
--
Ben Taylor
Digital Media Programming
@ Goucher College Digital Arts
whitechord.org
|
There was another issue with resizing. It would be best if the Here is my current version of NexusUICanvas (with typescript): import * as React from 'react';
import { Component } from 'react';
import * as Debug from "debug";
var debug = Debug('LM2.NexusUICanvas');
export type NxType = string; //'dial' | 'matrix' ,etc...
export interface NxWidget {
canvas: any;
height: number;
width: number;
preMove: any;
preRelease: any;
canvasID: any;
init(): void;
draw(): void;
resize(w?: number, h?: number): void;
checkPercentage(): void;
transmit(data: any): void;
set(value: any): void;
getName(): string;
[index: string]: any;
}
interface Attributes {
[index: string]: any
}
export interface INexusUICanvasPropsBase {
/**
* A style object to be set on the created canvas element.
*/
style?: {
height: number,
width: number,
[index: string]: any
}
/**
* An object containing properties that are to be set on the created
* canvas element before it is transformed into an NX interface.
*/
canvasAttrs?: Attributes
/**
* An object containing properties that are to be set on the created
* widget.
*/
widgetAttrs?: Attributes
}
export interface INexusUICanvasProps extends INexusUICanvasPropsBase {
type: NxType
}
// Need to make sure nx.globalWidgets = false to avoid global variable to the window object,
// which is
(window as any).nx.globalWidgets = false;
export default class NexusUICanvas extends Component<INexusUICanvasProps, {}> {
mountedCanvas: any;
widget: NxWidget;
componentDidMount() {
debug('componentDidMount() canvas.id: ', this.mountedCanvas.id, 'elm=', this.mountedCanvas);
// We set the canvas attribute 'nx' = type instead of calling transform(type),
// because manager.blockMove() is looking for the canvas.nx attribute.
this.mountedCanvas.setAttribute('nx', this.props.type);
this.updateCanvasFromProps();
// widget has internal state which is initialized (by the canvas) only when the widget is created.
// If the canvas is changed, we need to create a new widget.
// For example widget.height is initialized from canvas style when creating a new Widget. But it
// doesn't update when canvas style is changed.
this.widget = (window as any).nx.transform(this.mountedCanvas);
this.updateWidgetFromProps();
this.widget.draw();
}
componentWillUnmount() {
debug('componentWillUnmount() destroy canvas.id= ', this.mountedCanvas.id);
// We don't call the original widget.destroy() because it
// removes the canvas from the DOM, and we should leave that to React.
NexusUICanvas.destroyWidget(this.widget);
}
componentWillUpdate() {
debug('componentWillUpdate()');
}
shouldComponentUpdate(nextProps: INexusUICanvasProps, nextState: any) {
//TODO: optimize.
return true;
}
componentDidUpdate() {
debug('componentDidUpdate() widget=', this.widget);
this.updateCanvasFromProps();
this.updateWidgetFromProps();
}
render() {
debug('render(): props=', this.props);
return (
<canvas
style={this.props.style}
ref={this.refCallback.bind(this)}>
</canvas>
)
}
refCallback(domElement: any) {
debug('refCallback() domElm=', domElement);
this.mountedCanvas = domElement;
}
updateCanvasFromProps() {
const attrs = this.props.canvasAttrs;
if (attrs) {
Object.keys(attrs).forEach((attrName) => {
this.mountedCanvas.setAttribute(attrName, attrs[attrName]);
});
}
}
updateWidgetFromProps() {
const attrs = this.props.widgetAttrs;
if (attrs) {
Object.keys(attrs).forEach((attrName) => {
this.widget[attrName] = attrs[attrName];
});
}
// This is partial solution if we don't re-create a new widget when component is updated
if (this.props.style) {
this.widget.resize(this.props.style.width, this.props.style.height)
this.widget.checkPercentage();
}
this.widget.init();
}
/** @method destroy
Remove the widget object, canvas, and all related event listeners from the document.
*/
static destroyWidget(widget: NxWidget) {
const nx = (window as any).nx;
var type = nx.elemTypeArr.indexOf(widget.getName())
nx.elemTypeArr.splice(type, 1)
widget.canvas.ontouchmove = null;
widget.canvas.ontouchend = null;
widget.canvas.onclick = null;
widget.canvas.onmousemove = null;
widget.canvas.onmouseoff = null;
document.removeEventListener("mousemove", widget.preMove, false);
document.removeEventListener("mouseup", widget.preRelease, false);
// Commented-out original code which is inappropriate for React (EM)
// var elemToKill = document.getElementById(this.canvasID)
// if (elemToKill) {
// elemToKill.parentNode.removeChild(elemToKill);
// }
var id = widget.canvasID
delete nx.widgets[id];
// Added a check for nx.globalWidgets (EM)
if ((window as any).nx.globalWidgets === true) {
delete window[id];
}
}
} |
Hi @hepiyellow, Yes, There is an By the way, I noticed a bug with I am working on a refactor a.t.m., taking into account some of your concerns with react as well as other concerns from the past year. I imagine this revision will take some time, but I wanted to let you know it is in the works. Ben |
hi @hepiyellow , could you make a repo with a sample project? |
I will
… On 2017. Jan 31., at 10:22, Bruce LANE ***@***.***> wrote:
hi @hepiyellow , could you make a repo with a sample project?
I don't know about typescript, I guess it needs a transpiler task with gulp or webpack?
thank you very much
Bruce
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Here is my example for using NexusUI with React (and typescript) I think NexusUI is great to use with Mobx, I will add examples later on how to do it. https://github.com/hepiyellow/nexusui-mobx-react-typescript-sample |
oh yes, works fine! |
In the npm registry (npm install) the version is still 1.0.8, sith the old widgets, and no labels, no preset values. Can you update it please? |
Thanks @hepiyellow for your work sharing this tutorial. @turbo5 that's a different issue but thanks for catching it -- i've updated the npm registry to 1.2 with #76 . |
I know this is an old issue but it's the first result on Google for "nexus ui react". I am just posting my solution here incase anyone else is wondering how to do this. You will notice I am generating a random ID. This is because I do not rely on the ID for anything other than generating the dial and is purely personal choice. Everything else will be controlled by Here's a Dial Component: import React from 'react'
import Nexus from 'nexusui'
import randomID from 'random-id'
import PropTypes from 'prop-types'
class Dial extends React.Component {
static propTypes = {
size: PropTypes.arrayOf(PropTypes.number),
interaction: PropTypes.string,
mode: PropTypes.string,
min: PropTypes.number,
max: PropTypes.number,
step: PropTypes.number,
value: PropTypes.number,
}
static defaultProps = {
size: [75, 75],
interaction: 'radial',
mode: 'relative',
min: 0,
max: 1,
step: 0,
value: 0,
}
state = {
id: null
}
componentWillMount = () => {
this.setState({ id: randomID(10) })
}
onChange = value => {
console.log(`Dial value changed: ${value}`)
// You could pass in a callback via props to pass value to parent ...
// this.props.onChange(value)
}
componentDidMount = () => {
const dial = new Nexus.Dial(`#${this.state.id}`, {
size: this.props.size,
interaction: this.props.interaction, // "radial", "vertical", or "horizontal"
mode: this.props.mode, // "absolute" or "relative"
min: this.props.min,
max: this.props.max,
step: this.props.step,
value: this.props.value
})
dial.on('change', this.onChange)
}
render() {
return <div id={this.state.id}></div>
}
}
export default Dial Usage like so ... <Dial size={[ 100, 100 ]}/> And here is a Select Component import React from 'react'
import Nexus from 'nexusui'
import randomID from 'random-id'
import PropTypes from 'prop-types'
class Select extends React.Component {
static propTypes = {
size: PropTypes.arrayOf(PropTypes.number),
options: PropTypes.arrayOf(PropTypes.string),
value: PropTypes.number,
onChange: PropTypes.func,
}
static defaultProps = {
size: [ 100, 30 ],
value: null,
options: [],
onChange: () => {},
}
state = {
id: null,
selectedIndex: 0,
selectedValue: null
}
componentWillMount = () => {
this.setState({ id: randomID(10) })
}
onChange = ({ value, index }) => {
this.setState({
selectedIndex: index,
selectedValue: value
})
this.props.onChange(value, index)
}
componentDidMount = () => {
const select = new Nexus.Select(`#${this.state.id}`, {
size: this.props.size,
options: this.props.options
})
select.on('change', this.onChange)
}
render() {
return <div id={this.state.id}></div>
}
}
export default Select Usage: <Select
options={[ 'Major', 'Minor', 'Diminished' ]}
size={[ 100, 30 ]}
onChange={(value, index) => console.log(`Value ${value} is at index ${index}`)}
/> If you guys don't mind I would happily spend some weeks creating a react library for Nexus? I'd love to take on that task if nobody else is doing it as I plan to use Nexus in my many Web Audio projects with React. |
Thank you so much! This is great. Yes a react/Nexus library would be welcome. Accomplishing this with version 2 should be a lot more straightforward than it was in version 1. Please post issues if you find them. |
Working on it @taylorbf :) |
I am trying to figure out how to use the Nexus.Piano, would it be similar to how this dial is set-up? |
I've had a problem using this approach. When passing a callback to a Nexus components E.g when |
In case anyone here is interested, I needed to use NexusUI with React, so I wrote this library : react-nexusui Cheers ! |
Hi,
Previously I mentioned that in order to use NexusUI with react the property name "nx" must be changed to "data-nx" (See issue #59).
I did that localy, and it works in a very simple case, but here is the next blocker issue !
It is common in React to have a React Component render different React DOM Elements along it's lifecycle.
For example in this render method:
render() { if (!this.state.connected) { return <div>not connected</div>; } return <canvas data-nx="dial" />; }
it renders a
<div/>
when not connected , otherwise, renders a NexusUI dial canvas.In this case the problem is that the dial is not visible, not painted. I can see the 'canvas' element on the DOM (chrome debug tools) but that's it. (same code without the condition rendering of the
I think this happens, because NexusUI scans for
canvas
element on theonLoad
event only. And React doesn't reload the document, it manipulates the dom. So thought I would use themanager.add
function but it creates a canvas element. I don't want NexusUI to create the canvas, I guess I need React to create it.So I guess I need to add an ability to add/remove existing canvas elements to the
manager
.How would I add that, roughly?
The text was updated successfully, but these errors were encountered: