JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
stylus helpers: add wfpl_columns()
authorJason Woofenden <jason@jasonwoof.com>
Fri, 1 Jan 2016 17:31:41 +0000 (12:31 -0500)
committerJason Woofenden <jason@jasonwoof.com>
Fri, 1 Jan 2016 17:31:41 +0000 (12:31 -0500)
stylus_helpers.styl

index bc73f95..e6df86e 100644 (file)
@@ -239,3 +239,204 @@ noselect()
        -moz-user-select: none
        -ms-user-select: none
        user-select: none
+
+// Do all the crazy math to get columns to fit properly
+// and (optionally) scale for responsive designs.
+//
+// example:
+//
+//     columns = wfpl_columns({
+//             type: 'node',
+//             name: 'centerer',
+//             margin: 15px,
+//             width: 940px,
+//             child: {
+//                     type: 'node',
+//                     name: 'middler',
+//                     border-width: 1px,
+//                     border-style: solid,
+//                     border-color: black,
+//                     padding-left: 30px,
+//                     padding-right: 18px,
+//                     child: {
+//                             type: 'alternatives',
+//                             full: {
+//                                     type: 'series',
+//                                     nav: {
+//                                             type: 'node'
+//                                             width: 219px
+//                                             float: left
+//                                     },
+//                                     main: {
+//                                             type: 'node'
+//                                             margin-left: 48px,
+//                                             width: 623px
+//                                             float: left
+//                                     }
+//                             },
+//                             with_sidebar: {
+//                                     type: 'series',
+//                                     nav: {
+//                                             type: 'node',
+//                                             width: 219px
+//                                             float: left
+//                                     },
+//                                     main: {
+//                                             type: 'node',
+//                                             margin-left: 48px,
+//                                             width: 343px
+//                                             float: left
+//                                     },
+//                                     sidebar: {
+//                                             type: 'alternatives',
+//                                             plain: {
+//                                                     type: 'node'
+//                                                     margin-left: 30px,
+//                                                     width: 250px
+//                                                     float: left
+//                                             },
+//                                             bordered: {
+//                                                     type: 'node'
+//                                                     margin-left: 30px,
+//                                                     border-width: 1px,
+//                                                     border-style: solid
+//                                                     border-color: red
+//                                                     padding: 15px,
+//                                                     width: 218px
+//                                                     float: left
+//                                             }
+//                                     }
+//                             }
+//                     }
+//             }
+//     })
+// output css from column calculations
+//     for selector, css in columns.css
+//             body > {selector}
+//                     {css}
+//     @media screen and (max-width: (columns.width))
+//             // output responsive css from column calculations
+//             for selector, css in columns.responsive_css
+//                     body > {selector}
+//                             {css}
+//
+//             // as big as it can be
+//             body > .centerer
+//                     width: auto
+//
+//             // make sure that borders (which won't scale) and rounding errors don't
+//             // break the layout
+//             body > .centerer.full > .main,
+//             body > .centerer.with_sidebar > .sidebar.plain,
+//             body > .centerer.with_sidebar > .sidebar.bordered
+//                     margin-right: -10px
+//
+//     // switch to 1-column layout
+//     @media screen and (max-width: (single_column_max))
+//             for selector, css in columns.css
+//                     body > {selector}
+//                             if selector == '.centerer'
+//                                     margin-top: 0
+//                             else if selector in hide_in_one_column_mode
+//                                     display: none
+//                             else
+//                                     border: none
+//                                     float: none
+//                                     width: auto
+//                                     margin: 0
+//                                     padding: 0
+//                                     margin-top: columns['responsive_css']['.centerer']['margin']
+wfpl_columns_helper(top, node, selector, parent_width, expected_width)
+       css_rules = {}
+       responsive_css_rules = {}
+       if node.type == 'node'
+               left_width = 0px
+               right_width = 0px
+               width = null // inner
+               outer_width = null
+               if node.name
+                       if selector == ''
+                               selector = '.' + node.name
+                       else
+                               selector += ' > .' + node.name
+               // callculate width and outer_width
+               for k, v in node
+                       if match('^(margin|padding|border-width)$', k)
+                               left_width += v
+                               right_width += v
+                       if match('^((margin|padding)-left)|(border-left-width)$', k)
+                               left_width += v
+                       if match('^((margin|padding)-right)|(border-right-width)$', k)
+                               right_width += v
+                       if k != 'type' && k != 'child' && k != 'name' && k != 'outer-width' && k != 'outer_width'
+                               css_rules[k] = v
+               for k, v in node
+                       if k == 'width'
+                               width = v
+                               outer_width = left_width + v + right_width
+                       if k == 'outer-width' && k == 'outer_width'
+                               outer_width = k
+                               width = v - left_width - right_width
+               if (!width) && (!outer_width)
+                       if parent_width
+                               outer_width = parent_width
+                       else
+                               error("Couldn't figure out width of " + selector);
+               if outer_width && !width
+                       width = outer_width - left_width - right_width
+               if width && !outer_width
+                       outer_width = width + left_width + right_width
+               unless parent_width // should only happen at top level
+                       parent_width = outer_width
+               top['css'][selector] = css_rules
+               for k, v in node
+                       if k != 'outer-width' && k != 'outer_width' && k != 'border-width' && k != 'border-left-width' && k != 'border-right-width'
+                               if typeof(v) == 'unit' && unit(v) == 'px'
+                                       responsive_css_rules[k] = floor(unit((v / parent_width) * 100, '%'), 4)
+               top['responsive_css'][selector] = responsive_css_rules
+               if 'child' in node
+                       child_width = wfpl_columns_helper(top, node.child, selector, width, width)
+                       _columns_recurser(rargs)
+                       if child_width != width
+                               error(selector + " is the is wrong width. Expected: " + width + ", got: " + rargs.ret.w)
+               top['widths'][selector] = width
+               return outer_width
+       if node.type == 'alternatives'
+               for name, child in node
+                       if name != 'type'
+                               child_selector = selector + '.' + name
+                               child_width = wfpl_columns_helper(top, child, child_selector, parent_width, expected_width)
+                               if expected_width
+                                       if child_width != expected_width
+                                               error(child_selector + " is wrong width. Expected: " + expected_width + ", got: " + child_width)
+                               else
+                                       expected_width = child_width
+                               unless parent_width
+                                       parent_width = child_width
+               return expected_width
+       if node.type == 'series'
+               series_width = 0px
+               for name, child in node
+                       if name != 'type'
+                               if selector == ''
+                                       child_selector = '.' + name
+                               else
+                                       child_selector = selector + ' > .' + name
+                               child_width = wfpl_columns_helper(top, child, child_selector, parent_width, null)
+                               series_width += child_width
+               if expected_width
+                       if series_width != expected_width
+                               error(selector + " series total wrong. expected: " + expected_width + ", got: " + series_width);
+               return series_width
+       return // never reached with valid data
+
+wfpl_columns(hash)
+       top = { // contains settings, and is returned from porcelain
+               responsive: responsive
+               width: null
+               css: {}
+               responsive_css: {}
+               widths: {}
+       }
+       top.width = wfpl_columns_helper(top, hash, '', null, null)
+       return top