require 'archetype/functions/styleguide_memoizer' require 'thread' %w(components constants grammar helpers resolve styles themes).each do |dep| require "archetype/sass_extensions/functions/styleguide/#{dep}" end # # This is the magic of Archetype. This module provides the interfaces for constructing, # extending, and retrieving reusable UI components # module Archetype::SassExtensions::Styleguide # # interface for adding new components to the styleguide structure # # *Parameters*: # - $id {String} the component identifier # - $data {Map|List} the component data object # - $default {Map|List} the default data object (for extending) # - $theme {String} the theme to insert the component into # - $force {Boolean} if true, forcibly insert the component # *Returns*: # - {Boolean} whether or not the component was added # def styleguide_add_component(id, data, default = nil, theme = nil, force = false) @@archetype_styleguide_mutex.synchronize do theme = get_theme(theme) components = theme[:components] id = helpers.to_str(id) # if force was true, we have to invalidate the memoizer memoizer.clear(theme[:name]) if force # if we already have the component, don't create it again return Sass::Script::Bool.new(false) if component_exists(id, theme, nil, force) # otherwise add it components[id] = helpers.data_to_hash(default, 1, SPECIAL, ADDITIVES).merge(helpers.data_to_hash(data, 1, SPECIAL, ADDITIVES)) return Sass::Script::Bool.new(true) end end Sass::Script::Functions.declare :styleguide_add_component, [:id, :data] Sass::Script::Functions.declare :styleguide_add_component, [:id, :data, :default] Sass::Script::Functions.declare :styleguide_add_component, [:id, :data, :default, :theme] # # interface for extending an existing components in the styleguide structure # # *Parameters*: # - $id {String} the component identifier # - $data {List} the component data object # - $theme {String} the theme to insert the component into # - $extension {String} the name of the extension # - $force {Boolean} if true, forcibly extend the component # *Returns*: # - {Boolean} whether or not the component was extended # def styleguide_extend_component(id, data, theme = nil, extension = nil, force = false) @@archetype_styleguide_mutex.synchronize do theme = get_theme(theme) components = theme[:components] id = helpers.to_str(id) # if force was set, we'll create a random token for the name extension = rand(36**8).to_s(36) if force # convert the extension into a hash (if we don't have an extension, compose one out of its data) extension = helpers.to_str(extension || data).hash extensions = theme[:extensions] return Sass::Script::Bool.new(false) if component_exists(id, theme, extension, force) extensions.push(extension) components[id] = (components[id] ||= Archetype::Hash.new).rmerge(helpers.data_to_hash(data, 1, SPECIAL, ADDITIVES)) return Sass::Script::Bool.new(true) end end Sass::Script::Functions.declare :styleguide_extend_component, [:id, :data] Sass::Script::Functions.declare :styleguide_extend_component, [:id, :data, :theme] Sass::Script::Functions.declare :styleguide_extend_component, [:id, :data, :theme, :extension] # # check whether or not a component (or a component extension) has already been defined # # *Parameters*: # - $id {String} the component identifier # - $data {List} the component data object # - $theme {String} the theme to insert the component into # - $extension {String} the name of the extension # - $force {Boolean} if true, forcibly extend the component # *Returns*: # - {Boolean} whether or not the component/extension exists # def styleguide_component_exists(id, theme = nil, extension = nil, force = false) @@archetype_styleguide_mutex.synchronize do extension = helpers.to_str(extension).hash if not extension.nil? return Sass::Script::Bool.new( component_exists(id, theme, extension, force) ) end end Sass::Script::Functions.declare :styleguide_extend_component, [:id] Sass::Script::Functions.declare :styleguide_extend_component, [:id, :theme] Sass::Script::Functions.declare :styleguide_extend_component, [:id, :theme, :extension] Sass::Script::Functions.declare :styleguide_extend_component, [:id, :theme, :extension, :force] # # given a description of the component, convert it into CSS # # *Parameters*: # - $description {String|List|Map} the description of the component # - $theme {String} the theme to use # *Returns*: # - {Map} a map of styles # def _styleguide(description, state = nil, theme = nil) @@archetype_styleguide_mutex.synchronize do styles = get_styles(description, theme, state) styles = resolve_runtime_locale_values(styles) # convert it back to a Sass:Map and carry on return helpers.hash_to_map(styles) end end # # returns the CSS differences between components # # *Parameters*: # - $original {String|List|Map} the description or map representation of the original component # - $other {String|List|Map} the description or map representation of the new component # *Returns*: # - {List} a key-value paired list of styles # def styleguide_diff(original, other) @@archetype_styleguide_mutex.synchronize do # normalize our input (for back-compat) original = normalize_styleguide_definition(original) other = normalize_styleguide_definition(other) # compute the difference diff = original.diff(other) # convert the individual messages in a comparison original_message = helpers.get_meta_message(original).sub(MESSAGE_PREFIX, '').sub(MESSAGE_SUFFIX, '') other_message = helpers.get_meta_message(other).sub(MESSAGE_PREFIX, '').sub(MESSAGE_SUFFIX, '') diff = helpers.add_meta_message(diff, "#{MESSAGE_PREFIX}#{original_message}` vs `#{other_message}#{MESSAGE_SUFFIX}") # and return it as a map return helpers.hash_to_map(diff) end end # # given a styleguide definition or object, extract specified styles # # *Parameters*: # - $definition {String|List} the description of the component # - $properties {String|List} the properties to extract the derived styles for # - $format {String} the format to return the results in [auto|map|list] # - $strict {Boolean} if true, will only return an exact match, and not try to extrapolate the value # *Returns*: # - {List|Map|*} either a list/map of the values or the individual value itself # def styleguide_derived_style(definition, properties = [], format = 'auto', strict = false) @@archetype_styleguide_mutex.synchronize do # normalize our input definition = normalize_styleguide_definition(definition) # get the computed styles return derived_style(definition, properties, format, strict) end end end