Skip to content

Instantly share code, notes, and snippets.

@jamesmfriedman
Last active January 2, 2023 11:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jamesmfriedman/33c750912698f20aa9d4dda9df834e5d to your computer and use it in GitHub Desktop.
Save jamesmfriedman/33c750912698f20aa9d4dda9df834e5d to your computer and use it in GitHub Desktop.
MDC Foundation Proposal
/**
* 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