├── .gitignore ├── README.md ├── dev ├── basic.coffee └── materialize.config.styl ├── materialize.styl ├── package.json └── src └── side-nav.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.sublime-project 3 | *.sublime-workspace 4 | .vscode 5 | npm-debug.log 6 | static 7 | *.js 8 | shrinkwrap.yaml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ceri-side-nav 2 | 3 | a touch enabled, responsive side-nav. 4 | 5 | ### [Demo](https://ceri-comps.github.io/ceri-side-nav) 6 | 7 | 8 | # Install 9 | 10 | ```sh 11 | npm install --save-dev ceri-side-nav 12 | ``` 13 | 14 | ## Usage 15 | - [general ceri component usage instructions](https://github.com/cerijs/ceri#i-want-to-use-a-component-built-with-ceri) 16 | - in your project 17 | ```coffee 18 | window.customElements.define("ceri-side-nav", require("ceri-side-nav")) 19 | ``` 20 | ```html 21 | 22 |
  • First Text
  • 23 | 24 | 25 | 26 | 27 | 28 | 29 | ``` 30 | For examples see [`dev/`](dev/). 31 | 32 | 33 | 34 | #### Props 35 | Name | type | default | description 36 | ---:| --- | ---| --- 37 | target | String | null | Will attach toggling on given element, resolved by `querySelector`. 38 | fixed-screen-size | Number | 992 | Used with `fixed`. Above this size, the menu will stay opened. 39 | fixed | Boolean | false | should always show on large screens 40 | open | Boolean | false | set to open / close 41 | right | Boolean | false | attach to the right side instead of the left 42 | opacity | Number | 0.5 | opacity of the overlay 43 | z-index | Number | 1000 | minimum zIndex of the overlay. 44 | 45 | To not close the nav on a click inside, call `e.preventDefault()` on click. 46 | 47 | #### Events 48 | Name | description 49 | ---:| --- 50 | fixed(isFixed:Boolean) | emitted when menu get fixed or unfixed on large screen. 51 | toggled(isOpened:Boolean) | emitted when menu gets opened or closed. 52 | 53 | #### Themes 54 | - [ceri-materialize](https://github.com/ceri-comps/ceri-materialize) 55 | ```html 56 | 57 | 58 | ``` 59 | 60 | # Development 61 | Clone repository. 62 | ```sh 63 | npm install 64 | npm run dev 65 | ``` 66 | Browse to `http://localhost:8080/`. 67 | 68 | ## Notable changes 69 | #### 0.2.0 70 | - use ceri-materialize@2 71 | 72 | ## License 73 | Copyright (c) 2016 Paul Pflugradt 74 | Licensed under the MIT license. 75 | -------------------------------------------------------------------------------- /dev/basic.coffee: -------------------------------------------------------------------------------- 1 | require "./materialize.config.styl" 2 | window.customElements.define "ceri-side-nav", require "../src/side-nav.coffee" 3 | createView = require "ceri-dev-server/lib/createView" 4 | module.exports = createView 5 | mixins:[ 6 | require "ce/#if" 7 | require "ce/watch" 8 | ] 9 | structure: template 1, """ 10 | 11 |
  • First Text
  • 12 |
    13 |
    14 | 15 | 16 |
    17 | 18 |

    19 | source
    20 | """ 21 | computed: 22 | text: -> 23 | if @fixed 24 | if @sideNav.isFixed 25 | "Side-nav is fixed for this window-size. Make the window smaller for it to collapse." 26 | else 27 | if @right 28 | s = "drag >>" 29 | else 30 | s = "<< drag" 31 | return s+"\r\nMake window larger for the nav to stay opened" 32 | else 33 | if @right 34 | "drag >>" 35 | else 36 | "<< drag" 37 | style: -> 38 | if @right 39 | "text-align:right" 40 | else 41 | null 42 | class: -> if @materialize then "materialize" else null 43 | btnCls: -> if @materialize then "btn" else null 44 | data: -> 45 | right: false 46 | fixed: false 47 | materialize: false 48 | fixedScreenSize: 992 49 | tests: sideNav: -> 50 | it "should work", => 51 | should.exist @ -------------------------------------------------------------------------------- /dev/materialize.config.styl: -------------------------------------------------------------------------------- 1 | @require "~normalize.css" 2 | @require "../materialize" 3 | @require "~ceri-materialize/3_utils" 4 | @require "~ceri-materialize/3_typography" 5 | @require "~ceri-materialize/6_buttons" 6 | 7 | body { 8 | margin: 0; 9 | } 10 | ceri-side-nav:not(.materialize) { 11 | border:1px solid black; 12 | background-color: white; 13 | width:200px; 14 | padding: 20px; 15 | } 16 | .container { 17 | width: 100% !important 18 | } -------------------------------------------------------------------------------- /materialize.styl: -------------------------------------------------------------------------------- 1 | @require "~ceri-materialize/2_variables" 2 | @require "~ceri-materialize/4_zLevels" 3 | ceri-side-nav.materialize 4 | width: 300px 5 | margin: 0 6 | padding-bottom: 60px 7 | background-color: $sidenav-bg-color 8 | overflow-y: auto 9 | backface-visibility: hidden 10 | list-style-type: none 11 | @extend .z-depth-1 12 | .collapsible 13 | margin: 0 14 | li 15 | float: none 16 | line-height: $sidenav-line-height 17 | &.active 18 | background-color: rgba(0, 0, 0, 0.05) 19 | li > a 20 | color: $sidenav-font-color 21 | display: block 22 | font-size: $sidenav-font-size 23 | font-weight: 500 24 | height: $sidenav-item-height 25 | line-height: $sidenav-line-height 26 | padding: 0 $sidenav-padding * 2 27 | &:hover 28 | background-color: rgba(0, 0, 0, 0.05) 29 | &.btn, &.btn-large, &.btn-flat, &.btn-floating 30 | margin: 10px 15px 31 | &.btn, 32 | &.btn-large, 33 | &.btn-floating 34 | color: $button-raised-color 35 | &.btn-flat 36 | color: $button-flat-color 37 | &.btn:hover, 38 | &.btn-large:hover 39 | background-color: lighten($button-raised-background, 5%) 40 | &.btn-floating:hover 41 | background-color: $button-raised-background 42 | & > i, 43 | & > [class^="mdi-"], li > a > [class*="mdi-"], 44 | & > i.material-icons 45 | float: left 46 | height: $sidenav-item-height 47 | line-height: $sidenav-line-height 48 | margin: 0 $sidenav-padding * 2 0 0 49 | width: ($sidenav-item-height / 2) 50 | color: rgba(0, 0, 0, 0.54) 51 | .divider 52 | margin: $sidenav-padding / 2 0 0 0 53 | .subheader 54 | &:hover 55 | background-color: transparent 56 | cursor: initial 57 | pointer-events: none 58 | color: rgba(0, 0, 0, 0.54) 59 | font-size: $sidenav-font-size 60 | font-weight: 500 61 | line-height: $sidenav-line-height 62 | .collapsible-body 63 | padding: 0 64 | & > ul:not(.collapsible) > li.active 65 | background-color: $primary-color 66 | a 67 | color: $sidenav-bg-color -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ceri-side-nav", 3 | "description": "a touch enabled, responsive side-nav", 4 | "version": "0.2.0", 5 | "homepage": "https://github.com/ceri-comps", 6 | "author": { 7 | "name": "Paul Pflugradt", 8 | "email": "paul.pflugradt@gmail.com" 9 | }, 10 | "license": "MIT", 11 | "main": "side-nav.js", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/ceri-comps/ceri-side-nav" 15 | }, 16 | "engines": { 17 | "node": "*" 18 | }, 19 | "files": [ 20 | "*.js", 21 | "*.styl" 22 | ], 23 | "dependencies": { 24 | "ceri": "^1.0.26" 25 | }, 26 | "devDependencies": { 27 | "ceri-compiler": "^1.1.4", 28 | "ceri-dev-server": "^1.0.12", 29 | "ceri-materialize": "^2.0.0", 30 | "coffee-loader": "^0.7.3", 31 | "coffee-script": "^1.12.7", 32 | "css-loader": "^0.28.7", 33 | "gh-pages": "^1.0.0", 34 | "normalize.css": "^7.0.0", 35 | "script-runner": "^0.1.7", 36 | "stylus": "^0.54.5", 37 | "stylus-loader": "^3.0.1" 38 | }, 39 | "keywords": [ 40 | "side-nav", 41 | "ceri" 42 | ], 43 | "readmeFilename": "README.md", 44 | "scripts": { 45 | "build:coffee": "coffee -c -o . src/side-nav.coffee", 46 | "build:bundle": "ceri-compiler -b side-nav.js", 47 | "build": "run-npm build:*", 48 | "dev": "ceri-dev-server", 49 | "watch": "ceri-dev-server --test --watch", 50 | "test": "ceri-dev-server --test", 51 | "preversion": "npm test", 52 | "version": "npm run build && git add .", 53 | "postversion": "git push && git push --tags && npm publish", 54 | "ghpages": "ceri-dev-server --static static/ && gh-pages -d static" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/side-nav.coffee: -------------------------------------------------------------------------------- 1 | ceri = require "ceri/lib/wrapper" 2 | module.exports = ceri 3 | 4 | mixins: [ 5 | require "ceri/lib/props" 6 | require "ceri/lib/style" 7 | require "ceri/lib/animate" 8 | require "ceri/lib/directives" 9 | require "ceri/lib/open" 10 | require "ceri/lib/overlay" 11 | require "ceri/lib/#if" 12 | require "ceri/lib/draghandle" 13 | ] 14 | 15 | props: 16 | opacity: 17 | type: Number 18 | default: 0.5 19 | right: 20 | type: Boolean 21 | cbs: ["setParentMargin",((val) -> 22 | if val 23 | @style.right = 0 24 | @style.left = null 25 | else 26 | @style.right = null 27 | @style.left = null 28 | )] 29 | target: 30 | type: String 31 | fixed: 32 | type: Boolean 33 | cbs: "processFixed" 34 | delay: true 35 | fixedScreenSize: 36 | type: Number 37 | default: 992 38 | cbs: "processFixed" 39 | zIndex: 40 | type:Number 41 | default: 1000 42 | cbs: (val,old) -> 43 | s = @style 44 | return if s.zIndex and s.zIndex != old 45 | s.zIndex = val 46 | 47 | data: -> isFixed: null, keepOpen: true, dhZIndex: null 48 | 49 | overlay: 50 | zIndex: "zIndex" 51 | active: "dismissable" 52 | animate: "toggleAnimate" 53 | delay: true 54 | onClose: -> @style.zIndex = @zIndex; @dhZIndex = @zIndex-1; @hide.call(@) 55 | onOpen: (zIndex) -> @style.zIndex = zIndex+2; @dhZIndex = zIndex+1 56 | 57 | draghandle: 58 | __parentElement: 59 | active: -> !@isFixed 60 | initStyle: 61 | position: "absolute" 62 | top: 0 63 | bottom: 0 64 | style: -> 65 | zIndex: @dhZIndex 66 | width: if @open then null else "20px" 67 | left: if !@right or @open then 0 else null 68 | right: if @right or @open then 0 else null 69 | onClick: -> @hide() if @open 70 | onFirstMove: (o) -> 71 | unless @open 72 | @style.transform = "translateX(#{@fac*100}%)" 73 | @_attach() 74 | o.wasOpened = @open 75 | w = @offsetWidth * @fac 76 | if @right 77 | o.shouldShow = (move) -> move < 0 78 | o.shouldHide = (move) -> move > w 79 | else 80 | o.shouldShow = (move) -> move > 0 81 | o.shouldHide = (move) -> move < w 82 | onMove: (delta, o) -> 83 | move = 2 * delta.x + @offsetWidth * @fac * !o.wasOpened 84 | if o.shouldShow(move) 85 | @_setOpen() 86 | @style.transform = null 87 | o.move = 0 88 | else if o.shouldHide(move) 89 | @_setClose() 90 | @style.transform = "translateX(#{@fac*100}%)" 91 | o.move = -@offsetWidth 92 | else 93 | @style.transform = "translateX(#{move}px)" 94 | o.move = move 95 | onEnd: (delta, o) -> 96 | if @open and o.move < 0 97 | @animation = @enter _percent: 1 - o.move/@offsetWidth * @fac 98 | else if not @open and o.move > -@offsetWidth 99 | @animation = @leave _percent: o.move/@offsetWidth * @fac, done: @_detach 100 | 101 | events: 102 | click: 103 | this: 104 | active: "dismissable" 105 | notPrevented: true 106 | prevent: true 107 | cbs: ["toggle"] 108 | _target: 109 | active: ->@_target? and not @isFixed 110 | notPrevented: true 111 | prevent: true 112 | cbs: ["toggle"] 113 | destroy: true 114 | resize: 115 | window: 116 | el: window 117 | active: "fixed" 118 | throttled: true 119 | destroy: true 120 | cbs: "processFixed" 121 | directives: 122 | _target: 123 | type: "#" 124 | name: "if" 125 | activate: "_target" 126 | value: "isFixed" 127 | not: true 128 | initStyle: 129 | position: "fixed" 130 | height: "100%" 131 | computedStyle: 132 | this: -> 133 | willChange: if @isFixed then null else "transform" 134 | 135 | computed: 136 | _target: -> 137 | return null unless @target 138 | return document.querySelector(@target) 139 | fac: -> @right*2-1 140 | dismissable: -> 141 | @openingOrOpen and not @isFixed 142 | 143 | methods: 144 | makeFixed: (fixed) -> 145 | if fixed != @isFixed 146 | @isFixed = fixed 147 | @$emit name:"fixed", detail:fixed 148 | setParentMargin: -> 149 | if @parentElement 150 | if @isFixed 151 | width = @offsetWidth + "px" 152 | else 153 | width = null 154 | for el in @parentElement.children 155 | if el != @ 156 | @$style.set el, 157 | marginLeft: if @right then null else width 158 | marginRight: if @right then width else null 159 | processFixed: -> 160 | if @fixed 161 | old = @isFixed 162 | @makeFixed(window.innerWidth > @fixedScreenSize) 163 | if old != @isFixed 164 | @wasOpened = @open 165 | if @isFixed 166 | @show(false) 167 | @setParentMargin() 168 | else 169 | @setParentMargin() 170 | @hide(false) 171 | else 172 | @makeFixed(false) 173 | @setParentMargin() 174 | if not @openingOrOpen or not @wasOpened 175 | @hide(false) 176 | enter: (o) -> 177 | o.style = translateX: [@fac*100,0,"%"] 178 | return @$animate(o) 179 | 180 | leave: (o) -> 181 | o.style = translateX: [0,@fac*100,"%"] 182 | return @$animate(o) 183 | 184 | beforeToggle: -> not @isFixed 185 | 186 | onHide: -> @wasOpened = false 187 | --------------------------------------------------------------------------------