Skip to content
This repository has been archived by the owner on Jun 3, 2022. It is now read-only.

Initial commit of component render classes. #8

Merged
merged 20 commits into from
Nov 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0638019
Initial commit of component render classes.
chriscox Oct 20, 2016
e5d96a8
Merge branch 'develop' into feature/components
chriscox Oct 25, 2016
8500f7e
First pass of updates per feedback.
chriscox Oct 25, 2016
dad1c8b
Another pass of updates per feedback.
chriscox Oct 26, 2016
3c4c4fe
Refactors components to controls using Composition rather than Inheri…
chriscox Nov 7, 2016
2a0e894
Finalizes refactor of components to controls using Composition rather…
chriscox Nov 9, 2016
6bc9a3e
Updates constants formatting.
chriscox Nov 9, 2016
db0fa11
Fixes styles.less spacing/tabs.
chriscox Nov 9, 2016
939dbf7
Adds class commenting. Adds missing TextFieldControl. Removes SliderC…
chriscox Nov 9, 2016
f5d12f7
Merge branch 'develop' into feature/components
chriscox Nov 10, 2016
9ff18a0
Cleans up a few comment and strings.
chriscox Nov 10, 2016
2840c31
Updates control interfaces.
chriscox Nov 15, 2016
9bde50b
Refactors variable controls to call a prop callback method when updated.
chriscox Nov 17, 2016
be4a5d8
Work-in-progress.
chriscox Nov 18, 2016
bef5a8e
Now clones variables during update, allowing each control to handle i…
chriscox Nov 21, 2016
5d548ad
Resets package.json.
chriscox Nov 21, 2016
f791971
Merge branch 'develop' into feature/components
chriscox Nov 21, 2016
95e2423
Adds a render.tsx with logic that handles redrawing when variables up…
chriscox Nov 21, 2016
fffa746
Fixes adding callbacks to internal _callbacks
chriscox Nov 21, 2016
e1ea4fc
Minor code cleanup.
chriscox Nov 21, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/core/variables/Variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export interface VariableListParams extends VariableParams {
* @extends Function
*/
export interface VariableCallback extends Function {
variable: Variable;
variable?: Variable;
}

/**
Expand Down Expand Up @@ -78,7 +78,7 @@ export class Variable implements VariableParams {
this.defaultValue = defaultValue;
this._selectedValue = defaultValue;
if (callback) {
this.callbacks.push(callback);
this._callbacks.push(callback);
}
this._initialized = true;
}
Expand Down Expand Up @@ -156,6 +156,14 @@ export class Variable implements VariableParams {
return this._callbacks;
}

/**
* Adds a callback to array of callbacks.
* @param {VariableCallback} callback The callback to add.
*/
addCallback(callback: VariableCallback): any {
this._callbacks.push(callback);
}

/**
* Invokes each of the callback methods.
*/
Expand All @@ -170,7 +178,7 @@ export class Variable implements VariableParams {
* @param {VariableCallback} callback The callback to add and execute.
*/
addAndExecuteCallback(callback: VariableCallback): void {
this.callbacks.push(callback);
this._callbacks.push(callback);
callback(this);
}

Expand Down
100 changes: 100 additions & 0 deletions src/ui/DOMController.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/** @license
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

import * as React from "react";
import { KeyCode, KeyEvent, CSS } from "../lib/Constants";
import { Messaging } from "../lib/Messaging";
import { Overlay } from "./overlay/Overlay";
import { Variable } from "../core/variables/Variable";

/**
* Interface for the properties assigned to the DOMController component.
* @interface
*/
interface ControllerProps {
wrapperElement: HTMLElement;
variables: Array<Variable>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird that you are specifying variables externally but onUpdate internally. What you really want to do is have every call to updateVariable update variables and then re-render with the updated version. That would remove the need for setState/forceUpdate inside your controls and let them be pure functions.

The idiomatic way to do that would be to have variables (and its values) be immutable, so each control could say

shouldComponentUpdate(nextProps) {
  return nextProps.variable !== this.props.variable
}

updateVariable(variable: Variable, selectedValue: any): void;
}

/**
* Renders an MDL card-styled overlay containing a child control for each
* variable.
* @class
* @extends React.Component
*/
export class DOMController extends React.Component<ControllerProps, void> {

/** @override */
componentDidMount() {
// Register for messaging and key events.
Messaging.register(this.onMessageReceived.bind(this));
this.addKeyListener();
}

/** @override */
componentWillUnmount() {
// Unregister for messaging and key events.
Messaging.unregister();
this.removeKeyListener();
}

/**
* Handles receiving of window message.
* @param {MessageEvent} event The received message event.
*/
onMessageReceived(event: MessageEvent): void {
if (event.data === Messaging.type.ToggleVisibility) {
this.toggleVisibility();
}
}

/** Adds a key listener. */
addKeyListener(): void {
document.addEventListener(KeyEvent.DOWN, (e: KeyboardEvent) => {
if (e.keyCode === KeyCode.ESC) {
this.toggleVisibility();
}
});
}

/** Removes a key listener. */
removeKeyListener(): void {
document.removeEventListener(KeyEvent.DOWN);
}

/** Toggles the Remixer overlay visibility. */
toggleVisibility() {
this.props.wrapperElement.classList.toggle(CSS.RMX_VISIBLE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like I've asked this in every review and haven't seen any comments/changes. Maybe the comments got lost in the GitHub UI?

Why are you mutating the DOM of an element this component doesn't control? Couldn't this just be:

toggleVisibility() {
  this.setState({
    isVisible: !this.props.isVisible,
  }})
}

render() {
  const {
    isVisible,
    variables,
    updateVariable,
  } = this.props;

  if (this.props.isVisible) {
    return (
      // your DOM tree
    )
  } else {
    return <div />;
  }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we chatted about this. Its part of #12. Just never made that change.

}

/** @override */
render() {
return (
<div className="mdl-card mdl-shadow--6dp">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use string literals in some places and groups of CSSClassName.EXAMPLE in others?

<div className="mdl-card__title" ref="myInput">
<h2 className="mdl-card__title-text">Remixer</h2>
</div>
<div className="mdl-card__supporting-text mdl-card__actions mdl-card--border">
<Overlay
variables={this.props.variables}
updateVariable={this.props.updateVariable}
/>
</div>
</div>
);
}
}
100 changes: 100 additions & 0 deletions src/ui/controls/ColorSwatchControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/** @license
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

import * as React from "react";
import { ColorControlProps } from "./controlProps";
import { CSS, VariableType } from "../../lib/Constants";

/**
* A color swatch picker control consisting of a single color swatch for each
* possible value.
* @class
* @extends React.Component
*/
export class ColorSwatchControl extends React.Component<ColorControlProps, void> {

/** Handles the update event for this control. */
onClick = (event: React.FormEvent<HTMLElement>): void => {
this.props.updateVariable(
this.props.variable,
(event.target as HTMLElement).dataset["value"]
);
}

/** @override */
shouldComponentUpdate(nextProps: ColorControlProps) {
return nextProps.variable !== this.props.variable;
}

/** @override */
render() {
const {
title,
possibleValues,
selectedValue
} = this.props.variable;

return (
<div className={`${CSS.RMX_COLOR_SWATCH} ${CSS.MDL_LIST_ITEM} ${CSS.MDL_TWO_LINE}`}>
<span className={CSS.MDL_PRIMARY}>
<span>{title}
<span className={CSS.RMX_SELECTED_VALUE}>{selectedValue}</span>
</span>
<span className={CSS.MDL_SECONDARY}>
{possibleValues.map((value: string) => (
<ColorSwatch color={value} key={value}
isSelected={selectedValue === value}
onClick={this.onClick}
/>
))}
</span>
</span>
</div>
);
}
}

/**
* Interface containing properties for a single color swatch.
* @interface
*/
interface ColorSwatchProps {
color: string;
isSelected: boolean;
onClick: any;
}

/**
* Returns a single color swatch displayed within the `ColorSwatchControl`.
* @param {ColorSwatchProps} props The color swatch properties.
*/
function ColorSwatch(props: ColorSwatchProps) {
const {
color,
isSelected,
onClick,
} = props;
return (
<div
className={CSS.RMX_COLOR_SWATCH_ITEM}
style={{backgroundColor: color}}
data-value={color}
onClick={onClick}
>
{isSelected ? <i className="material-icons">check</i> : ""}
</div>
);
}
75 changes: 75 additions & 0 deletions src/ui/controls/DropdownControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/** @license
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

import * as React from "react";
import { CSS } from "../../lib/Constants";
import { StringControlProps } from "./controlProps";

/**
* A dropdown control.
* @class
* @extends React.Component
*/
export class DropdownControl extends React.Component<StringControlProps, void> {

/** Handles the update event for this control. */
onClick = (event: React.FormEvent<HTMLElement>): void => {
this.props.updateVariable(
this.props.variable,
(event.target as HTMLElement).dataset["value"]
);
}

/** @override */
shouldComponentUpdate(nextProps: StringControlProps) {
return nextProps.variable !== this.props.variable;
}

/** @override */
render() {
const {
title,
key,
possibleValues,
selectedValue
} = this.props.variable;
const id = `${CSS.RMX_DROPDOWN}-${key}`;

return (
<div className={`${CSS.RMX_DROPDOWN} ${CSS.MDL_LIST_ITEM}`}>
<span className={CSS.MDL_PRIMARY}>{title}</span>
<span className={CSS.MDL_SECONDARY}>
<button id={id} className="mdl-button mdl-js-button">
<span>
{selectedValue}<i className="material-icons">arrow_drop_down</i>
</span>
</button>
<ul
className="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect"
htmlFor={id}
>
{possibleValues.map((value: string) => (
<li className="mdl-menu__item" key={value}
onClick={this.onClick}
data-value={value}>{value}
</li>
))}
</ul>
</span>
</div>
);
}
}
72 changes: 72 additions & 0 deletions src/ui/controls/RadioListControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/** @license
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

import * as React from "react";
import { CSS, VariableType } from "../../lib/Constants";
import { StringControlProps } from "./controlProps";

/**
* A radio list control.
* @class
* @extends React.Component
*/
export class RadioListControl extends React.Component<StringControlProps, void> {

/** Handles the update event for this control. */
onChange = (event: React.FormEvent<HTMLInputElement>): void => {
this.props.updateVariable(
this.props.variable,
(event.target as HTMLInputElement).value
);
}

/** @override */
shouldComponentUpdate(nextProps: StringControlProps) {
return nextProps.variable !== this.props.variable;
}

/** @override */
render() {
const {
title,
key,
possibleValues,
selectedValue
} = this.props.variable;
const id = `${CSS.RMX_RADIO_LIST_ITEM}-${key}`;

return (
<div className={`${CSS.RMX_RADIO_LIST} ${CSS.MDL_LIST_ITEM}`}>
<span className={CSS.MDL_PRIMARY}>{title}</span>
<span className={CSS.MDL_SECONDARY}>
{possibleValues.map((value: string, i: number) => (
<label className={`${CSS.RMX_RADIO_LIST_ITEM} mdl-radio mdl-js-radio mdl-js-ripple-effect`}
htmlFor={`${id}-${i}`} key={value}
>
<input type="radio" id={`${id}-${i}`}
className="mdl-radio__button"
name="options" value={value}
checked={selectedValue === value}
onChange={this.onChange}
/>
<span className="mdl-radio__label">{value}</span>
</label>
))}
</span>
</div>
);
}
}
Loading