Last active
June 25, 2019 01:33
-
-
Save samselikoff/7b4b4a96b24dfa3e0606340a88eaa95f to your computer and use it in GitHub Desktop.
Revisions
-
samselikoff revised this gist
Oct 16, 2018 . No changes.There are no files selected for viewing
-
samselikoff revised this gist
Oct 16, 2018 . No changes.There are no files selected for viewing
-
samselikoff created this gist
Jul 28, 2018 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,6 @@ export default class StyleGroup { constructor(styles) { this.styles = styles; this.name = ''; // must set at runtime } } This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,222 @@ import Mixin from '@ember/object/mixin'; import StyleGroup from 'ember-cli-ui-components/lib/style-group'; import { assign } from '@ember/polyfills'; import { assert } from '@ember/debug'; import { computed } from '@ember/object'; /* The computed classes are stored in the `activeClasses` property. By default they are applied to the root element. If you don't want them to, set applyActiveClassesToRoot: false and use them in your template <div class='mr4'> <p class={{activeClasses}}>{{yield}}</p> </div> */ export default Mixin.create({ styles: {}, applyActiveClassesToRoot: true, style: '', init() { this._super(...arguments); this._setStyleGroupNames(); this._validateDefaultStyle(); this._setTagName(); if (this.get('tagName') !== '' && this.get('applyActiveClassesToRoot')) { this.classNameBindings = this.classNameBindings.slice(); this.classNameBindings.push('activeClasses'); } }, activeStyles: computed('style', function() { let activeStyles = (this.get('styles.defaultStyle') || '').split(/\s/); let externalactiveStyles = (this.get('style') || '').split(/\s/); this._validateStyles(externalactiveStyles); externalactiveStyles.forEach(style => { let styleGroup = this._getStyleGroupForStyle(style); if (styleGroup) { let match = this._checkListForStyleFromGroup(activeStyles, styleGroup); if (match) { activeStyles.splice(activeStyles.indexOf(match), 1, style); } else { activeStyles.push(style); } } else { activeStyles.push(style); } }) return activeStyles; }), activeClasses: computed('activeStyles', function() { let baseClasses = this._getBaseClasses(); let styleClasses = this._getStyleClasses(); return baseClasses.concat(styleClasses) .filter(el => !!el) .join(' '); }), // Private _getBaseClasses() { let baseClasses = this.get('styles.base') || ''; return baseClasses.split(/\s/); }, _getStyleClasses() { let styleDefinitions = this._getStyleDefinitions(); return this.get('activeStyles') .map(name => styleDefinitions[name]) .filter(definition => definition !== undefined) .map(definition => { let classes; if (typeof definition === 'string') { classes = definition; } else { classes = definition.style; } return classes; }); }, /* Return flat object of styles (top-level and groups) */ _getStyleDefinitions() { let styles = this.get('styles') || {}; return Object.keys(styles).reduce((allStyles, key) => { let newStyle; if (styles[key] instanceof StyleGroup) { newStyle = styles[key].styles } else { newStyle = { [key]: styles[key] }; } Object.keys(newStyle).forEach(key => { assert(`Styled: You defined two styles named '${key}' on '${this._debugContainerKey}'. Stylenames must be unique across all groups.`, allStyles[key] === undefined); }); return assign({}, allStyles, newStyle); }, {}); }, _getActiveStyleDefinitions() { let definitions = this._getStyleDefinitions(); let activeStyles = this.get('activeStyles'); return Object.keys(definitions) .filter(style => activeStyles.includes(style)) .reduce((hash, style) => { hash[style] = definitions[style]; return hash; }, {}); }, _setStyleGroupNames() { Object.keys(this.get('styles') || []) .forEach(key => { let definition = this.get(`styles.${key}`); if (definition instanceof StyleGroup) { definition.name = key; } }); }, _styleGroups() { return Object.keys(this.get('styles')) .map(key => this.get(`styles.${key}`)) .filter(defn => defn instanceof StyleGroup); }, _validateDefaultStyle() { let defaultStyle = this.get('styles.defaultStyle'); if (defaultStyle) { defaultStyle.split(' ') .filter(Boolean) .forEach(style => { assert( `Styled: You set a default style named '${style}' on '${this._debugContainerKey}', but that style was not defined.`, this._styleExists(style) ); }); } }, _styleExists(style) { let allStyleKeys = Object.keys(this._getStyleDefinitions()); return allStyleKeys.includes(style); }, _validateStyles(activeStyles) { let styleGroups = this._styleGroups(); let styleGroupsUsed = []; activeStyles.filter(Boolean).forEach(style => { // Verify every active style has a definition assert( `Styled: You're using a style named '${style}' on '${this._debugContainerKey}', but that style was not defined.`, this._styleExists(style) ); // Verify multiple styles from the same group are not being used styleGroups.forEach(styleGroup => { let stylesInGroup = Object.keys(styleGroup.styles); if (stylesInGroup.includes(style)) { assert( `Styled: You passed the '${style}' style to a ${this._debugContainerKey} but you've already used a style from the '${styleGroup.name}' oneOf group.`, !styleGroupsUsed.includes(styleGroup.name) ); styleGroupsUsed.push(styleGroup.name); } }); }); }, _getStyleGroupForStyle(style) { return this._styleGroups().find(styleGroup => { return Object.keys(styleGroup.styles).includes(style); }); }, _checkListForStyleFromGroup(list, styleGroup) { let stylesFromGroup = Object.keys(styleGroup.styles); return list.find(style => { return stylesFromGroup.includes(style); }); }, _setTagName() { let activeDefinitions = this._getActiveStyleDefinitions(); let styleDidSetTagName; Object.keys(activeDefinitions) .forEach(style => { let definition = activeDefinitions[style]; if (definition.tagName) { assert(`You're rendering a '${this._debugContainerKey}' with an active style of '${style}' that's setting the tagName, but the '${styleDidSetTagName}' style is already active and also setting the tagName.`, !styleDidSetTagName); styleDidSetTagName = style; this.set('tagName', definition.tagName); } }); } }); This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,74 @@ import Component from '@ember/component'; import { Styled, group } from 'ember-cli-ui-components'; export default Component.extend(Styled, { tagName: 'button', styles: { base: 'leading-tight pointer relative transition', defaultStyle: 'inline-block medium gray dim margins round', colors: group({ gray: 'bg-black-10 text-black-80 font-medium', subtle: 'bg-black-10 text-black-40 font-medium', brand: 'border-none bg-brand-gradient text-white font-medium', warn: 'border-none bg-dark-red text-white font-semibold', white: 'font-normal bg-transparent border-solid border-2 border-white text-white', blue: 'border-none bg-blue text-white', 'white-bg': 'font-normal bg-white text-near-black', }), active: 'bg-light-red text-white', sizes: group({ small: 'text-7 xs:text-6 py-1 xs:py-2 px-2 xs:px-3', medium: 'text-6 xs:text-4 py-2 xs:py-3 px-3 xs:px-4', large: 'text-5 xs:text-4 py-3 px-3 xs:px-4' }), 'nowrap': 'whitespace-no-wrap', floating: 'shadow-l', behavior: group({ dim: 'dim', disabled: 'opacity-50 no-events' }), margins: group({ margins: 'mt-2 mb-3', marginless: '' }), uppercase: 'uppercase', radii: group({ round: 'rounded-2', pill: 'rounded-pill', append: 'rounded-r' }), bold: 'font-bold', full: 'w-full', displays: group({ block: 'block', 'inline-block': 'inline-block', flex: 'flex' }), input: { tagName: 'input' }, link: { style: 'no-underline', tagName: 'a' } } });