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
@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