├── .bowerrc ├── .gitignore ├── bower.json ├── package.json ├── dist ├── angular-layout.min.js ├── angular-layout.js ├── angular-layout-debug.min.css ├── angular-layout-debug.css ├── angular-layout.min.css └── angular-layout.css ├── src ├── js │ └── angular-layout.js └── less │ ├── angular-layout-debug.less │ └── angular-layout.less ├── Gruntfile.js ├── demos └── fixed-width-page.html └── README.md /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "vendor", 3 | "json": "bower.json" 4 | } 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | .DS_Store 3 | /.sass-cache/ 4 | /node_modules/ 5 | /vendor/ 6 | /build/ -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-layout", 3 | "version": "0.1.1", 4 | "main": [ 5 | "dist/angular-layout.css", 6 | "dist/angular-layout.js" 7 | ], 8 | "devDependencies": {}, 9 | "dependencies": {} 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | { 4 | "name": "Gabriele Genta" 5 | }, 6 | { 7 | "name": "Martin Mouterde" 8 | } 9 | ], 10 | "name": "angular-layout", 11 | "version": "0.1.1", 12 | "homepage": "https://github.com/demerzel3/angular-layout", 13 | "licenses": { 14 | "type": "MIT" 15 | }, 16 | "bugs": "https://github.com/demerzel3/angular-layout/issues", 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:demerzel3/angular-layout.git" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "grunt": "~0.4.1", 24 | "grunt-autoprefixer": "^1.0.1", 25 | "grunt-banner": "^0.2.3", 26 | "grunt-contrib-clean": "~0.4.1", 27 | "grunt-contrib-copy": "~0.4.1", 28 | "grunt-contrib-cssmin": "^0.10.0", 29 | "grunt-contrib-less": "^0.11.4", 30 | "grunt-contrib-uglify": "~0.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dist/angular-layout.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-layout - v0.1.1 - 2015-02-08 3 | * https://github.com/demerzel3/angular-layout 4 | * 5 | * Copyright (c) 2015 6 | * Authors : Gabriele Genta & Martin Mouterde 7 | * Licensed MIT <> 8 | */ 9 | !function(){"use strict";var a=angular.module("angular.layout",[]);a.directive("solid",function(){return{restrict:"E",scope:{size:"@"},replace:!0,template:'
'}}),a.directive("glue",function(){return{restrict:"E",replace:!0,template:''}});var b=function(b,c,d){a.directive(b,function(){return{restrict:"AE",link:function(a,e){e.attr("direction"+d,c),e.addClass("flexbox"),e.addClass(b)}}})};b("hbox","row",""),b("hboxXs","row","-xs"),b("hboxSm","row","-sm"),b("hboxMd","row","-md"),b("hboxLg","row","-lg"),b("vbox","column",""),b("vboxXs","column","-xs"),b("vboxSm","column","-sm"),b("vboxMd","column","-md"),b("vboxLg","column","-lg")}(); -------------------------------------------------------------------------------- /src/js/angular-layout.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | var angularLayoutModule = angular.module("angular.layout", []); 4 | 5 | angularLayoutModule.directive("solid", function () { 6 | return { 7 | restrict: "E", 8 | scope: {size: "@"}, 9 | replace: true, 10 | template: '' 11 | }; 12 | }); 13 | 14 | angularLayoutModule.directive("glue", function () { 15 | return { 16 | restrict: "E", 17 | replace: true, 18 | template: '' 19 | }; 20 | }); 21 | 22 | var directiveFactory = function (directiveName,direction, suffix) { 23 | angularLayoutModule.directive(directiveName, function () { 24 | return { 25 | restrict: "AE", 26 | link: function (scope, element) { 27 | element.attr("direction" + suffix, direction); 28 | element.addClass("flexbox"); 29 | element.addClass(directiveName); 30 | } 31 | }; 32 | }); 33 | }; 34 | 35 | directiveFactory("hbox","row",""); 36 | directiveFactory("hboxXs","row","-xs"); 37 | directiveFactory("hboxSm","row","-sm"); 38 | directiveFactory("hboxMd","row","-md"); 39 | directiveFactory("hboxLg","row","-lg"); 40 | 41 | directiveFactory("vbox","column",""); 42 | directiveFactory("vboxXs","column","-xs"); 43 | directiveFactory("vboxSm","column","-sm"); 44 | directiveFactory("vboxMd","column","-md"); 45 | directiveFactory("vboxLg","column","-lg"); 46 | 47 | })(); -------------------------------------------------------------------------------- /dist/angular-layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-layout - v0.1.1 - 2015-02-08 3 | * https://github.com/demerzel3/angular-layout 4 | * 5 | * Copyright (c) 2015 6 | * Authors : Gabriele Genta & Martin Mouterde 7 | * Licensed MIT <> 8 | */ 9 | (function () { 10 | 'use strict'; 11 | var angularLayoutModule = angular.module("angular.layout", []); 12 | 13 | angularLayoutModule.directive("solid", function () { 14 | return { 15 | restrict: "E", 16 | scope: {size: "@"}, 17 | replace: true, 18 | template: '' 19 | }; 20 | }); 21 | 22 | angularLayoutModule.directive("glue", function () { 23 | return { 24 | restrict: "E", 25 | replace: true, 26 | template: '' 27 | }; 28 | }); 29 | 30 | var directiveFactory = function (directiveName,direction, suffix) { 31 | angularLayoutModule.directive(directiveName, function () { 32 | return { 33 | restrict: "AE", 34 | link: function (scope, element) { 35 | element.attr("direction" + suffix, direction); 36 | element.addClass("flexbox"); 37 | element.addClass(directiveName); 38 | } 39 | }; 40 | }); 41 | }; 42 | 43 | directiveFactory("hbox","row",""); 44 | directiveFactory("hboxXs","row","-xs"); 45 | directiveFactory("hboxSm","row","-sm"); 46 | directiveFactory("hboxMd","row","-md"); 47 | directiveFactory("hboxLg","row","-lg"); 48 | 49 | directiveFactory("vbox","column",""); 50 | directiveFactory("vboxXs","column","-xs"); 51 | directiveFactory("vboxSm","column","-sm"); 52 | directiveFactory("vboxMd","column","-md"); 53 | directiveFactory("vboxLg","column","-lg"); 54 | 55 | })(); -------------------------------------------------------------------------------- /src/less/angular-layout-debug.less: -------------------------------------------------------------------------------- 1 | .vbox > .flexboxDebug.glue, 2 | .hbox > .flexboxDebug.glue { 3 | height: 1px; 4 | display: block; 5 | border-style: dotted; 6 | border-width: thin; 7 | border-color: rgb(174, 219, 219); 8 | } 9 | 10 | .vbox > .flexboxDebug.glue:after, 11 | .hbox > .flexboxDebug.glue:after { 12 | content: "\2192"; 13 | font-size: 24px; 14 | float: right; 15 | position: relative; 16 | top: -19px; 17 | right: -4px; 18 | color: rgb(174, 219, 219); 19 | } 20 | .vbox > .flexboxDebug.glue:before, 21 | .hbox > .flexboxDebug.glue:before { 22 | content: "\2190"; 23 | font-size: 24px; 24 | float: left; 25 | position: relative; 26 | top: -19px; 27 | left: -4px; 28 | color: rgb(174, 219, 219); 29 | } 30 | 31 | .vbox > .flexboxDebug.solid, 32 | .hbox > .flexboxDebug.solid { 33 | border: 3px double; 34 | border-color: black; 35 | } 36 | 37 | .vbox > .flexboxDebug.solid:before, 38 | .hbox > .flexboxDebug.solid:before { 39 | content: attr(size); 40 | } 41 | 42 | .hbox.flexboxDebug{ 43 | .mixin_gap_h_debug(20); 44 | } 45 | .vbox.flexboxDebug{ 46 | .mixin_gap_v_debug(20); 47 | } 48 | 49 | .vbox.flexboxDebug:before, 50 | .hbox.flexboxDebug:before { 51 | content: attr(debugLabel); 52 | z-index: 10; 53 | position: absolute; 54 | background-color: azure; 55 | } 56 | 57 | .vbox.flexboxDebug, 58 | .hbox.flexboxDebug { 59 | border-style: dashed; 60 | border-width: thin; 61 | border-color: rgb(174, 219, 219); 62 | } 63 | 64 | .mixin_gap_h_debug(@nb) { 65 | .-(@i: @nb) when (@i > 0) { 66 | &[gap="@{i}"] > *:not(:last-child) { 67 | border-right-width: e(%("%apx",@i)); 68 | border-right-color: red; 69 | border-right-style: inset; 70 | margin-right: 0; 71 | } 72 | .-((@i - 1)); 73 | } .-; 74 | } 75 | 76 | .mixin_gap_v_debug(@nb) { 77 | .-(@i: @nb) when (@i > 0) { 78 | &[gap="@{i}"] > *:not(:last-child) { 79 | border-bottom-width: e(%("%apx",@i)); 80 | border-bottom-color: red; 81 | border-bottom-style: inset; 82 | margin-bottom: 0; 83 | } 84 | .-((@i - 1)); 85 | } .-; 86 | } 87 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | /** 4 | * Load required Grunt tasks. These are installed based on the versions listed 5 | * in `package.json` when you do `npm install` in this directory. 6 | */ 7 | grunt.loadNpmTasks('grunt-contrib-clean'); 8 | grunt.loadNpmTasks('grunt-contrib-uglify'); 9 | grunt.loadNpmTasks('grunt-contrib-less'); 10 | grunt.loadNpmTasks('grunt-contrib-copy'); 11 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 12 | grunt.loadNpmTasks('grunt-banner'); 13 | grunt.loadNpmTasks('grunt-autoprefixer'); 14 | 15 | /** 16 | * This is the configuration object Grunt uses to give each plugin its 17 | * instructions. 18 | */ 19 | grunt.initConfig({ 20 | /** 21 | * We read in our `package.json` file so we can access the package name and 22 | * version. It's already there, so we don't repeat ourselves here. 23 | */ 24 | pkg: grunt.file.readJSON("package.json"), 25 | 26 | /** 27 | * The banner is the comment that is placed at the top of our compiled 28 | * source files. It is first processed as a Grunt template, where the `<%=` 29 | * pairs are evaluated based on this very configuration object. 30 | */ 31 | usebanner: { 32 | dist: { 33 | options: { 34 | banner: '/**\n' + 35 | ' * <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' + 36 | ' * <%= pkg.homepage %>\n' + 37 | ' *\n' + 38 | ' * Copyright (c) <%= grunt.template.today("yyyy") %>\n' + 39 | ' * Authors : Gabriele Genta & Martin Mouterde\n' + 40 | ' * Licensed <%= pkg.licenses.type %> <<%= pkg.licenses.url %>>\n' + 41 | ' */' 42 | }, 43 | files: { 44 | src: [ 'dist/**'] 45 | } 46 | } 47 | }, 48 | autoprefixer: { 49 | dist: { 50 | src: 'dist/angular-layout.css', 51 | dest: 'dist/angular-layout.css' 52 | } 53 | }, 54 | /** 55 | * The directories to delete when `grunt clean` is executed. 56 | */ 57 | clean: [ 58 | 'dist/' 59 | ], 60 | 61 | copy: { 62 | dist: { 63 | files: [ 64 | { 65 | src: [ 'src/js/angular-layout.js' ], 66 | dest: 'dist/angular-layout.js' 67 | } 68 | ] 69 | } 70 | }, 71 | /** 72 | * Minify the sources! 73 | */ 74 | uglify: { 75 | dist: { 76 | files: { 77 | 'dist/angular-layout.min.js': 'src/js/angular-layout.js' 78 | } 79 | } 80 | }, 81 | cssmin: { 82 | dist: { 83 | files: [ 84 | { 85 | expand: true, 86 | cwd: 'dist', 87 | src: ['*.css'], 88 | dest: 'dist', 89 | ext: '.min.css' 90 | } 91 | ] 92 | } 93 | }, 94 | less: { 95 | dev: { 96 | files: [ 97 | { 98 | expand: true, 99 | cwd: 'src/less', 100 | src: ['*.less'], 101 | dest: 'dist', 102 | ext: '.css' 103 | } 104 | ] 105 | } 106 | } 107 | }); 108 | 109 | /** 110 | * The default task is to build and compile. 111 | */ 112 | grunt.registerTask('default', [ 113 | 'build' 114 | ]); 115 | 116 | /** 117 | * The `build` task gets your app ready to run for development and testing. 118 | */ 119 | grunt.registerTask('build', [ 120 | 'clean', 121 | 'copy', 122 | 'uglify', 123 | 'less', 124 | 'autoprefixer', 125 | 'cssmin', 126 | 'usebanner' 127 | ]); 128 | 129 | }; 130 | -------------------------------------------------------------------------------- /src/less/angular-layout.less: -------------------------------------------------------------------------------- 1 | 2 | @directionValues: row, row-reverse, column, column-reverse; 3 | @alignItemsValues: baseline, center, flex-end, flex-start, stretch; 4 | @JustifyContentValues: center, flex-end, flex-start, space-around, space-between; 5 | @alignContentValues: center, flex-end, flex-start; 6 | @wrapValues: nowrap, wrap, wrap-reverse; 7 | @screenFormat: lg, md, sm, xs; 8 | @media-xs: ~"only screen and (max-width : 480px)"; 9 | @media-sm: ~"only screen and (min-width : 481px) and (max-width : 768px)"; 10 | @media-md: ~"only screen and (min-width: 769px) and (max-width : 992px)"; 11 | @media-lg: ~"only screen and (min-width: 993px) and(max-width : 1200px)"; 12 | @medias : @media-lg, @media-md, @media-sm, @media-xs; 13 | @glueGrowShrink: 99; 14 | 15 | .glue { 16 | flex-grow: @glueGrowShrink; 17 | flex-shrink: @glueGrowShrink; 18 | } 19 | 20 | .hbox { 21 | .mixin_gap(20,margin-right); 22 | } 23 | .vbox { 24 | .mixin_gap(20,margin-bottom); 25 | } 26 | 27 | .flexbox { 28 | display: flex; 29 | //add justify attr style 30 | .mixin_attrValueAsStyle(@JustifyContentValues,justify,justify-content); 31 | //add align attr style 32 | .mixin_attrValueAsStyle(@alignItemsValues,align,align-items); 33 | //add xJustify attr style 34 | .mixin_attrValueAsStyle(@alignContentValues,x-justify,align-content); 35 | //add wrap attr style 36 | .mixin_attrValueAsStyle(@wrapValues,wrap,flex-wrap); 37 | //add orientation attr style 38 | .mixin_attrValueAsStyle(@directionValues,direction,flex-direction); 39 | //add grow attr style 40 | //.mixin_attrValueAsIntStyle(20,grow,flex-grow,''); 41 | //add shrink attr style 42 | //.mixin_attrValueAsIntStyle(20,shrink,flex-shrink,''); 43 | //add order attr style 44 | //.mixin_attrValueAsIntStyle(20,order,order,''); 45 | //add grow attr style 46 | .mixin_childAttrValueAsIntStyle(20,grow,flex-grow,''); 47 | //add shrink attr style 48 | .mixin_childAttrValueAsIntStyle(20,shrink,flex-shrink,''); 49 | //add order attr style 50 | .mixin_childAttrValueAsIntStyle(20,order,order,''); 51 | .responsive_flexbox(); 52 | } 53 | 54 | .mixin_attrValueAsStyle(@arrayValues,@attributeName,@styleName) { 55 | .loop(@i: length(@arrayValues)) when (@i > 0) { 56 | @name: e(extract(@arrayValues, @i)); 57 | &[@{attributeName}="@{name}"] {@{styleName}: @name} 58 | .loop((@i - 1)); 59 | } .loop; 60 | } 61 | 62 | .mixin_attrValueAsIntStyle(@nb,@attributeName,@styleName,@unit) { 63 | .loop(@i: @nb) when (@i >= 0) { 64 | &[@{attributeName}="@{i}@{unit}"] {@{styleName}: @i e(@unit)} 65 | .loop((@i - 1)); 66 | } .loop; 67 | } 68 | 69 | .mixin_childAttrValueAsIntStyle(@nb,@attributeName,@styleName,@unit) { 70 | .loop(@i: @nb) when (@i >= 0) { 71 | & > *[@{attributeName}="@{i}@{unit}"] {@{styleName}: @i e(@unit)} 72 | .loop((@i - 1)); 73 | } .loop; 74 | } 75 | 76 | 77 | .mixin_gap(@nb,@margin) { 78 | .loop(@i: @nb) when (@i >= 0) { 79 | &[gap="@{i}"] > * {@{margin}: e(%("%apx",@i))} 80 | &[gap="@{i}"] :last-child {@{margin}: 0; } 81 | .loop((@i - 1)); 82 | } .loop; 83 | } 84 | 85 | .mixin_gap_h_debug(@nb) { 86 | .loop(@i: @nb) when (@i >= 0) { 87 | &[gap="@{i}"] > * { 88 | border-right-width: e(%("%apx",@i)); 89 | border-right-color: red; 90 | border-right-style: inset; 91 | } 92 | &[gap="@{i}"] :last-child {@{margin}: 0; } 93 | .loop((@i - 1)); 94 | } .loop; 95 | } 96 | 97 | .responsive_flexbox() { 98 | .loop(@i: length(@screenFormat)) when (@i > 0) { 99 | @aScreen: e(extract(@screenFormat, @i)); 100 | @aMedia: e(extract(@medias, @i)); 101 | @media @aMedia { .responsive_flexbox(@aScreen) } 102 | .loop((@i - 1)); 103 | } .loop; 104 | } 105 | 106 | .responsive_flexbox(@suffix) { 107 | //add justify attr style 108 | .mixin_attrValueAsStyle(@JustifyContentValues,e(%("justify-%a",@suffix)),justify-content); 109 | //add align attr style 110 | .mixin_attrValueAsStyle(@alignItemsValues,e(%("align-%a",@suffix)),align-items); 111 | //add xJustify attr style 112 | .mixin_attrValueAsStyle(@alignContentValues,e(%("x-justify-%a",@suffix)),align-content); 113 | //add wrap attr style 114 | .mixin_attrValueAsStyle(@wrapValues,e(%("wrap-%a",@suffix)),flex-wrap); 115 | //add orientation attr style 116 | .mixin_attrValueAsStyle(@directionValues,e(%("direction-%a",@suffix)),flex-direction); 117 | //add grow attr style 118 | //.mixin_attrValueAsIntStyle(20,e(%("grow-%a",@suffix)),flex-grow,''); 119 | //add shrink attr style 120 | //.mixin_attrValueAsIntStyle(20,e(%("shrink-%a",@suffix)),flex-shrink,''); 121 | //add order attr style 122 | //.mixin_attrValueAsIntStyle(20,e(%("order-%a",@suffix)),order,''); 123 | //add grow attr style 124 | .mixin_childAttrValueAsIntStyle(20,e(%("grow-%a",@suffix)),flex-grow,''); 125 | //add shrink attr style 126 | .mixin_childAttrValueAsIntStyle(20,e(%("shrink-%a",@suffix)),flex-shrink,''); 127 | //add order attr style 128 | .mixin_childAttrValueAsIntStyle(20,e(%("order-%a",@suffix)),order,''); 129 | } 130 | -------------------------------------------------------------------------------- /demos/fixed-width-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque cursus erat vitae tortor vulputate tempor. Sed eget eros at risus tincidunt elementum non vel nisl. Curabitur nec vestibulum eros, gravida pretium tellus. Vestibulum commodo viverra magna et sagittis. Mauris ut vestibulum justo. Praesent quis erat eu nunc adipiscing venenatis eget a lorem. Donec a vehicula metus, in bibendum enim. Fusce in mauris vitae ligula dignissim blandit in quis ligula. Proin turpis dolor, pellentesque eu mi eget, pulvinar lobortis ante. Morbi sem nibh, vulputate quis scelerisque sit amet, sagittis lacinia odio. Nullam ut dapibus sem, eu aliquam nulla. Suspendisse ut risus hendrerit, sollicitudin ipsum vel, pharetra leo.
59 |Praesent blandit auctor dui, quis elementum justo venenatis in. Ut iaculis vulputate elit, at convallis diam vestibulum aliquam. Cras porta tempus congue. Phasellus nec orci libero. Integer ut velit lobortis, suscipit arcu in, varius risus. Aenean ultrices eros nibh. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque eget tortor ac magna convallis suscipit.
60 |Nullam et nisi eget ligula feugiat molestie. Vivamus eu sem vel lorem cursus porttitor. Quisque orci quam, molestie eget imperdiet id, blandit sit amet purus. In in elementum nisl. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nunc blandit bibendum augue id condimentum. Aenean iaculis pellentesque justo, a bibendum felis lacinia in. Sed cursus et tortor eu fringilla. Morbi gravida ullamcorper erat, eget congue lorem ullamcorper vel. Phasellus et euismod urna. Aliquam erat volutpat. Vestibulum adipiscing, quam non aliquam interdum, magna diam accumsan nulla, eu lobortis mauris nulla non mauris. Praesent lobortis molestie aliquam.
61 |Proin fringilla lacus eget tellus ultricies, eu pretium elit gravida. Donec vitae augue urna. Quisque imperdiet lorem nec sagittis tristique. Phasellus egestas viverra lectus, at malesuada est cursus laoreet. Praesent non nibh eu eros hendrerit molestie. Etiam vulputate consequat pretium. Nam viverra libero libero, vel ultrices magna mollis ac. Duis volutpat nunc ut enim pretium, ut vestibulum justo vulputate. Morbi a odio feugiat, venenatis justo nec, condimentum tellus.
62 |Sed non ultricies enim, at dapibus quam. Mauris placerat mattis eros, id porttitor mauris mattis sit amet. Fusce lacus sem, consequat vitae venenatis eu, gravida nec risus. Duis id tempor lorem. Phasellus quis hendrerit augue. Duis ut urna non nunc imperdiet ultrices in at risus. Morbi tempus elit vel lacus ullamcorper auctor. Morbi mattis auctor consequat. Donec feugiat tincidunt tellus sed dignissim. Fusce imperdiet erat ante, mattis cursus sem tristique non. Cras eget congue elit, eu eleifend dolor. Aliquam quis tincidunt enim. In vel lacus in odio rhoncus euismod quis nec libero. Morbi tincidunt, mi ultrices eleifend auctor, ligula enim lacinia diam, eget aliquet tellus est nec elit. Mauris id libero ullamcorper, interdum felis id, luctus neque.
63 |