Files
sass-layout/sass-layout.scss
2015-02-04 17:08:11 +08:00

357 lines
13 KiB
SCSS

$global-frame-map: () !global;
@import "_util";
@function empty-layout() {
$empty-layout: (
width: 0,
height: 0,
top: 0,
left: 0,
flex-total: 0,
flex: 0,
non-flex-pixels: 0,
align-items: 'stretch',
child-nodes: ()
);
$empty-layout: layout-property("margin", $empty-layout);
$empty-layout: layout-property("padding", $empty-layout);
$empty-layout: layout-property("used", $empty-layout);
@return $empty-layout;
}
@mixin layout($layout) {
$current-node: #{&} !default;
$layout: map-merge($layout,(
selector: $current-node,
parent-selector: parent-selector($current-node),
start-direction: if(map-get($layout, flex-direction) == 'row', left, top),
cross-start-direction: if(map-get($layout, flex-direction) == 'row', top, left),
));
$layout: layout-property("margin", $layout);
$layout: layout-property("padding", $layout);
$layout: layout-property("used", $layout);
@each $key, $value in $layout {
@if is-percentage($value) {
@if str-index($key, left) != null or str-index($key, right) != null or str-index($key, width) != null {
$layout: map-set($layout, $key, map-get(parent($layout), width) * $value / 100%);
}@else if str-index($key, top) != null or str-index($key, bottom) != null or str-index($key, height) != null {
$layout: map-set($layout, $key, map-get(parent($layout), height) * $value / 100%);
}
}
}
@if map-get($layout, top) != null {
$layout: map-set($layout, top, map-get($layout, top) + map-get($layout, margin-top));
}
@if map-get($layout, left) != null {
$layout: map-set($layout, left, map-get($layout, left) + map-get($layout, margin-left));
}
$layout: map-merge(empty-layout(), $layout);
$layout: map-merge((
child-nodes: ()
), $layout);
$layout: map-merge((
align-self: if(has-parent($layout), map-get(parent($layout), align-items), 'stretch')
), $layout);
@include save-layout($layout);
@include register-with-parent($layout);
}
@mixin register-with-parent($layout) {
$parent-node: parent($layout);
@if $parent-node != null {
$child-nodes: append(map-get($parent-node, child-nodes), map-get($layout, selector));
$flexs: map-get($parent-node, flex-total) + map-get($layout, flex);
$start-direction: map-get($parent-node, start-direction);
$axis: if(is-vertical($start-direction), height, width);
$non-flex-pixels: if(map-get($layout, flex) == 0 and map-get($layout, position) != absolute, spent-space-on-direction($layout, $start-direction), 0);
$parent-node: map-merge($parent-node, (
child-nodes: $child-nodes,
flex-total: $flexs,
non-flex-pixels: map-get($parent-node, non-flex-pixels) + $non-flex-pixels,
));
@include save-layout($parent-node);
}
}
@mixin render-layout() {
@each $selector, $node in root-nodes() {
@include render-one-layout($node);
}
@each $selector, $node in non-root-nodes() {
@if $selector != null and str-length($selector) > 0 {
#{$selector} {
top: map-get($node, top);
left: map-get($node, left);
width: map-get($node, width);
height: map-get($node, height);
@each $attribute, $value in $node {
@if str-slice($attribute, 1, 1) == '_' {
#{str-slice($attribute, 2, str-length($attribute))}: render-value($node, $value);
}
}
}
}
}
}
@function render-value($node, $value) {
$result: nth($value, 1);
@if map-has-key($node, $result) {
$result: map-get($node, $result);
} @else if $result == max-height-weight {
$result: max(map-get($node, height), map-get($node, width));
}
@if length($value) > 1 {
$result: $result * nth($value, 2);
}
@if length($value) > 2 {
$result: $result + nth($value, 3);
}
@return $result;
}
@function has-parent($node) {
@return map-get($node, parent-selector) != "";
}
@function parent($node) {
@return get-layout(map-get($node, parent-selector));
}
@mixin render-one-layout($node) {
@if map-get($node, bottom) != null {
$parent-height: if(has-parent($node), map-get(parent($node), height), 0);
@if map-get($node, top) > 0 {
$node: map-set($node, height, $parent-height - map-get($node, top) - map-get($node, bottom));
} @else {
$node: map-set($node, top, $parent-height - map-get($node, height) - map-get($node, bottom));
}
}
@if map-get($node, right) != null {
$parent-width: if(has-parent($node), map-get(parent($node), width), 0);
@if map-get($node, left) > 0 {
$node: map-set($node, width, $parent-width - map-get($node, left) - map-get($node, right));
} @else {
$node: map-set($node, left, $parent-width - map-get($node, width) - map-get($node, right));
}
}
$node: calculate-params-on-direction($node, top);
$node: calculate-params-on-direction($node, left);
@if map-get($node, height) == 0 {
$node: map-merge($node, (
height: map-get($node, padding-top) + map-get($node, padding-bottom),
altered-height: true
));
}
@if map-get($node, width) == 0 {
$node: map-merge($node, (
width: map-get($node, padding-left) + map-get($node, padding-right),
altered-width: true
));
}
@include save-layout($node);
$child-nodes: prepare-child-nodes-for-justify-content($node);
@each $child_node in $child-nodes {
@include render-one-layout($child_node);
}
$node: get-layout(map-get($node, selector));
@include adjust-parent-if-needed($node);
$node: map-merge($node, (
height: max(map-get($node, height), map-get($node, padding-top) + map-get($node, padding-bottom)),
width: max(map-get($node, width), map-get($node, padding-left) + map-get($node, padding-right)),
));
$node: map-merge($node, (
right: map-get($node, left) + map-get($node, width),
bottom: map-get($node, top) + map-get($node, height)
));
@include save-layout($node);
}
@function prepare-child-nodes-for-justify-content($node) {
@if length(map-get($node, child-nodes)) > 0 {
$justify-content: map-get($node, justify-content);
$fake-node: map-merge(empty-layout(), (
parent-selector: map-get($node, selector),
flex: 1
));
$flex-point-to-add: 0;
$child-nodes: ();
@each $node-selector in map-get($node, child-nodes) {
$child-nodes: append($child-nodes, get-layout($node-selector));
}
@if ($justify-content == flex-start) {
$flex-point-to-add: 1;
$child-nodes: append($child-nodes, $fake-node);
} @else if($justify-content == flex-end) {
$flex-point-to-add: 1;
$new-child-nodes: ();
$child-nodes: join(($fake-node,), $child-nodes);
} @else if($justify-content == space-between) {
$flex-point-to-add: length($child-nodes) - 1;
$child-nodes: join-list-with-item($child-nodes, $fake-node);
} @else if($justify-content == space-around) {
$flex-point-to-add: length($child-nodes);
$fake-half-node: map-merge(empty-layout(), (
parent-selector: map-get($node, selector),
flex: 0.5
));
$child-nodes: join-list-with-item($child-nodes, $fake-node);
$child-nodes: join(($fake-half-node,), $child-nodes);
$child-nodes: append($child-nodes, $fake-half-node);
} @else if($justify-content == center) {
$flex-point-to-add: 2;
$child-nodes: join(($fake-node,), $child-nodes);
$child-nodes: append($child-nodes, $fake-node);
}
$node: map-merge($node, (
flex-total: map-get($node, flex-total) + $flex-point-to-add
));
$node: save-layout($node);
@return $child-nodes;
}
@return ();
}
@function join-list-with-item($list, $item) {
$new-list: ();
@if length($list) > 1 {
@for $i from 1 through length($list) - 1 {
$new-list: join($new-list, (nth($list, $i), $item));
}
}
@return append($new-list, nth($list, length($list)));
}
@function calculate-params-on-direction($node, $direction) {
$used-property: if(has-parent($node), map-get(parent($node), used-#{$direction}), 0);
$parent-node: parent($node);
$direction-value: $used-property + map-get($node, margin-#{$direction});
@if has-parent($node) {
$direction-value: $direction-value + map-get($parent-node, padding-#{$direction});
}
@if map-get($node, $direction) == 0 {
$node: map-merge($node, ($direction: $direction-value));
}
@if has-parent($node) and $direction == map-get($parent-node, start-direction) {
@if map-get($node, flex) > 0 {
$flexable-space: if(
is-vertical($direction),
map-get($parent-node, height) - map-get($parent-node, padding-top) - map-get($parent-node, padding-bottom),
map-get($parent-node, width) - map-get($parent-node, padding-left) - map-get($parent-node, padding-right)
);
$flexable-space: $flexable-space - map-get($parent-node, non-flex-pixels);
$flex-total: map-get($parent-node, flex-total);
$flexable-space: $flexable-space - if(is-vertical($direction), map-get($node, margin-top) + map-get($node, margin-bottom), map-get($node, margin-left) + map-get($node, margin-right));
$node: map-set($node, if(is-vertical($direction), height, width), $flexable-space * map-get($node, flex) / $flex-total);
}
@if map-get($node, position) != absolute {
$used-property: $used-property + spent-space-on-direction($node, $direction);
$parent-node: map-set($parent-node, used-#{$direction}, $used-property);
$parent-node: save-layout($parent-node);
}
}
@if has-parent($node) and $direction == map-get($parent-node, cross-start-direction) {
$align-self: map-get($node, align-self);
$new-position: map-get($node, margin-#{$direction}) + map-get($parent-node, padding-#{$direction});
$axis: if(is-vertical($direction), height, width);
$parent-size: map-get($parent-node, $axis) - map-get($parent-node, padding-#{$direction}) - map-get($parent-node, padding-#{oppsite-direction($direction)});
@if $align-self == flex-end {
$new-position: max($new-position, $parent-size - spent-space-on-direction($node, $direction));
} @else if $align-self == center {
$new-position: max($new-position, ($parent-size - spent-space-on-direction($node, $direction)) / 2);
} @else if $align-self == stretch {
@if map-get($node, $axis) == 0 {
$axis-value: $parent-size - map-get($node, margin-#{$direction}) - map-get($node, margin-#{oppsite-direction($direction)});
$node: map-set($node, $axis, max(0, $axis-value));
}
}
@if map-get($node, $direction) == 0 {
$node: map-set($node, $direction, $new-position);
}
}
@return $node;
}
@function oppsite-direction($direction) {
$map: (
top: bottom,
left: right,
bottom: top,
right: left
);
@return map-get($map, $direction);
}
@function spent-space-on-direction($node, $direction) {
@if is-vertical($direction) {
@return map-get($node, height) + map-get($node, margin-top) + map-get($node, margin-bottom);
} @else {
@return map-get($node, width) + map-get($node, margin-left) + map-get($node, margin-right);
}
}
@function is-vertical($direction) {
@return $direction == 'top' or $direction == 'bottom';
}
@mixin adjust-parent-if-needed($node) {
@if has-parent($node) and map-get($node, position) != absolute {
$parent: parent($node);
@if map-get($parent, width) == 0 or map-get($parent, altered-width) == true {
$width: map-get($node, left) + map-get($node, width) + map-get($node, margin-right) + map-get($parent, padding-right);
$width: max(map-get($parent, width), $width);
$parent: map-set($parent, width, $width);
$parent: map-set($parent, altered-width, true);
}
@if map-get($parent, height) == 0 or map-get($parent, altered-height) == true {
$height: map-get($node, top) + map-get($node, height) + map-get($node, margin-bottom) + map-get($parent, padding-bottom);
$height: max(map-get($parent, height), $height);
$parent: map-set($parent, height, $height);
$parent: map-set($parent, altered-height , true);
}
@include save-layout($parent);
}
}
@mixin render-child-layout($node) {
@if length(map-get($node, child-nodes)) > 0 {
@each $child_selector in map-get($node, child-nodes) {
$child_node: get-layout($child_selector);
$child_node: map-merge($child_node, (top: $parent-top, left: 0));
$parent-top: $parent-top + map-get($child_node, height);
@include save-layout($child_node);
@include render-child-layout($child_node);
}
}
}
@function root-nodes() {
$root-nodes: ();
@each $selector, $node in $global-frame-map {
@if map-get($node, parent-selector) == "" {
$root-nodes: map-set($root-nodes, $selector, $node);
}
}
@return $root-nodes;
}
@function non-root-nodes() {
$non-root-nodes: ();
@each $selector, $node in $global-frame-map {
@if map-get($node, parent-selector) != "" {
$non-root-nodes: map-set($non-root-nodes, $selector, $node);
}
}
@return $non-root-nodes;
}