├── .gitignore ├── LICENSE.md ├── README.md ├── css ├── fonts │ ├── MaterialIcons-Regular.eot │ ├── MaterialIcons-Regular.svg │ ├── MaterialIcons-Regular.ttf │ ├── MaterialIcons-Regular.woff │ └── material-icons.woff2 ├── material-design-font-style.css ├── miller-material.css ├── miller.css └── miller.scss ├── demo ├── .DS_Store ├── css │ ├── demo.css │ └── simple-dropdown.css └── js │ ├── demo.js │ ├── lokijs.min.js │ ├── simple-dropdown.js │ └── simple-popup.min.js ├── fonts ├── .DS_Store └── roboto │ ├── Roboto-Bold.ttf │ ├── Roboto-Light.ttf │ └── Roboto-Regular.ttf ├── index.html ├── js ├── jquery.js └── miller.js ├── package.json └── tutorial ├── index.html ├── jquery.js ├── miller.css └── miller.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_store 2 | .idea/ 3 | 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Degen Sharew 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # responsive-miller-column 2 | 3 | Please look at the practical demo page here 4 | [link](https://dsharew.github.io/responsive-miller-column/). 5 | 6 |

Responsive Miller columns

7 | 8 | This is a Jquery plugin that implements Miller Columns with responsive design. 9 | Miller columns (also known as Cascading) are a browsing/visualization technique that can be applied to tree structures. The columns allow multiple levels of the hierarchy to be open at once, and provide a visual representation of the current location. It is closely related to techniques used earlier in the Smalltalk browser, but was independently invented by Mark S. Miller in 1980 at Yale University. The technique was then used at Project Xanadu, Datapoint, and NeXT. [More info](https://en.wikipedia.org/wiki/Miller_columns) 10 | 11 |

Main Features

12 | 1. Responsive on all screen sizes 13 | 2. Material Design support 14 | 3. Editable 15 | 16 |

Dependency

17 | _jQuery_ 18 | 19 |

How to install

20 | ```npm i responsive-miller-column``` 21 | 22 |

Interfaces

23 | The plugin defines two data classes so far. 24 |
Category
25 | Data class that defines the columns. 26 | 27 | ```javascript 28 | function Category() { 29 | 30 | var _this = this; 31 | 32 | _this.categoryId = guid(); 33 | 34 | _this.setCategoryId = function (categoryId) { 35 | 36 | _this.categoryId = categoryId; 37 | 38 | }; 39 | 40 | _this.getCategoryId = function () { 41 | return _this.categoryId; 42 | }; 43 | 44 | 45 | _this.setCategoryName = function (categoryName) { 46 | 47 | _this.categoryName = categoryName; 48 | 49 | }; 50 | _this.getCategoryName = function () { 51 | return _this.categoryName; 52 | }; 53 | 54 | 55 | _this.setParentId = function (parentId) { 56 | 57 | _this.parentId = parentId; 58 | 59 | }; 60 | _this.getParentId = function () { 61 | return _this.parentId; 62 | }; 63 | 64 | 65 | _this.setIsLowestLevel = function (isLowestLevel) { 66 | _this.isLowestLevel = isLowestLevel; 67 | }; 68 | _this.getIsLowestLevel = function () { 69 | return _this.isLowestLevel 70 | }; 71 | 72 | _this.setItems = function (categoryItems) { 73 | _this.items = categoryItems; 74 | }; 75 | _this.getItems = function () { 76 | return _this.items 77 | } 78 | 79 | 80 | } 81 | ``` 82 | 83 |
CategoryItem
84 | Data class that defines each item that are contained with in columns. 85 | 86 | ```javascript 87 | 88 | function CategoryItem() { 89 | 90 | var _this = this; 91 | 92 | _this.itemId = guid(); 93 | 94 | _this.setItemId = function (itemId) { 95 | 96 | _this.itemId = itemId; 97 | 98 | }; 99 | 100 | _this.getItemId = function () { 101 | return _this.itemId; 102 | }; 103 | 104 | 105 | _this.setItemName = function (itemName) { 106 | 107 | _this.itemName = itemName; 108 | 109 | }; 110 | _this.getItemName = function () { 111 | return _this.itemName; 112 | }; 113 | 114 | 115 | _this.setItemIcon = function (itemIcon) { 116 | 117 | _this.itemIcon = itemIcon; 118 | 119 | }; 120 | _this.getItemIcon = function () { 121 | return _this.itemIcon; 122 | }; 123 | 124 | 125 | _this.setParentId = function (parentId) { 126 | 127 | _this.parentId = parentId; 128 | 129 | }; 130 | _this.getParentId = function () { 131 | return _this.parentId; 132 | }; 133 | 134 | 135 | _this.setCategoryId = function (categoryId) { 136 | 137 | _this.categoryId = categoryId; 138 | 139 | }; 140 | _this.getCategoryId = function () { 141 | return _this.categoryId; 142 | }; 143 | 144 | 145 | _this.setHasChildren = function (hasChildren) { 146 | _this.hasChildren = hasChildren; 147 | }; 148 | _this.getHasChildren = function () { 149 | return _this.hasChildren 150 | }; 151 | 152 | _this.setNumChildren = function(numChildren) { 153 | _this.numChildren = numChildren; 154 | _this.setHasChildren(numChildren != 0); 155 | } 156 | 157 | _this.getNumChildren = function(){ 158 | return _this.numChildren; 159 | }; 160 | 161 | _this.isDeletable = true; 162 | _this.setIsDeletable = function (isDeletable) { 163 | _this.isDeletable = isDeletable; 164 | }; 165 | _this.getIsDeletable = function () { 166 | return _this.isDeletable 167 | }; 168 | 169 | 170 | } 171 | 172 | 173 | ``` 174 | 175 | ``` isDeleteable ``` property is used if the miller column is editable. If this prop is true a __Delete__ icon will appear infront of the item. 176 | 177 | Category contains zero or more CategoryItem. 178 | 179 |
JSON support
180 | 181 | The plugin also accepts any js object as far as it contains the required props. 182 | Here is a valid json object as example. 183 | Note: the plugin does not accepts JSON string, if you have a json string you have to call ```JSON.parse(jsonString)``` to convert your json string into object. 184 | 185 | ```javascript 186 | { 187 | "categoryId":"63ef58e5-fc92-9934-9b4c-ca5f457a425b", 188 | "categoryName":"Category 2", 189 | "parentId":"53732c02-f3d3-10de-1710-c74a8e3df260", 190 | "isLowestLevel":false, 191 | "items":[ 192 | { 193 | "itemId":"50b73f3a-a302-14dc-e61c-3da72876e712", 194 | "isDeleteable":true, 195 | "itemName":"Category 2 item 1", 196 | "hasChildren":false, 197 | "categoryId":"63ef58e5-fc92-9934-9b4c-ca5f457a425b", 198 | "parentId":"d39e098e-5ecb-3ba5-8fd8-fa20c7685e8c" 199 | }, 200 | { 201 | "itemId":"d98c4bfc-cae5-3355-08b8-84d0bc4edd59", 202 | "isDeleteable":true, 203 | "itemName":"Category 2 item 2", 204 | "hasChildren":true, 205 | "categoryId":"63ef58e5-fc92-9934-9b4c-ca5f457a425b", 206 | "parentId":"d39e098e-5ecb-3ba5-8fd8-fa20c7685e8c" 207 | }, 208 | { 209 | "itemId":"50603731-b62e-d0d2-dda8-493e9882651f", 210 | "isDeleteable":true, 211 | "itemName":"Category 2 item 3", 212 | "hasChildren":true, 213 | "categoryId":"63ef58e5-fc92-9934-9b4c-ca5f457a425b", 214 | "parentId":"d39e098e-5ecb-3ba5-8fd8-fa20c7685e8c" 215 | }, 216 | { 217 | "itemId":"72942b44-d011-1530-26b0-aa7f3351209e", 218 | "isDeleteable":true, 219 | "itemName":"Category 2 item 4", 220 | "hasChildren":true, 221 | "categoryId":"63ef58e5-fc92-9934-9b4c-ca5f457a425b", 222 | "parentId":"d39e098e-5ecb-3ba5-8fd8-fa20c7685e8c" 223 | }, 224 | { 225 | "itemId":"34644c0c-56f2-dc59-a649-2ea8d7195e97", 226 | "isDeleteable":true, 227 | "itemName":"Category 2 item 5", 228 | "hasChildren":false, 229 | "categoryId":"63ef58e5-fc92-9934-9b4c-ca5f457a425b", 230 | "parentId":"d39e098e-5ecb-3ba5-8fd8-fa20c7685e8c" 231 | } 232 | ] 233 | } 234 | ``` 235 | 236 |

Usage:

237 |

How to initialize?

238 | 239 | ```javascript 240 | 241 | var $millerCol = $("#miller_col"); 242 | $millerCol.millerColumn({ 243 | initData: rootCategory 244 | }); 245 | 246 | ``` 247 | ```rootCategory``` has to be type of class ```Category``` or any other class that contains at aleast the required props defined with in class ```Category```. 248 |

Options

249 |
Read only
250 | 251 | The miller column can be either readonly or editable; if it is editable you will action buttons for adding childrens, editing category item, deleting category item. 252 | When these action buttons are clicked they will emit the appropriate events. See events section for more info. 253 | ```javascript 254 | 255 | $millerCol.millerColumn({ 256 | isReadOnly: true, 257 | initData: rootCategory 258 | }); 259 | 260 | ``` 261 | 262 | 263 |

Adding a Category(Column)

264 | 265 | ```javascript 266 | 267 | var $millerCol = $("#miller_col"); 268 | $millerCol.millerColumn("addCol", category); 269 | 270 | ``` 271 | 272 |

Adding a Category Item

273 | 274 | ```javascript 275 | 276 | var $millerCol = $("#miller_col"); 277 | $millerCol.millerColumn("addItem", categoryItem); 278 | 279 | ``` 280 | ```categoryItem``` has to be type of class ```CategoryItem``` or any other class that contains at aleast the required props defined with in class ```CategoryItem```. 281 | 282 | 283 |

Update Category Item

284 | 285 | ```javascript 286 | 287 | var $millerCol = $("#miller_col"); 288 | $millerCol.millerColumn("updateItem", categoryItem); 289 | 290 | ``` 291 | 292 |

Delete Category Item

293 | 294 | ```javascript 295 | 296 | var $millerCol = $("#miller_col"); 297 | $millerCol.millerColumn("deleteItem", categoryItem); 298 | 299 | ``` 300 | 301 |

Events:

302 | The plugin emits the ff events. 303 | 304 |

Item Selected Event

305 | 306 | Event Name: ```item-selected``` 307 | Data: ```CategoryItem``` object that contains all necessary info about the selected category item. 308 | Usage: e.g how to listen for this event: 309 | ```javascript 310 | $millerCol.on("item-selected", ".miller-col-list-item", function (event, data) { 311 | //your logic here. 312 | }); 313 | 314 | ``` 315 | 316 |

Add CategoryItem event

317 | 318 | Event Name: ```add-item``` 319 | Data: ```Category``` object that contains all necessary info about category where the action was triggered. 320 | Usage: e.g how to listen for this event: 321 | ```javascript 322 | $millerCol.on("add-item", ".miller-col-container", function (event, data) { 323 | //your logic here. 324 | }); 325 | 326 | ``` 327 | 328 |

Edit CategoryItem event

329 | 330 | Event Name: ```edit-item``` 331 | Data: ```CategoryItem``` object that contains all necessary info about the tobe edited category item. 332 | Usage: e.g how to listen for this event: 333 | ```javascript 334 | $millerCol.on("edit-item", ".miller-col-list-item", function (event, data) { 335 | //your logic here. 336 | }); 337 | 338 | ``` 339 | 340 |

Delete CategoryItem event

341 | 342 | Event Name: ```delete-item``` 343 | Data: ```CategoryItem``` object that contains all necessary info about the tobe deleted category item. 344 | Usage: e.g how to listen for this event: 345 | ```javascript 346 | $millerCol.on("delete-item", ".miller-col-list-item", function (event, data) { 347 | //your logic here. 348 | }); 349 | 350 | ``` 351 | -------------------------------------------------------------------------------- /css/fonts/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsharew/responsive-miller-column/06ac4b708675b9ee7cab5a2f5f11d2d77df5962d/css/fonts/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /css/fonts/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsharew/responsive-miller-column/06ac4b708675b9ee7cab5a2f5f11d2d77df5962d/css/fonts/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /css/fonts/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsharew/responsive-miller-column/06ac4b708675b9ee7cab5a2f5f11d2d77df5962d/css/fonts/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /css/fonts/material-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsharew/responsive-miller-column/06ac4b708675b9ee7cab5a2f5f11d2d77df5962d/css/fonts/material-icons.woff2 -------------------------------------------------------------------------------- /css/material-design-font-style.css: -------------------------------------------------------------------------------- 1 | /* Roboto font */ 2 | 3 | @font-face { 4 | font-family: "Roboto"; 5 | src: url('../fonts/roboto/Roboto-Regular.ttf'); 6 | font-weight: normal; 7 | } 8 | 9 | @font-face { 10 | font-family: "Roboto-bold"; 11 | src: url('../fonts/roboto/Roboto-Bold.ttf'); 12 | font-weight: normal; 13 | } 14 | 15 | @font-face { 16 | font-family: "Roboto-light"; 17 | src: url('../fonts/roboto/Roboto-Light.ttf'); 18 | font-weight: normal; 19 | } 20 | 21 | /* Miller material */ 22 | 23 | .miller-col-list-item .list-item-text { 24 | font-family: "Roboto-light", sans-serif; 25 | } 26 | 27 | .miller-col-title-text { 28 | font-family: "Roboto-bold", sans-serif; 29 | text-transform: uppercase; 30 | font-size: 90%; 31 | font-weight: 700; 32 | letter-spacing: 1px; 33 | } 34 | 35 | .material-icons { 36 | color: #bbb; 37 | -webkit-transition: 300ms; 38 | -moz-transition: 300ms; 39 | -ms-transition: 300ms; 40 | transition: 300ms; 41 | } 42 | 43 | .miller-col-nav .material-icons { 44 | font-size: 2.85em; 45 | } 46 | 47 | /* fallback */ 48 | 49 | @font-face { 50 | font-family: 'Material Icons'; 51 | font-style: normal; 52 | font-weight: 400; 53 | src: url(fonts/material-icons.woff2) format('woff2'), url(fonts/MaterialIcons-Regular.woff) format('woff'), url(fonts/MaterialIcons-Regular.ttf) format('truetype'), url(fonts/MaterialIcons-Regular.svg), url(fonts/MaterialIcons-Regular.eot); 54 | } 55 | 56 | .material-icons { 57 | font-family: 'Material Icons'; 58 | font-weight: normal; 59 | font-style: normal; 60 | font-size: 24px; 61 | line-height: 1; 62 | letter-spacing: normal; 63 | text-transform: none; 64 | display: inline-block; 65 | white-space: nowrap; 66 | word-wrap: normal; 67 | direction: ltr; 68 | font-feature-settings: 'liga'; 69 | -webkit-font-feature-settings: 'liga'; 70 | -webkit-font-smoothing: antialiased; 71 | text-rendering: optimizeLegibility; 72 | -moz-osx-font-smoothing: grayscale; 73 | } 74 | -------------------------------------------------------------------------------- /css/miller-material.css: -------------------------------------------------------------------------------- 1 | .miller-col-list-item .list-item-text { 2 | font-family: "Roboto-light", sans-serif; 3 | } 4 | 5 | .miller-col-title-text { 6 | font-family: "Roboto-bold", sans-serif; 7 | text-transform: uppercase; 8 | font-size: 90%; 9 | font-weight: 700; 10 | letter-spacing: 1px; 11 | } 12 | 13 | .material-icons { 14 | color: #bbb; 15 | -webkit-transition: 300ms; 16 | -moz-transition: 300ms; 17 | -ms-transition: 300ms; 18 | transition: 300ms; 19 | } 20 | 21 | .miller-col-nav .material-icons { 22 | font-size: 2.85em; 23 | } -------------------------------------------------------------------------------- /css/miller.css: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar { 2 | background: rgba(0, 0, 0, 0); 3 | width: 6px; 4 | } 5 | 6 | ::-webkit-scrollbar-thumb { 7 | background: rgba(0, 0, 0, 0.3); 8 | height: 30px; 9 | border-radius: 2px; 10 | } 11 | 12 | * [class^=miller] { 13 | box-sizing: border-box; 14 | padding: 0; 15 | margin: 0; 16 | } 17 | 18 | .miller-cols-container-wrapper { 19 | max-width: 75%; 20 | margin: auto; 21 | position: relative; 22 | z-index: 1; 23 | background-color: #fff; 24 | } 25 | 26 | .miller-cols-body { 27 | display: flex; 28 | display: -ms-flexbox; 29 | display: -webkit-box; 30 | overflow: hidden; 31 | height: 100%; 32 | min-height: 300px; 33 | position: relative; 34 | z-index: 1; 35 | margin: 0 auto; 36 | border-left: 1px solid #DBDBDB; 37 | } 38 | 39 | .miller-col-container, 40 | .miller-col-loading-container { 41 | border: 1px solid #DBDBDB; 42 | border-left: 0; 43 | display: -ms-flexbox; 44 | display: -webkit-box; 45 | display: flex; 46 | -webkit-box-orient: vertical; 47 | -webkit-box-direction: normal; 48 | -ms-flex-direction: column; 49 | flex-direction: column; 50 | -webkit-box-flex: 1; 51 | -ms-flex: 1; 52 | flex: 1; 53 | position: relative; 54 | padding: 0; 55 | overflow-wrap: break-word; 56 | } 57 | 58 | .miller-col-list-item.selected { 59 | background: #eaeaea; 60 | color: rgba(0, 0, 0, 0.9); 61 | } 62 | 63 | .miller-col-loading-container .miller-col-body { 64 | width: 100%; 65 | height: 100%; 66 | } 67 | 68 | .miller-col-body { 69 | -webkit-overflow-scrolling: touch; 70 | margin-top: 48px; 71 | padding-top: 0; 72 | overflow-y: auto; 73 | } 74 | 75 | .miller-col-loading-container.col-loading .loading-icon-container { 76 | position: absolute; 77 | top: 10%; 78 | left: 45%; 79 | height: 5rem; 80 | width: 5rem; 81 | transform: translate(-50%, -50%); 82 | background-size: contain; 83 | } 84 | 85 | .miller-col-nav { 86 | position: absolute; 87 | top: 50%; 88 | transform: translateY(-50%); 89 | } 90 | 91 | .miller-col-nav .material-icons { 92 | font-size: 3em; 93 | font-weight: 300; 94 | float: right; 95 | } 96 | 97 | .miller-col-nav:hover > * { 98 | cursor: pointer; 99 | color: #777; 100 | } 101 | 102 | .miller-col-nav.nav-prev { 103 | left: 0; 104 | transform: translateX(-100%); 105 | } 106 | 107 | .miller-col-nav.nav-next { 108 | right: 0; 109 | transform: translateX(100%); 110 | } 111 | 112 | .miller-col-loading-container.hidden, 113 | .miller-col-container.hidden { 114 | display: none; 115 | } 116 | 117 | .miller-col-nav.hidden { 118 | visibility: hidden; 119 | opacity: 0; 120 | } 121 | 122 | .miller-col-title { 123 | border-bottom: 1px solid #DBDBDB; 124 | text-align: center; 125 | padding: 10px; 126 | font-weight: 900; 127 | -webkit-box-shadow: 0px 5px 4px rgba(0, 0, 0, 0.05); 128 | -moz-box-shadow: 0px 5px 4px rgba(0, 0, 0, 0.05); 129 | box-shadow: 0px 5px 4px rgba(0, 0, 0, 0.05); 130 | position: absolute; 131 | z-index: 10; 132 | height: 48px; 133 | background: #fff; 134 | width: 100%; 135 | } 136 | 137 | .miller-col-title .material-icons{ 138 | position: absolute; 139 | top: 50%; 140 | transform: translateX(-100%) translateY(-50%); 141 | } 142 | 143 | .miller-col-title .material-icons.action-add:hover{ 144 | font-weight: bold; 145 | color: gray; 146 | } 147 | .miller-col-title .material-icons.action-edit{ 148 | padding-right: 30px; 149 | } 150 | .miller-col-title .material-icons.action-edit:hover{ 151 | font-weight: bold; 152 | color: gray; 153 | } 154 | 155 | .miller-col-title-text { 156 | text-transform: uppercase; 157 | font-size: 90%; 158 | font-weight: 700; 159 | letter-spacing: 1px; 160 | } 161 | 162 | .miller-col-actions { 163 | float: right; 164 | } 165 | 166 | .miller-col-actions .material-icons { 167 | cursor: pointer; 168 | display: none; 169 | } 170 | 171 | .miller-col-container, 172 | .miller-col-loading-container { 173 | min-width: 210px; 174 | max-width: 400px; 175 | } 176 | 177 | .miller-col-list-item { 178 | padding: 10px; 179 | font-weight: 300; 180 | display: table; 181 | width: 100%; 182 | color: rgba(0, 0, 0, 0.75); 183 | position: relative; 184 | text-align: left; 185 | word-break: break-all; 186 | min-height: 44px; 187 | } 188 | 189 | .miller-col-list-item .num-children-badge{ 190 | border-radius: 50%; 191 | padding: 5px; 192 | border: 1px solid #ddd; 193 | color: #666; 194 | text-align: center; 195 | position: absolute; 196 | right: 35px; 197 | top: 50%; 198 | transform: translateY(-50%); 199 | } 200 | 201 | .miller-col-list-item > * { 202 | vertical-align: middle; 203 | } 204 | 205 | .miller-col-list-item .list-item-text .list-item-icon { 206 | float: left; 207 | padding-top: 4px; 208 | padding-bottom: 4px; 209 | } 210 | 211 | .list-item-icon{ 212 | margin-right: 10px; 213 | } 214 | 215 | .miller-col-list-item .has-children { 216 | position: absolute; 217 | top: 50%; 218 | transform: translateY(-50%); 219 | right: 15px; 220 | } 221 | 222 | .miller-col-list-item .list-item-actions { 223 | position: absolute; 224 | right: 12px; 225 | display: none; 226 | } 227 | 228 | .miller-col-list-item[data-has-children="true"] .list-item-actions { 229 | right: 40px; 230 | } 231 | 232 | .miller-col-list-item:hover .list-item-actions { 233 | display: inline; 234 | } 235 | 236 | .miller-col-title:hover .miller-col-actions .material-icons { 237 | display: inline; 238 | } 239 | 240 | .miller-col-list-item:hover .material-icons, 241 | .miller-col-list-item.selected .material-icons { 242 | color: #777; 243 | } 244 | 245 | .list-item-actions .material-icons { 246 | padding-left: 8px; 247 | opacity: 0.6; 248 | } 249 | 250 | .list-item-actions .material-icons:hover { 251 | opacity: 1; 252 | } 253 | 254 | .miller-col-list-item:hover { 255 | background: rgba(0, 0, 0, 0.05); 256 | cursor: pointer; 257 | } 258 | 259 | .text-node { 260 | white-space: nowrap; 261 | } 262 | 263 | 264 | /* 265 | Spinner style. 266 | */ 267 | 268 | .spinner { 269 | min-width: 24px; 270 | min-height: 24px; 271 | } 272 | 273 | .spinner:before { 274 | content: 'Loading…'; 275 | position: absolute; 276 | top: 50%; 277 | left: 50%; 278 | width: 16px; 279 | height: 16px; 280 | margin-top: -10px; 281 | margin-left: -10px; 282 | } 283 | 284 | .spinner:not(:required):before { 285 | content: ''; 286 | border-radius: 50%; 287 | border: 2px solid rgba(0, 0, 0, .3); 288 | border-top-color: rgba(0, 0, 0, .6); 289 | animation: spinner .6s linear infinite; 290 | -webkit-animation: spinner .6s linear infinite; 291 | } 292 | 293 | @keyframes spinner { 294 | to { 295 | transform: rotate(360deg); 296 | } 297 | } 298 | 299 | @-webkit-keyframes spinner { 300 | to { 301 | -webkit-transform: rotate(360deg); 302 | } 303 | } 304 | 305 | /* hide badge icon when user hovers on list item being on editable mode */ 306 | 307 | .miller-cols-container-wrapper[data-is-read-only="false"] .miller-col-list-item:hover .num-children-badge{ 308 | display: none; 309 | } 310 | -------------------------------------------------------------------------------- /css/miller.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsharew/responsive-miller-column/06ac4b708675b9ee7cab5a2f5f11d2d77df5962d/demo/.DS_Store -------------------------------------------------------------------------------- /demo/css/demo.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 16px; 3 | } 4 | 5 | * { 6 | box-sizing: border-box; 7 | padding: 0; 8 | margin: 0; 9 | -webkit-font-smoothing: antialiased; 10 | text-rendering: optimizeLegibility; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | 14 | body { 15 | font-family: "Roboto", sans-serif; 16 | font-weight: normal; 17 | font-size: 100%; 18 | color: #222; 19 | background: #fff; 20 | font-weight: 300; 21 | } 22 | 23 | .miller-cols-container-wrapper { 24 | margin-top: 10px; 25 | box-shadow: rgba(0, 0, 0, 0.10) 0px 15px 40px 0px; 26 | } 27 | 28 | .miller-col-body { 29 | padding: 10px 0; 30 | padding-top: 0; 31 | } 32 | 33 | .miller-col-list-item .list-item-text { 34 | font-family: "Roboto-light", sans-serif; 35 | } 36 | 37 | .miller-col-title { 38 | padding: 15px 15px; 39 | } 40 | 41 | .miller-col-title-text { 42 | font-family: "Roboto-bold", sans-serif; 43 | text-transform: uppercase; 44 | font-size: 90%; 45 | font-weight: 700; 46 | letter-spacing: 1px; 47 | } 48 | 49 | .material-icons { 50 | color: #bbb; 51 | -webkit-transition: 300ms; 52 | -moz-transition: 300ms; 53 | -ms-transition: 300ms; 54 | transition: 300ms; 55 | } 56 | 57 | .miller-col-nav .material-icons { 58 | font-size: 2.85em; 59 | } 60 | 61 | .spinner-wrapper{ 62 | position: relative; 63 | margin-top: 40px; 64 | } 65 | 66 | /* style for editable miller col feature*/ 67 | 68 | .popup-wrapper { 69 | background-color: #fff; 70 | border: 1px solid #ddd; 71 | border-radius: 5px; 72 | box-shadow: 0 2px 8px #aaa; 73 | overflow: hidden; 74 | top: 100px; 75 | } 76 | .popup-title { 77 | padding: 10px 15px; 78 | background-color: #f4f4f4; 79 | border-bottom: 1px solid #f0f0f0; 80 | } 81 | .popup-title h3 { 82 | margin: 0; 83 | line-height: 1.5em; 84 | color: #333; 85 | } 86 | .popup-body { 87 | padding: 10px 15px; 88 | color: #555; 89 | } 90 | .popup-close, .button, .popup-wrapper .material-icons { 91 | float: right; 92 | margin-top: 2px; 93 | padding: 0; 94 | font-size: 24px; 95 | line-height: 1; 96 | border: 0; 97 | background: transparent; 98 | color: #aaa; 99 | cursor: pointer; 100 | } 101 | .popup-close:hover, 102 | .button:hover, 103 | .popup-wrapper .material-icons:hover{ 104 | color: #333; 105 | } 106 | 107 | .popup-wrapper .footer{ 108 | 109 | padding: 10px 0px; 110 | 111 | } 112 | 113 | .clearfix{ 114 | display: block; 115 | clear: both; 116 | } 117 | 118 | input{ 119 | display: block; 120 | width: 90%; 121 | height: 34px; 122 | padding: 6px 12px; 123 | font-size: 14px; 124 | line-height: 1.42857143; 125 | color: #555; 126 | background-color: #fff; 127 | background-image: none; 128 | border: 1px solid #ccc; 129 | border-radius: 4px; 130 | float: right; 131 | } 132 | 133 | input:focus{ 134 | border-color: #ccc; 135 | outline: 0; 136 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(200,200,200,.6); 137 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(200,200,200,.6); 138 | } 139 | 140 | .middle-body { 141 | position: relative; 142 | } 143 | 144 | .middle-body .material-icons{ 145 | width: 10%; 146 | float: left; 147 | position: absolute; 148 | top: 50%; 149 | transform: translateY(-50%); 150 | } 151 | 152 | .dropdown-content .material-icons{ 153 | width: 30px; 154 | position: relative; 155 | transform: translateX(25%); 156 | } 157 | 158 | /*style for switch button */ 159 | /* GLOBALS */ 160 | 161 | *, 162 | *:after, 163 | *:before { 164 | -webkit-box-sizing: border-box; 165 | -moz-box-sizing: border-box; 166 | box-sizing: border-box; 167 | padding: 0; 168 | margin: 0; 169 | } 170 | 171 | .switch { 172 | margin: 50px auto; 173 | position: relative; 174 | } 175 | 176 | .switch label { 177 | width: 100%; 178 | height: 100%; 179 | position: relative; 180 | display: block; 181 | } 182 | 183 | .switch input { 184 | top: 0; 185 | right: 0; 186 | bottom: 0; 187 | left: 0; 188 | -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 189 | filter: alpha(opacity=0); 190 | -moz-opacity: 0; 191 | opacity: 0; 192 | z-index: 100; 193 | position: absolute; 194 | width: 100%; 195 | height: 100%; 196 | cursor: pointer; 197 | } 198 | 199 | .switch.demo3 { 200 | width: 180px; 201 | height: 50px; 202 | text-align: center; 203 | } 204 | 205 | .switch.demo3 label { 206 | display: block; 207 | width: 100%; 208 | height: 100%; 209 | background: #a5a39d; 210 | border-radius: 40px; 211 | } 212 | 213 | .switch.demo3 label:after { 214 | content: ""; 215 | position: absolute; 216 | z-index: -1; 217 | top: -8px; right: -8px; bottom: -8px; left: -8px; 218 | border-radius: inherit; 219 | } 220 | 221 | .switch.demo3 label:before { 222 | content: ""; 223 | position: absolute; 224 | z-index: -1; 225 | top: -18px; right: -18px; bottom: -18px; left: -18px; 226 | border-radius: inherit; 227 | box-shadow: 228 | 0 1px 0 rgba(255,255,255,0.5); 229 | -webkit-filter: blur(1px); 230 | -moz-filter: blur(1px); 231 | -ms-filter: blur(1px); 232 | -o-filter: blur(1px); 233 | filter: blur(1px); 234 | } 235 | 236 | .switch.demo3 label i { 237 | display: block; 238 | height: 100%; 239 | width: 60%; 240 | border-radius: inherit; 241 | background: silver; 242 | position: absolute; 243 | z-index: 2; 244 | right: 40%; 245 | top: 0; 246 | box-shadow: 247 | inset 0 1px 0 white, 248 | 0 0 8px rgba(0,0,0,0.3), 249 | 0 5px 5px rgba(0,0,0,0.2); 250 | } 251 | 252 | .switch.demo3 label i:after { 253 | content: ""; 254 | position: absolute; 255 | left: 15%; 256 | top: 25%; 257 | width: 70%; 258 | height: 50%; 259 | border-radius: inherit; 260 | } 261 | 262 | .switch.demo3 label i:before { 263 | content: "off"; 264 | text-transform: uppercase; 265 | font-style: normal; 266 | font-weight: bold; 267 | color: rgba(0,0,0,0.4); 268 | text-shadow: 0 1px 0 #bcb8ae, 0 -1px 0 #97958e; 269 | font-family: Helvetica, Arial, sans-serif; 270 | font-size: 24px; 271 | position: absolute; 272 | top: 50%; 273 | margin-top: -12px; 274 | right: -50%; 275 | } 276 | 277 | .switch.demo3 input:checked ~ label { 278 | background: #9abb82; 279 | } 280 | 281 | .switch.demo3 input:checked ~ label i { 282 | right: -1%; 283 | } 284 | 285 | .switch.demo3 input:checked ~ label i:before { 286 | content: "on"; 287 | right: 115%; 288 | color: #82a06a; 289 | text-shadow: 290 | 0 1px 0 #afcb9b, 291 | 0 -1px 0 #6b8659; 292 | } -------------------------------------------------------------------------------- /demo/css/simple-dropdown.css: -------------------------------------------------------------------------------- 1 | 2 | .dropbtn { 3 | background-color: #4CAF50; 4 | color: white; 5 | padding: 16px; 6 | font-size: 16px; 7 | border: none; 8 | cursor: pointer; 9 | } 10 | 11 | .dropbtn:hover, .dropbtn:focus { 12 | background-color: white; 13 | } 14 | 15 | .dropdown { 16 | position: relative; 17 | display: inline-block; 18 | } 19 | 20 | .dropdown-content { 21 | display: none; 22 | position: absolute; 23 | background-color: #f9f9f9; 24 | min-width: 160px; 25 | box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); 26 | } 27 | 28 | .dropdown-content i { 29 | color: black; 30 | padding: 12px 16px; 31 | text-decoration: none; 32 | display: block; 33 | } 34 | 35 | .dropdown-content i:hover {background-color: #f1f1f1} 36 | 37 | .show {display:block;} -------------------------------------------------------------------------------- /demo/js/demo.js: -------------------------------------------------------------------------------- 1 | var CONSTANT_MAX_NUMBER_CATEGORIES = 6; 2 | var iconList = ["clear", "store", "call", "wifi", "portrait"]; 3 | 4 | function findCategoryByParentId(categoriesCollection, parentId) { 5 | 6 | var category = categoriesCollection.findOne({ 7 | parentId: parentId 8 | }); 9 | 10 | //delete inactive functions pulled from db 11 | for (k in category) { 12 | if (typeof category[k] == 'undefined') delete category[i]; 13 | } 14 | 15 | //copy Category model functions to category (db object) 16 | category = $.extend(new Category(), category); 17 | 18 | return category; 19 | } 20 | 21 | function initDemoData(db, categoriesCollection, categoryItemCollection) { 22 | 23 | var categoryParentId = null; 24 | var categoryItemParentId = null; 25 | 26 | for (var i = 1; i <= CONSTANT_MAX_NUMBER_CATEGORIES; ++i) { 27 | 28 | var category = new Category(); 29 | 30 | category.setCategoryName("Category " + i); 31 | category.setParentId(categoryParentId); 32 | category.setIsLowestLevel(false); 33 | 34 | if (CONSTANT_MAX_NUMBER_CATEGORIES == i) category.setIsLowestLevel(true); 35 | 36 | categoriesCollection.insert(category); 37 | 38 | categoryParentId = category.getCategoryId(); 39 | } 40 | 41 | console.log(categoriesCollection.find()); 42 | 43 | var rootCategory = findCategoryByParentId(categoriesCollection, null); 44 | 45 | createChildrenCategoryItems(categoryItemCollection, categoriesCollection, rootCategory, null, 20); 46 | 47 | } 48 | 49 | function createChildrenCategoryItems(categoryItemCollection, categoriesCollection, category, parentCategoryItem, numChildren) { 50 | 51 | if (category && 0 == numChildren) return; 52 | 53 | for (var i = 1; i <= numChildren; ++i) { 54 | 55 | var categoryItem = new CategoryItem(); 56 | 57 | var numChildren2 = parseInt(Math.random() * 6); 58 | 59 | if (category.getIsLowestLevel()) numChildren2 = 0; 60 | 61 | categoryItem.setItemName(category.getCategoryName() + " item " + i); 62 | categoryItem.setItemIcon(iconList[parseInt(Math.random() * 6 - 1)]); 63 | categoryItem.setHasChildren(numChildren2 != 0); 64 | categoryItem.setNumChildren(numChildren2); 65 | categoryItem.setCategoryId(category.getCategoryId()); 66 | 67 | var parentCategoryItemId = parentCategoryItem == null ? null : parentCategoryItem.getItemId(); 68 | categoryItem.setParentId(parentCategoryItemId); 69 | 70 | categoryItemCollection.insert(categoryItem); 71 | 72 | var childCategory = findCategoryByParentId(categoriesCollection, category.getCategoryId()); 73 | 74 | if (!category.getIsLowestLevel()) 75 | createChildrenCategoryItems(categoryItemCollection, categoriesCollection, childCategory, categoryItem, numChildren2); 76 | 77 | } 78 | 79 | } 80 | 81 | function createDialog($dialogBodyContent, dialogTitle){ 82 | 83 | //remove prev popup instances 84 | $("#popup").remove(); 85 | 86 | var $dialog = $("
").attr("id", "popup").addClass("popup-wrapper hide"); 87 | var $dialogContent = $("
").addClass("popup-content"); 88 | var $dialogTitle = $("
").addClass("popup-title"); 89 | var $btnClose = $("