-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
React example complexity #393
Comments
The problem you probably see is that the the somewhat complex components for Material Components Web implement a "Foundation" prototype, which essentially defines functions for binding events, adding and removing classes, updating styles, etc. This has to be done to create components that can be used in any framework. The react example you see, the reason it's so extensive is because it implements the Foundation prototype to work with React. For example, you see that it implements the "addClass" function?
Managing the state is sort of a "react" thing. In other frameworks, you might be setting a variable. In vanilla JS, you'd just be appending the class to You'll often see that by making something "extensible", you run into a situation where you expose a lot of functionality, and it becomes overwhelming. I see it with a lot of javascript libraries. To answer your second question, you really only implement these "mdc" components once. Afterwards, your components will start to look like normal react components. So no, it doesn't increase in complexity. It's more of an initial spike, and then a downward slope. |
I guess really, what this is crying out for is someone to take the initiative and translate these into react components and open-source them. 🙄 |
@kminehart Thanks, that's an excellent explanation! I understand better now. These are the guts of the elements that would typically be wrapped up in an npm library. |
@burkhardr honestly it wouldn't take a whole lot of effort. In one of my projects for my employer I have most of the components finished in React. When I've finished them all I'll clean them up and publish them. My only worry is that as |
@kminehart Keep us in the loop if your employer lets you open-source. If you're worried about keeping up with the underlying library, I'm sure the core maintainers and the wider community will be happy to help. |
It doesn't look like it's going to happen as we're switching focus from MDC for the time being. I'll provide some snippets though, it's honestly very similar for other mdc components. The hardest one for me was I never got around to implementing the focus, nor do I quite have the time to do that either. :( import React, {PropTypes} from 'react';
import {MDCSimpleMenu, MDCSimpleMenuFoundation} from '@material/menu';
import MenuItem from '../MenuItem';
import {Set as ImmutableSet, Map as ImmutableMap} from 'immutable';
import {getTransformPropertyName} from '@material/menu/util';
const OPENFROMS = {
"top-left": "mdc-simple-menu--open-from-top-left",
"top-right": "mdc-simple-menu--open-from-top-right",
"bottom-left": "mdc-simple-menu--open-from-bottom-left",
"bottom-right": "mdc-simple-menu--open-from-bottom-right",
}
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: new ImmutableSet(),
};
this.foundation = new MDCSimpleMenuFoundation({
addClass: (className) => {
this.setState(prevState => ({
classes: prevState.classes.add(className),
}));
},
removeClass: (className) => {
this.setState(prevState => ({
classes: prevState.classes.remove(className),
}));
},
hasClass: (className) => {
if(!this.refs.menu) return;
return this.refs.menu.classList.contains(className);
},
hasNecessaryDom: () => {
return !!this.refs.menu;
},
getInnerDimensions: () => {
return {
width: this.refs.menuItemsContainer.offsetWidth,
height: this.refs.menuItemsContainer.offsetHeight,
};
},
hasAnchor: () => {
return this.refs.menu.parentElement && this.refs.menu.parentElement.classList.contains('mdc-menu-anchor');
},
getAnchorDimensions: () => {
return this.refs.menu.parentElement.getBoundingClientRect()
},
getWindowDimensions: () => {
return {width: window.innerWidth, height: window.innerHeight};
},
setScale: (x, y) => {
if(!this.refs.menu) return;
this.refs.menu.style[getTransformPropertyName(window)] = `scale(${x}, ${y})`;
},
setInnerScale: (x, y) => {
if(!this.refs.menuItemsContainer) return;
this.refs.menuItemsContainer.style[getTransformPropertyName(window)] = `scale(${x}, ${y})`;
},
getNumberOfItems: () => {
if(!this.props.items) return;
return this.props.items.length;
},
registerInteractionHandler: (type, handler) => {
if(!this.refs.menu) return;
this.refs.menu.addEventListener(type, handler);
},
deregisterInteractionHandler: (type, handler) => {
if(!this.refs.menu) return;
this.refs.menu.removeEventListener(type, handler);
},
registerDocumentClickHandler: (handler) => {
document.addEventListener('click', handler);
},
deregisterDocumentClickHandler: (handler) => {
document.removeEventListener('click', handler);
},
setTransitionDelayForItemAtIndex: (index, value) => {
return;
},
getIndexForEventTarget: (target) => {
if(!this.props.items) return;
return this.props.items.indexOf(target);
},
notifySelected: (evtData) => {
let evt = new CustomEvent('MDCSimpleMenu:selected', {
detail: {
index: evtData.index,
item: this.items[evtData.index],
},
});
this.refs.menu.dispatchEvent(evt);
},
notifyCancel: () => {
let evt = new CustomEvent('MDCSimpleMenu:cancel');
this.refs.menu.dispatchEvent(evt);
this.props.onClose();
},
saveFocus: () => {
},
restoreFocus: () => {
},
isFocused: () => {
},
focus: () => {
},
getFocusedItemIndex: () => {
},
focusItemAtIndex: (index) => {
},
isRtl: () => {
},
setTransformOrigin: (origin) => {
this.refs.menu.style[`${getTransformPropertyName(window)}-origin`] = origin;
},
setPosition: (position) => {
if(!this.refs.menu) return;
this.refs.menu.style.left = 'left' in position ? position.left : null;
this.refs.menu.style.right = 'right' in position ? position.right : null;
this.refs.menu.style.top = 'top' in position ? position.top : null;
this.refs.menu.style.bottom = 'bottom' in position ? position.bottom : null;
},
getAccurateTime: () => {
return window.performance.now();
},
});
}
componentDidUpdate() {
if(this.props.open && !this.foundation.isOpen()) this.foundation.open();
}
componentDidMount() {
this.foundation.init();
}
componentWillUnmount() {
this.foundation.destroy();
}
render() {
return(
<div className={`mdc-simple-menu ${OPENFROMS[this.props.openFrom]} ${this.props.className || ''} ${this.state.classes.toJS().join(' ')}`}
ref="menu">
<ul
className='mdc-simple-menu__items mdc-list'
role='menu'
ref='menuItemsContainer'>
{this.props.items.map((item, key) =>
<MenuItem key={key} label={item.label} icon={item.icon} onClick={item.onClick} />
)}
</ul>
</div>
)
}
}
Menu.defaultProps = {
openFrom: "top-right",
items: [],
onClose: () => {},
open: false,
};
Menu.propTypes = {
openFrom: PropTypes.oneOf([
"top-right",
"top-left",
"bottom-right",
"bottom-left",
]),
items: PropTypes.array.isRequired,
onClose: PropTypes.func,
open: PropTypes.bool.isRequired,
};
export default Menu; MenuItem import React, {PropTypes} from 'react';
import {ripple} from 'material-components-web';
class MenuItem extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
}
render() {
return(
<li className={`mdc-list-item ${this.props.className || ''}`}
role='menuitem'
onClick={this.props.onClick}>
{
this.props.icon
?
<i className="mdc-list-item__start-detail material-icons" aria-hidden="true">{this.props.icon}</i>
:
null
}
{this.props.label}
</li>
)
}
}
MenuItem.defaultProps = {
onClick: () => {},
};
MenuItem.propTypes = {
label: PropTypes.string.isRequired,
icon: PropTypes.string,
};
export default MenuItem; Obviously you're free to use, distribute, and modify code however you want; it comes without warranty. |
Found this for anyone still looking for react wrappers around these components. |
Hi, right now I am working in a wrapper https://github.com/carlitux/material-toolbox if you can take a look would be great. I hope soon I will publish to npm. |
This is a feature request for the react examples I guess. I'm learning React and I've just started to try and implement MDC-Web.
I'm using MDC-Web v0.6.0 with React v15.4.1 (Ubuntu 16.04 Vagrant box running on Fedora 25 + Firefox)
I've installed and run the react example. It works perfectly straight out of the box, which is awesome. Plus it gives a fully working state handling model using PureComponents and Immutable as part of the example which is also great.
What concerns me is that Checkbox.js is ~200 lines long (excluding the licence comment).
If it is 200 lines to implement a checkbox, how much coding does it take to implement a form with error handling? Or beyond that an application.
Perhaps I'm just penalising the example because it gives a good level of detail of handling state, which most other examples don't do.
So my questions would be:
The text was updated successfully, but these errors were encountered: