// Parser // ====== // - susy-parse [function] // - susy-normalize [function] // - susy-normalize-columns [function] // - susy-normalize-span [function] // - susy-normalize-spread [function] // - susy-normalize-location [function] // Parse // ----- /// Parse shorthand span expression /// /// @access private /// /// @param {List} $shorthand - /// Shorthand expression to define the width of the span, /// optionally containing: /// - a count, length, or column-list span; /// - `at $n`, `first`, or `last` location on asymmetrical grids; /// - `narrow`, `wide`, or `wider` for optionally spreading /// across adjacent gutters; /// - `of $n ` for available grid columns /// and spread of the container; /// - and `set-gutters $n` to override global gutter settings /// @param {Bool} $context-only [false] - /// Allow the parser to ignore span and span-spread values, /// only parsing context and container-spread /// /// @return {Map} - /// Map of span settings /// (`span`, `location`, `columns`, `gutters`, `spread`, `container-spread`) /// parsed from shorthand input @function susy-parse( $shorthand, $context-only: false ) { $parse-error: 'Unknown shorthand property:'; $options: ( 'first': 'location', 'last': 'location', 'alpha': 'location', 'omega': 'location', 'narrow': 'spread', 'wide': 'spread', 'wider': 'spread', ); $return: (); $span: null; $columns: null; $of: null; $next: false; // Allow context-only shorthand, without span @if ($context-only) and (not index($shorthand, 'of')) { @if su-valid-columns($shorthand, 'fail-silent') { $shorthand: 'of' $shorthand; } @else { $shorthand: join('of', $shorthand); } } // loop through the shorthand list @for $i from 1 through length($shorthand) { $item: nth($shorthand, $i); $type: type-of($item); $details: '[#{$type}] `#{$item}`'; // if we know what's supposed to be coming next… @if $next { // If it's supposed to be a column width… @if ($next == 'column-width') { // if it looks like a column width… @if ($type == 'number') and (not unitless($item)) { // could be either span or context width… @if $of { $columns: join($columns, ('x' $item)); } @else { $span: join($span, ('x' $item)); } } @else { // Column-width error @return _susy-error( '`#{$item}` is not a valid column-width', 'susy-parse'); } } @else { // For other values of next, we simply add to the return map $return: map-merge($return, ($next: $item)); } // Reset next to `false` $next: false; } @else { // If we don't know what's supposed to be coming… // Keywords… @if ($type == 'string') { // Check the map for keywords… @if map-has-key($options, $item) { $setting: map-get($options, $item); // Spread could be on the span or the container… @if ($setting == 'spread') and ($of) { $return: map-merge($return, ('container-spread': $item)); } @else { $return: map-merge($return, ($setting: $item)); } } @else if ($item == 'all') { // `All` is a span shortcut $span: 'all'; } @else if ($item == 'at') { // Some keywords setup what's next… $next: 'location'; } @else if ($item == 'x') { $next: 'column-width'; } @else if ($item == 'set-gutters') { $next: 'gutters'; } @else if ($item == 'of') { $of: true; } @else { // Error if we're out of ideas @return _susy-error('#{$parse-error} #{$details}', 'susy-parse'); } } @else if ($type == 'number') or ($type == 'list') { // Numbers & lists… // We don't have a span, and we're not expecting context… @if not ($span or $of) { $span: $item; } @else if ($of) and (not $columns) { // We are expecting context… $columns: $item; } @else { // Error if we don't understand… @return _susy-error('#{$parse-error} #{$details}', 'susy-parse'); } } @else { // Error if we don't know this type… @return _susy-error('#{$parse-error} #{$details}', 'susy-parse'); } } } // If we have span, merge it in @if $span { $return: map-merge($return, ('span': $span)); } // If we have columns, merge them in @if $columns { $return: map-merge($return, ('columns': $columns)); } // Return the map of settings… @return $return; } // Susy Normalize // -------------- /// Normalize the values in a configuration map /// /// @access private /// /// @param {Map} $config - /// Map of Susy configuration settings to normalize /// /// @return {Map} - /// Map of Susy configuration settings, with all values normalized @function susy-normalize( $config ) { @each $setting in ('columns', 'spread', 'container-spread') { $value: map-get($config, $setting); @if $value { $function: if(($setting == 'container-spread'), 'spread', $setting); $function: 'susy-normalize-#{$function}'; @if function-exists('get-function') { $function: get-function($function); } $value: call($function, $value); } $config: map-merge($config, ($setting: $value)); } $span: map-get($config, 'span'); $location: map-get($config, 'location'); $columns: map-get($config, 'columns'); @if $span { $span: susy-normalize-span($span, $columns); $config: map-merge($config, ('span': $span)); } @if $location { $location: susy-normalize-location($span, $location, $columns); $config: map-merge($config, ('location': $location)); } @return $config; } // Repeat // ------ /// Similar to the `repeat(, )` function /// that is available in CSS Grid templates, /// repeats any value a given number of times — /// a useful shortcut for defining symetrical static grids /// /// @group a-config /// /// @param {Integer} $count - /// The number of repetitions /// @param {Any} $value - /// Any value can be repeated /// /// @return {List} - /// List of repeated values @function susy-repeat( $count, $value: 1 ) { $return: (); @for $i from 1 through $count { $return: join($return, $value); } @return $return; } // Normalize Columns // ----------------- /// Normalize `columns` shorthand for Su /// /// @access private /// /// @param {Number | List} $columns - /// Columns value to normalize /// /// @return {List} - /// List value for `$columns` @function susy-normalize-columns( $columns ) { $return: (); @if (type-of($columns) == 'number') and unitless($columns) { @return susy-repeat($columns); } @else if index($columns, 'x') and length($columns) == 3 { $count: nth($columns, 1); $width: nth($columns, -1); @return susy-repeat($count, $width); } @return $columns; } // Normalize Span // -------------- /// Normalize `span` shorthand for Su /// /// @access private /// /// @param {Number | List | 'all'} $span - /// Span value to normalize /// @param {List} $columns - /// Normalized list of columns in the grid /// /// @return {Number | List} - /// Number or list value for `$span` @function susy-normalize-span( $span, $columns: susy-get('columns') ) { @if (type-of($span) == 'number') { @return $span; } @else if $span == 'all' { @return length($columns); } @return susy-normalize-columns($span); } // Normalize Spread // ---------------- /// Normalize `spread` shorthand for Su /// /// @access private /// /// @param {0 | 1 | -1 | 'narrow' | 'wide' | 'wider'} $spread - /// Spread across adjacent gutters, relative to a column-count — /// either `narrow` (-1), `wide` (0), or `wider` (1) /// /// @return {Number} - /// Numeric value for `$spread` @function susy-normalize-spread( $spread ) { $normal-spread: ( 'narrow': -1, 'wide': 0, 'wider': 1, ); @return map-get($normal-spread, $spread) or $spread; } // Normalize Location // ------------------ /// Normalize `location` shorthand for Su /// /// @access private /// /// @param {Number} $span - /// Number of grid-columns to be spanned /// @param {Integer | 'first' | 'last'} $location - /// Starting (1-indexed) column position of a span, /// or a named location keyword /// @param {List} $columns - /// Normalized list of columns in the grid /// /// @return {Integer} - /// Numeric value for `$location` @function susy-normalize-location( $span, $location, $columns ) { $count: length($columns); $normal-locations: ( 'first': 1, 'alpha': 1, 'last': $count - $span + 1, 'omega': $count - $span + 1, ); @return map-get($normal-locations, $location) or $location; }