Last active
January 2, 2023 11:11
-
-
Save jamesmfriedman/33c750912698f20aa9d4dda9df834e5d to your computer and use it in GitHub Desktop.
MDC Foundation Proposal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Current Adapter Example | |
* This is a simple adapter, some are extremely complex... | |
* It is not immediately obvious why some code lives in the adapter and some in the foundation | |
* The Select alone is over 700 lines of component logic... | |
**/ | |
MDCCheckboxFoundation({ | |
addClass: (className) => this.root_.classList.add(className), | |
removeClass: (className) => this.root_.classList.remove(className), | |
setNativeControlAttr: (attr, value) => this.nativeCb_.setAttribute(attr, value), | |
removeNativeControlAttr: (attr) => this.nativeCb_.removeAttribute(attr), | |
getNativeControl: () => this.nativeCb_, | |
isIndeterminate: () => this.indeterminate, | |
isChecked: () => this.checked, | |
hasNativeControl: () => !!this.nativeCb_, | |
setNativeControlDisabled: (disabled) => this.nativeCb_.disabled = disabled, | |
forceLayout: () => this.root_.offsetWidth, | |
isAttachedToDOM: () => Boolean(this.root_.parentNode), | |
}); | |
/** | |
* Proposal, turn the foundation into a state machine! | |
* Instead of having the foundation figure out how to instrument behaviors, | |
* have it be a declaration of what the state of the component should look like. | |
* This includes a mapping of classes, attributes, and event handlers for any given element. | |
* | |
* This should help with just about every third party library integration. | |
* This example is React, but the same applies for Angular, AngularJS, Vue, and Polymer. | |
* | |
* The Vanilla JS components would just have to instrument their own way to apply | |
* classes and attributes, but it would be a small framework that would apply to all components. | |
*/ | |
class extends React.Component { | |
constructor(props) { | |
super(props); | |
// create the foundation. This doesn't do any DOM manipulation, it just creates the source of truth. | |
// do this here so frameworks can render the initial content with the appropriate classes | |
// Removes need for | |
// - addClass | |
// - removeClass | |
// - setNativeControlAttr | |
// - removeNativeControlAttr | |
// - isIndeterminate | |
// - isChecked | |
// - setNativeControlDisabled | |
this.foundation = new CheckboxFoundation(); | |
/** | |
* this.foundation = { | |
* root: { | |
* classes: 'mdc-checkbox', | |
* attributes: {}, | |
* events: {}, | |
* domNode: null | |
* }, | |
* nativeControl: { | |
* classes: 'mdc-checkbox__native-control', | |
* attributes: { | |
* checked: false, | |
* indeterminate: false | |
* disabled: false | |
* }, | |
* events: { | |
* change: function | |
* }, | |
* domNode: null | |
* }, | |
* ... | |
* } | |
*/ | |
} | |
componentDidMount() { | |
// There is rendered code with classes in the DOM | |
// Pass the root element and now internally you can get all of the DOM nodes you need | |
// i.e. getNativeControl | |
// Removes need for | |
// - getNativeControl | |
// - hasNativeControl | |
// - isAttachedToDOM | |
// - forceLayout should be the same in any framework... dont know why it has its own method | |
this.foundation.getDomNodes(this.root); | |
/** | |
* this.foundation = { | |
* root: { | |
* classes: 'mdc-checkbox', | |
* attributes: {}, | |
* events: {}, | |
* domNode: <div .../> | |
* }, | |
* nativeControl: { | |
* classes: 'mdc-checkbox__native-control', | |
* attributes: { | |
* checked: false, | |
* indeterminate: false | |
* disabled: false | |
* }, | |
* events: { | |
* change: function | |
* }, | |
* domNode: <input .../> | |
* }, | |
* ... | |
* } | |
*/ | |
} | |
render() { | |
// whenever we need to, we can update the foundation state | |
// remember that all the foundation is doing is telling us a list of our classes and attributes | |
// so this is safe to call whenever we want in our update cycle. | |
// For react, we would want to call this on every render | |
this.foundation.setState({ checked: true, disabled: false }); | |
return ( | |
<div ref={(el) => (this.root = el)} class={foundation.root.classes} {...foundation.root.attributes}> | |
<input type="checkbox" | |
onChange={foundation.nativeControl.events.change} | |
class={foundation.nativeControl.classes} | |
{...foundation.nativeControl.attributes} | |
/> | |
<div class={foundation.background.classes}> | |
<svg class={foundation.checkmark.classes} viewBox="0 0 24 24"> | |
<path class={foundation.checkmarkPath.classes} fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59" /> | |
</svg> | |
<div class={foundation.mixedMark.classes}></div> | |
</div> | |
</div> | |
) | |
} | |
} | |
/** | |
* Same code, sans comments | |
*/ | |
class extends React.Component { | |
constructor(props) { | |
super(props); | |
this.foundation = new CheckboxFoundation(); | |
} | |
componentDidMount() { | |
this.foundation.getDomNodes(this.root); | |
} | |
render() { | |
this.foundation.setState({ checked: true, disabled: false }); | |
return ( | |
<div ref={(el) => (this.root = el)} class={foundation.root.classes} {...foundation.root.attributes}> | |
<input type="checkbox" | |
onChange={foundation.nativeControl.events.change} | |
class={foundation.nativeControl.classes} | |
{...foundation.nativeControl.attributes} | |
/> | |
<div class={foundation.background.classes}> | |
<svg class={foundation.checkmark.classes} viewBox="0 0 24 24"> | |
<path class={foundation.checkmarkPath.classes} fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59" /> | |
</svg> | |
<div class={foundation.mixedMark.classes}></div> | |
</div> | |
</div> | |
) | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment