mirror of
https://github.com/zhigang1992/sass-layout.git
synced 2026-01-12 16:42:48 +08:00
357 lines
13 KiB
SCSS
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;
|
|
}
|