Skip to content

Instantly share code, notes, and snippets.

@BoLaMN
Last active December 25, 2015 01:16
Show Gist options
  • Save BoLaMN/5d3543dc522b35f631de to your computer and use it in GitHub Desktop.
Save BoLaMN/5d3543dc522b35f631de to your computer and use it in GitHub Desktop.
do ->
angular.module 'material.core.colors', ['material.core.theming']
.provider '$mdColors', ($mdColorPalette) ->
style = angular.element '<style></style>'
document.head.appendChild style[0]
stylesheet = style[0].sheet
index = 0
DARK_CONTRAST_COLOR = [ 0, 0, 0, 0.87 ]
LIGHT_CONTRAST_COLOR = [ 255, 255, 255, 0.87 ]
STRONG_LIGHT_CONTRAST_COLOR = [ 255, 255, 255 ]
addCustomStyle = (cssname, name, color, contrast = '') ->
if contrast
contrast = "color: #{contrast}"
stylesheet.insertRule ".md-#{cssname}-#{name}.text { #{contrast} !important }", index
stylesheet.insertRule ".md-#{cssname}-#{name}.background { background-color: #{color}; #{contrast} }", index + 1
index += 2
return
clearStyleSheet = ->
while stylesheet.cssRules.length > 0
stylesheet.deleteRule 0
colorNames: []
colorStore: {}
colorSelected: null
themeNames: []
themeStore: {}
getContrastColor: (palette, hueName) ->
{ contrastDefaultColor, contrastLightColors, contrastStrongLightColors, contrastDarkColors } = palette
if angular.isString contrastLightColors
contrastLightColors = contrastLightColors.split ' '
if angular.isString contrastStrongLightColors
contrastStrongLightColors = contrastStrongLightColors.split ' '
if angular.isString contrastDarkColors
contrastDarkColors = contrastDarkColors.split ' '
if contrastDefaultColor is 'light'
if contrastDarkColors?.indexOf(hueName) > -1
DARK_CONTRAST_COLOR
else
if contrastStrongLightColors?.indexOf(hueName) > -1
STRONG_LIGHT_CONTRAST_COLOR
else
LIGHT_CONTRAST_COLOR
else
if contrastLightColors?.indexOf(hueName) > -1
if contrastStrongLightColors?.indexOf(hueName) > -1
STRONG_LIGHT_CONTRAST_COLOR
else
LIGHT_CONTRAST_COLOR
else
DARK_CONTRAST_COLOR
storeAndLoadPalettes: (colors, themes, primaryPalette) ->
@colorStore = colors
@themeStore = themes
@colorNames = Object.keys colors
@themeNames = Object.keys themes
@loadPalette primaryPalette
return
loadPalette: (newPalette) ->
if @colorSelected
clearStyleSheet()
@colorSelected = newPalette
for name, color of @colorStore[newPalette]
addCustomStyle 'fg', name, color.value, color.contrast
addCustomStyle 'bg', name, color.value, color.contrast
for themeName, theme of @themeStore
cleanedThemeName = if themeName is 'default' then '' else themeName + '-'
for groupName, group of theme
for name, color of group
addCustomStyle cleanedThemeName + groupName, name, color.value, color.contrast
return
$get: ->
colorNames: @colorNames
colorStore: @colorStore
colorSelected: @colorSelected
themeNames: @themeNames
themeStore: @themeStore
loadPalette: @loadPalette
.config ($mdThemingProvider, $mdColorsProvider) ->
colorStore = {}
parsePalette = (paletteName, palette) ->
paletteContrast = palette
hueColors = $mdThemingProvider._THEMES['default'].colors['primary'].hues
colors = {}
addHue = (hueName) ->
contrastColor = $mdThemingProvider._rgba $mdColorsProvider.getContrastColor(palette, hueColors[hueName])
colors[hueName] = value: palette[hueColors[hueName]], contrast: contrastColor
copyColors = (colorName) ->
if /#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})\b/.test(palette[colorName])
contrastColor = $mdThemingProvider._rgba $mdColorsProvider.getContrastColor(palette, colorName)
colors[colorName] = value: palette[colorName], contrast: contrastColor
return
colorStore[paletteName] = colors
Object.keys(palette).forEach copyColors
Object.keys(hueColors).forEach addHue
return
for paletteName, palette of $mdThemingProvider._PALETTES
parsePalette paletteName, palette
themeStore = {}
parseTheme = (themeName) ->
themeColorGroups = $mdThemingProvider._THEMES[themeName].colors
colors = {}
defineColors = (themeGroup) ->
themeStore[themeName][themeGroup] ?= {}
definedColors = colorStore[themeColorGroups[themeGroup].name]
for item, value of themeColorGroups[themeGroup].hues
themeStore[themeName][themeGroup][item] = definedColors[value]
return
themeStore[themeName] ?= {}
Object.keys(themeColorGroups).forEach defineColors
return
Object.keys($mdThemingProvider._THEMES).forEach parseTheme
primaryPalette = $mdThemingProvider._THEMES['default'].colors.primary.name
$mdColorsProvider.storeAndLoadPalettes colorStore, themeStore, primaryPalette
return
.directive 'mdStyle', ($mdColors, $parse) ->
restrict: 'A'
link: (scope, element, attrs) ->
{ colorSelected, colorStore, colorNames, themeStore, themeNames } = $mdColors
parsedStyles = $parse attrs.mdStyle
styles = parsedStyles()
for cssName, cssValue of styles
[color, hue, hue2] = cssValue.split '.'
if color in ['primary', 'accent', 'background', 'foreground', 'warn']
color = themeStore['default'][color]
else if color not in colorNames
color = colorSelected
if themeStore[color]
color = themeStore[color]
if hue2
color = color[hue][hue2]
else
color = color[hue]['default']
color = colorStore[color] or color
colorObject = color[hue] or color.default
if colorObject
if cssName is 'background-color'
element.css 'color', colorObject.contrast
if angular.isString attrs.mdContrast
element.css cssName, colorObject.contrast
else
element.css cssName, colorObject.value
@BoLaMN
Copy link
Author

BoLaMN commented Sep 12, 2015

<!-- Red Background, White Text -->
<md-button class="md-warn-default background">My Button</md-button>
<!-- Red Background With hue-2, White Text -->
<md-button class="md-warn-hue-2 background">My Button</md-button>
<!-- Pink Text -->
<md-button class="md-accent-default text">My Button</md-button>
<!-- Pink Background, White Text -->
<md-button class="md-accent-default background">My Button</md-button>

<!-- md-style type object -->
<md-button md-style="{'background-color': 'hue-1'}">My Button</md-button>
<md-button md-style="{'background-color': '100'}">My Button</md-button>
<md-button md-style="{'background-color': 'warn.hue-3'}">My Button</md-button>
<md-button md-style="{'background-color': 'accent.400'}">My Button</md-button>
<md-button md-style="{'background-color': 'green.900'}">My Button</md-button>

@bathos
Copy link

bathos commented Oct 12, 2015

This looks really interesting and I've been trying to understand/adapt it, but I'm wondering about some stuff:

  • Does the getContrastColor function work correctly? It looks for hueName in each color set, yet hueName does not appear to be a variable in scope.
  • What is the point of colorToRgbaArray? It looks like it only gets used with the constants, which could just as easily be arrays in the first place, and it has all that handling for formats that never get used.
  • copyColors takes two arguments, but the second one does not seem to be used.

@bathos
Copy link

bathos commented Oct 12, 2015

So I now understand that the parts I was confused by came from material's own code, hence some of it not being applicable here. Still, it was very instructive. Here's what I came up based on this if you're curuous (just for generating classes, not the directive; and it's es6+):

module.run(($mdColorPalette, $mdTheming) => {
    const { THEMES } = $mdTheming;

    const rules = [];

    for (const themeName in THEMES) {
      const { colors } = THEMES[themeName];

      const themeCls = themeName == 'default' ? '' : `.md-${ themeName }-theme`;

      for (const intention in colors) {
        const intentCls = `.md-${ intention }`;

        const {
          name: color,
          hues: {
            'default': h0,
            'hue-1': h1,
            'hue-2': h2,
            'hue-3': h3
          }
        } = colors[intention];

        const palette = $mdColorPalette[color];

        [ h0, h1, h2, h3 ]
          .map(hue => {
            const { contrast, value } = palette[hue];

            return [ value, contrast ].map(([ ...rgb ]) =>
              `rgb${ rgb.length == 4 ? 'a' : '' }(${ rgb.join(',') })`
            );
          })
          .forEach(([ color, contrast ], i) => {
            const hueCls = i ? `.md-hue-${ i }` : '';

            const selector = [ themeCls, intentCls, hueCls ]
              .filter(n => n)
              .join('');

            const textRule = `${ selector }.text{color:${ color }}`;

            const bgRule = selector +
              `.background{background-color:${ color };color:${ contrast }}`;

            rules.push(textRule, bgRule);
          });
      }
    }

    const style = document.createElement('style');

    document.head.appendChild(style);

    const { sheet } = style;

    rules.forEach(::sheet.insertRule);
  });

@BoLaMN
Copy link
Author

BoLaMN commented Oct 12, 2015

Hey, sorry i did some extra work on this but forgot to update. fixes all the things you pointed out, it now checks the contrast colour correctly and nice pickup with the colorToRgbaArray ive ripped that out and replaced the them with arrays to reduce the code.

I added checking for "md-contrast" in the md-style directive eg <md-button md-contrast class="md-accent-default background">My Button</md-button>

Cheers

@BoLaMN
Copy link
Author

BoLaMN commented Oct 12, 2015

i should also point out that this md-style directive actually uses $parse instead of isolated or child scopes avoiding the multiple directives scope error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment