diff --git a/apimocker_config.json b/apimocker_config.json index 0bae76a1b..273526d6f 100644 --- a/apimocker_config.json +++ b/apimocker_config.json @@ -61,6 +61,10 @@ "verbs": ["GET"], "mockFile": "datagrid/children/sorted.json" }, + "admin/api/datagrid/fields": { + "verbs": ["GET"], + "mockFile": "datagrid/fields.json" + }, "admin/api/autocomplete": { "verbs": ["GET"], diff --git a/apimocks/datagrid/fields.json b/apimocks/datagrid/fields.json new file mode 100644 index 000000000..97696b21c --- /dev/null +++ b/apimocks/datagrid/fields.json @@ -0,0 +1,55 @@ +[{ + "name":"id", + "translation":"public.id", + "disabled":true, + "default":false, + "sortable":true, + "type":"", + "width":"", + "minWidth":"", + "editable":false +}, +{ + "name":"content1", + "translation":"Content 1", + "disabled":false, + "default":false, + "sortable":true, + "type":"title", + "width":"25%", + "minWidth":"", + "editable":false +}, +{ + "name":"content2", + "translation":"Content 2", + "disabled":false, + "default":false, + "sortable":true, + "type":"title", + "width":"25%", + "minWidth":"", + "editable":false +}, +{ + "name":"content3", + "translation":"Content 3", + "disabled":false, + "default":false, + "sortable":true, + "type":"bytes", + "width":"25%", + "minWidth":"", + "editable":false +}, +{ + "name":"date", + "translation":"Date", + "disabled":false, + "default":false, + "sortable":true, + "type":"", + "width":"25%", + "minWidth":"", + "editable":false +}] diff --git a/bower.json b/bower.json index 41dc2b976..1cb89a3f5 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "husky", - "version": "0.6.5", + "version": "0.7.0", "main": "js/husky.js", "dependencies": { "backbone": "~1.0.0", diff --git a/demos/datagrid/script.js b/demos/datagrid/script.js index 131713d74..62eb24f68 100644 --- a/demos/datagrid/script.js +++ b/demos/datagrid/script.js @@ -37,9 +37,11 @@ require(['lib/husky'], function(Husky) { validation: true, addRowTop: true, showHead: true, // false to hide table-head - //fullWidth: true, // uncomment for full-width mode + fullWidth: true, // uncomment for full-width mode contentContainer: '#content', - highlightSelected: true + highlightSelected: true, + croppedMaxLength: 5, + stickyHeader: true }, thumbnail: { } @@ -49,43 +51,7 @@ require(['lib/husky'], function(Husky) { searchFields: ['fullName'], columnOptionsInstanceName: '', el: '#datagrid', - matchings: [ - { - content: 'Content 1', - width: "25%", - name: "content1", - editable: true, - sortable: true, - type: 'title', - validation: { - required: true - } - }, - { - content: 'Content 2', - width: "25%", - name: "content2", - editable: false, - sortable: true, - type: 'bytes', - validation: { - required: true - } - }, - { - content: 'Content 3', - width: "25%", - name: "content3", - type: 'thumbnails' - }, - { - content: 'Date', - width: "25%", - sortable: true, - name: 'date', - type: 'date' - } - ] + matchings: 'http://husky.lo:7878/admin/api/datagrid/fields' } }, { diff --git a/demos/input/index.html b/demos/input/index.html index 88e31d561..93ca96677 100644 --- a/demos/input/index.html +++ b/demos/input/index.html @@ -89,6 +89,7 @@

Husky input demo

Husky input demo
+ +
+
+
+ +
+
+
+
+
diff --git a/demos/overlay/index.html b/demos/overlay/index.html index ca3235747..114607514 100644 --- a/demos/overlay/index.html +++ b/demos/overlay/index.html @@ -303,7 +303,8 @@ sizeRelativeTo: '#overlaySlides .slide-0 .overlay-content', wrapper: {height: 100}, showEdit: false, - showStatus: false + showStatus: false, + responsive: false } } ] diff --git a/demos/select/index.html b/demos/select/index.html index fdc8f5ac2..78af2248a 100644 --- a/demos/select/index.html +++ b/demos/select/index.html @@ -510,7 +510,7 @@

Husky Select Demo

console.log(form.mapper.getData()); }); - $('#ddms5').on('changed', function() { + $('#ddms5').on('change', function() { alert("i got changed"); }); }); diff --git a/dist/husky.css b/dist/husky.css index c67960f14..c639d8909 100644 --- a/dist/husky.css +++ b/dist/husky.css @@ -652,9 +652,10 @@ table { overflow: hidden; width: 100%; white-space: nowrap; + cursor: pointer; } -/* line 76, ../scss/utilities.scss */ +/* line 77, ../scss/utilities.scss */ .editable:focus, .editable:active { background: white; @@ -662,208 +663,208 @@ table { outline: 0; } -/* line 82, ../scss/utilities.scss */ +/* line 83, ../scss/utilities.scss */ .editable:hover { border: 1px solid #cccccc; outline: 0; } -/* line 89, ../scss/utilities.scss */ +/* line 90, ../scss/utilities.scss */ .clickable { color: black !important; } -/* line 93, ../scss/utilities.scss */ +/* line 94, ../scss/utilities.scss */ .faded { opacity: 0.3; } -/* line 102, ../scss/utilities.scss */ +/* line 103, ../scss/utilities.scss */ .m-top-0 { margin-top: 0px !important; } -/* line 105, ../scss/utilities.scss */ +/* line 106, ../scss/utilities.scss */ .p-top-0 { padding-top: 0px !important; } -/* line 108, ../scss/utilities.scss */ +/* line 109, ../scss/utilities.scss */ .m-bottom-0 { margin-bottom: 0px !important; } -/* line 111, ../scss/utilities.scss */ +/* line 112, ../scss/utilities.scss */ .p-bottom-0 { padding-bottom: 0px !important; } -/* line 116, ../scss/utilities.scss */ +/* line 117, ../scss/utilities.scss */ .m-top-5 { margin-top: 5px !important; } -/* line 119, ../scss/utilities.scss */ +/* line 120, ../scss/utilities.scss */ .p-top-5 { padding-top: 5px !important; } -/* line 123, ../scss/utilities.scss */ +/* line 124, ../scss/utilities.scss */ .m-bottom-5 { margin-bottom: 5px !important; } -/* line 126, ../scss/utilities.scss */ +/* line 127, ../scss/utilities.scss */ .p-bottom-5 { padding-bottom: 5px !important; } -/* line 130, ../scss/utilities.scss */ +/* line 131, ../scss/utilities.scss */ .m-right-5 { margin-right: 5px !important; } -/* line 133, ../scss/utilities.scss */ +/* line 134, ../scss/utilities.scss */ .p-right-5 { padding-right: 5px !important; } -/* line 137, ../scss/utilities.scss */ +/* line 138, ../scss/utilities.scss */ .m-left-5 { margin-left: 5px !important; } -/* line 140, ../scss/utilities.scss */ +/* line 141, ../scss/utilities.scss */ .p-left-5 { padding-left: 5px !important; } -/* line 145, ../scss/utilities.scss */ +/* line 146, ../scss/utilities.scss */ .m-top-10 { margin-top: 10px !important; } -/* line 149, ../scss/utilities.scss */ +/* line 150, ../scss/utilities.scss */ .m-bottom-10 { margin-bottom: 10px !important; } -/* line 153, ../scss/utilities.scss */ +/* line 154, ../scss/utilities.scss */ .m-right-10 { margin-right: 10px !important; } -/* line 157, ../scss/utilities.scss */ +/* line 158, ../scss/utilities.scss */ .m-left-10 { margin-left: 10px !important; } -/* line 162, ../scss/utilities.scss */ +/* line 163, ../scss/utilities.scss */ .p-top-10 { padding-top: 10px !important; } -/* line 167, ../scss/utilities.scss */ +/* line 168, ../scss/utilities.scss */ .m-top-15 { margin-top: 15px !important; } -/* line 171, ../scss/utilities.scss */ +/* line 172, ../scss/utilities.scss */ .m-bottom-15 { margin-bottom: 15px !important; } -/* line 175, ../scss/utilities.scss */ +/* line 176, ../scss/utilities.scss */ .m-right-15 { margin-right: 15px !important; } -/* line 179, ../scss/utilities.scss */ +/* line 180, ../scss/utilities.scss */ .m-left-15 { margin-left: 15px !important; } -/* line 184, ../scss/utilities.scss */ +/* line 185, ../scss/utilities.scss */ .m-top-20 { margin-top: 20px !important; } -/* line 188, ../scss/utilities.scss */ +/* line 189, ../scss/utilities.scss */ .m-bottom-20 { margin-bottom: 20px !important; } -/* line 192, ../scss/utilities.scss */ +/* line 193, ../scss/utilities.scss */ .m-right-20 { margin-right: 20px !important; } -/* line 196, ../scss/utilities.scss */ +/* line 197, ../scss/utilities.scss */ .m-left-20 { margin-left: 20px !important; } -/* line 202, ../scss/utilities.scss */ +/* line 203, ../scss/utilities.scss */ .m-top-25 { margin-top: 25px !important; } -/* line 206, ../scss/utilities.scss */ +/* line 207, ../scss/utilities.scss */ .m-bottom-25 { margin-bottom: 25px !important; } -/* line 210, ../scss/utilities.scss */ +/* line 211, ../scss/utilities.scss */ .m-right-25 { margin-right: 25px !important; } -/* line 214, ../scss/utilities.scss */ +/* line 215, ../scss/utilities.scss */ .m-left-25 { margin-left: 25px !important; } -/* line 219, ../scss/utilities.scss */ +/* line 220, ../scss/utilities.scss */ .m-top-30 { margin-top: 30px !important; } -/* line 223, ../scss/utilities.scss */ +/* line 224, ../scss/utilities.scss */ .m-bottom-30 { margin-bottom: 30px !important; } -/* line 227, ../scss/utilities.scss */ +/* line 228, ../scss/utilities.scss */ .m-right-30 { margin-right: 30px !important; } -/* line 231, ../scss/utilities.scss */ +/* line 232, ../scss/utilities.scss */ .m-left-30 { margin-left: 30px !important; } -/* line 236, ../scss/utilities.scss */ +/* line 237, ../scss/utilities.scss */ .m-top-40 { margin-top: 40px !important; } -/* line 240, ../scss/utilities.scss */ +/* line 241, ../scss/utilities.scss */ .m-bottom-40 { margin-bottom: 40px !important; } -/* line 244, ../scss/utilities.scss */ +/* line 245, ../scss/utilities.scss */ .m-right-40 { margin-right: 40px !important; } -/* line 248, ../scss/utilities.scss */ +/* line 249, ../scss/utilities.scss */ .m-left-40 { margin-left: 40px !important; } -/* line 256, ../scss/utilities.scss */ +/* line 257, ../scss/utilities.scss */ .m-height-25 { height: 25px !important; } @@ -2984,7 +2985,7 @@ fieldset[disabled] .datepicker table tr td span.active.disabled.active, fieldset } /* line 4, ../scss/fontsawesome/_core.scss */ -.fa, [class^="fa-"], .custom-checkbox input:checked + .icon::after, .custom-checkbox input.is-checked + .icon::after, .navigation-item-container .navigation-items li:hover:not(.navigation-subitems) a::before, .navigation-item-container .navigation-items li.is-selected a::before, .addButton, .pagination-wrapper.dropdowns .pagination .pagination-prev::after, .pagination-wrapper.dropdowns .pagination .pagination-next::after, .auto-complete-list-selection .tm-tag-remove, .husky-datagrid .table-container.overflow:after, .husky-matcher.overflow .wrapper:after, .sortable .text-block .head label::before { +.fa, [class^="fa-"], .custom-checkbox input:checked + .icon::after, .custom-checkbox input.is-checked + .icon::after, .navigation-item-container .navigation-items li:hover:not(.navigation-subitems) a::before, .navigation-item-container .navigation-items li.is-selected a::before, .addButton, .pagination-wrapper.dropdowns .pagination .pagination-prev::after, .pagination-wrapper.dropdowns .pagination .pagination-next::after, .auto-complete-list-selection .tm-tag-remove, .husky-datagrid .husky-table.overflow:after, .husky-datagrid thead tr th.sorted-asc .cell-content::after, .husky-datagrid thead tr th.sorted-desc .cell-content::after, .husky-matcher.overflow .wrapper:after, .sortable .text-block .head label::before { display: inline-block; font-family: FontAwesome; font-style: normal; @@ -3070,11 +3071,11 @@ fieldset[disabled] .datepicker table tr td span.active.disabled.active, fieldset } /* line 14, ../scss/fontsawesome/_bordered-pulled.scss */ -.fa.pull-left, .pull-left[class^="fa-"], .custom-checkbox input:checked + .pull-left.icon::after, .custom-checkbox input.is-checked + .pull-left.icon::after, .navigation-item-container .navigation-items li:hover:not(.navigation-subitems) a.pull-left::before, .navigation-item-container .navigation-items li.is-selected a.pull-left::before, .pull-left.addButton, .pagination-wrapper.dropdowns .pagination .pull-left.pagination-prev::after, .pagination-wrapper.dropdowns .pagination .pull-left.pagination-next::after, .auto-complete-list-selection .pull-left.tm-tag-remove, .husky-datagrid .pull-left.table-container.overflow:after, .husky-matcher.overflow .pull-left.wrapper:after, .sortable .text-block .head label.pull-left::before { +.fa.pull-left, .pull-left[class^="fa-"], .custom-checkbox input:checked + .pull-left.icon::after, .custom-checkbox input.is-checked + .pull-left.icon::after, .navigation-item-container .navigation-items li:hover:not(.navigation-subitems) a.pull-left::before, .navigation-item-container .navigation-items li.is-selected a.pull-left::before, .pull-left.addButton, .pagination-wrapper.dropdowns .pagination .pull-left.pagination-prev::after, .pagination-wrapper.dropdowns .pagination .pull-left.pagination-next::after, .auto-complete-list-selection .pull-left.tm-tag-remove, .husky-datagrid .pull-left.husky-table.overflow:after, .husky-datagrid thead tr th.sorted-asc .pull-left.cell-content::after, .husky-datagrid thead tr th.sorted-desc .pull-left.cell-content::after, .husky-matcher.overflow .pull-left.wrapper:after, .sortable .text-block .head label.pull-left::before { margin-right: .3em; } /* line 15, ../scss/fontsawesome/_bordered-pulled.scss */ -.fa.pull-right, .pull-right[class^="fa-"], .custom-checkbox input:checked + .pull-right.icon::after, .custom-checkbox input.is-checked + .pull-right.icon::after, .navigation-item-container .navigation-items li:hover:not(.navigation-subitems) a.pull-right::before, .navigation-item-container .navigation-items li.is-selected a.pull-right::before, .pull-right.addButton, .pagination-wrapper.dropdowns .pagination .pull-right.pagination-prev::after, .pagination-wrapper.dropdowns .pagination .pull-right.pagination-next::after, .auto-complete-list-selection .pull-right.tm-tag-remove, .husky-datagrid .pull-right.table-container.overflow:after, .husky-matcher.overflow .pull-right.wrapper:after, .sortable .text-block .head label.pull-right::before { +.fa.pull-right, .pull-right[class^="fa-"], .custom-checkbox input:checked + .pull-right.icon::after, .custom-checkbox input.is-checked + .pull-right.icon::after, .navigation-item-container .navigation-items li:hover:not(.navigation-subitems) a.pull-right::before, .navigation-item-container .navigation-items li.is-selected a.pull-right::before, .pull-right.addButton, .pagination-wrapper.dropdowns .pagination .pull-right.pagination-prev::after, .pagination-wrapper.dropdowns .pagination .pull-right.pagination-next::after, .auto-complete-list-selection .pull-right.tm-tag-remove, .husky-datagrid .pull-right.husky-table.overflow:after, .husky-datagrid thead tr th.sorted-asc .pull-right.cell-content::after, .husky-datagrid thead tr th.sorted-desc .pull-right.cell-content::after, .husky-matcher.overflow .pull-right.wrapper:after, .sortable .text-block .head label.pull-right::before { margin-left: .3em; } @@ -5750,17 +5751,18 @@ fieldset[disabled] .datepicker table tr td span.active.disabled.active, fieldset /* line 184, ../scss/modules/overlay.scss */ .overlay-content { + overflow: auto; padding-left: 0; margin: 0 20px 10px 20px; } -/* line 188, ../scss/modules/overlay.scss */ +/* line 189, ../scss/modules/overlay.scss */ .overlay-content .message { padding: 20px 0 25px 0; font-size: 16px; color: #999999; } -/* line 195, ../scss/modules/overlay.scss */ +/* line 196, ../scss/modules/overlay.scss */ .husky-overlay-backdrop { width: 100%; height: 100%; @@ -7358,25 +7360,9 @@ table { max-width: 50px; } /* line 64, ../scss/modules/tables.scss */ -.table.is-selectable tr { - cursor: pointer; -} -/* line 66, ../scss/modules/tables.scss */ -.table.is-selectable tr:hover > td { - background-color: #fff; -} -/* line 71, ../scss/modules/tables.scss */ -.table.is-selectable thead tr { - cursor: auto; -} -/* line 78, ../scss/modules/tables.scss */ .table .thumb img { vertical-align: middle; } -/* line 83, ../scss/modules/tables.scss */ -.table .remove-row { - text-align: center; -} /* line 5, ../scss/modules/btns.scss */ .btn { @@ -7548,20 +7534,27 @@ a.btn-black, a.btn-highlight { content: ' '; display: block; } -/* line 11, ../scss/modules/pagination.scss */ +/* line 10, ../scss/modules/pagination.scss */ +.pagination-wrapper .pagination-loader { + width: auto; + overflow: hidden; + text-align: center; + padding-top: 5px; +} +/* line 18, ../scss/modules/pagination.scss */ .pagination-wrapper.dropdowns .show-elements { float: left; } -/* line 15, ../scss/modules/pagination.scss */ +/* line 22, ../scss/modules/pagination.scss */ .pagination-wrapper.dropdowns .dropdown-menu { width: 200px; } -/* line 19, ../scss/modules/pagination.scss */ +/* line 26, ../scss/modules/pagination.scss */ .pagination-wrapper.dropdowns .pagination { height: 30px; float: right; } -/* line 23, ../scss/modules/pagination.scss */ +/* line 30, ../scss/modules/pagination.scss */ .pagination-wrapper.dropdowns .pagination .pagination-main { margin: 0; display: inline-block; @@ -7572,7 +7565,7 @@ a.btn-black, a.btn-highlight { line-height: 28px; height: 30px; } -/* line 34, ../scss/modules/pagination.scss */ +/* line 41, ../scss/modules/pagination.scss */ .pagination-wrapper.dropdowns .pagination .pagination-prev { padding: 0 13px 0 8px; border: 1px solid #cccccc; @@ -7581,13 +7574,13 @@ a.btn-black, a.btn-highlight { height: 30px; text-align: center; } -/* line 42, ../scss/modules/pagination.scss */ +/* line 49, ../scss/modules/pagination.scss */ .pagination-wrapper.dropdowns .pagination .pagination-prev::after { content: "\f054"; display: inline-block; font-size: 16px; } -/* line 50, ../scss/modules/pagination.scss */ +/* line 57, ../scss/modules/pagination.scss */ .pagination-wrapper.dropdowns .pagination .pagination-next { padding: 0 13px 0 8px; border: 1px solid #cccccc; @@ -7596,27 +7589,27 @@ a.btn-black, a.btn-highlight { height: 30px; text-align: center; } -/* line 58, ../scss/modules/pagination.scss */ +/* line 65, ../scss/modules/pagination.scss */ .pagination-wrapper.dropdowns .pagination .pagination-next::after { content: "\f053"; display: inline-block; font-size: 16px; } -/* line 68, ../scss/modules/pagination.scss */ +/* line 75, ../scss/modules/pagination.scss */ .pagination-wrapper.showall { cursor: pointer; margin-top: 0; padding-left: 10px; display: inline-block; } -/* line 74, ../scss/modules/pagination.scss */ +/* line 81, ../scss/modules/pagination.scss */ .pagination-wrapper.showall .square { width: 50px; height: 50px; border: 1px dashed #cccccc; float: left; } -/* line 80, ../scss/modules/pagination.scss */ +/* line 87, ../scss/modules/pagination.scss */ .pagination-wrapper.showall .text { display: inline-block; padding-left: 10px; @@ -8419,12 +8412,13 @@ a.btn-black, a.btn-highlight { position: relative; top: 0px; left: 0px; - min-width: 100%; + width: 100%; height: 100%; } /* line 21, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation { height: 100%; + width: 100%; background-color: white; border-style: solid; border-color: #cccccc; @@ -8435,11 +8429,11 @@ a.btn-black, a.btn-highlight { min-height: 200px; max-height: 750px; } -/* line 34, ../scss/modules/column-navigation.scss */ +/* line 35, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation.overflow .column:last-child { border-right: none; } -/* line 39, ../scss/modules/column-navigation.scss */ +/* line 40, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column-navigation-loader { position: absolute; left: 0; @@ -8447,7 +8441,7 @@ a.btn-black, a.btn-highlight { margin-top: -70px; width: 100%; } -/* line 47, ../scss/modules/column-navigation.scss */ +/* line 48, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column { border-right: 1px solid #cccccc; overflow-y: auto; @@ -8459,7 +8453,7 @@ a.btn-black, a.btn-highlight { top: 0px; left: 0px; } -/* line 58, ../scss/modules/column-navigation.scss */ +/* line 59, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column .no-page { position: absolute; top: 50%; @@ -8470,25 +8464,25 @@ a.btn-black, a.btn-highlight { height: 160px; margin-top: -55px; } -/* line 68, ../scss/modules/column-navigation.scss */ +/* line 69, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column .no-page .icon { font-size: 72px; line-height: 80px; } -/* line 72, ../scss/modules/column-navigation.scss */ +/* line 73, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column .no-page .text { padding-top: 5px; font-size: 14px; font-weight: bold; } -/* line 79, ../scss/modules/column-navigation.scss */ +/* line 80, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul { list-style-type: none; margin: 0; padding: 0; font-size: 14px; } -/* line 85, ../scss/modules/column-navigation.scss */ +/* line 86, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul li { padding: 15px 11px 15px 15px; height: 50px; @@ -8501,52 +8495,52 @@ a.btn-black, a.btn-highlight { -ms-user-select: none; -o-user-select: none; } -/* line 94, ../scss/modules/column-navigation.scss */ +/* line 95, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul li:hover { background-color: #f1f1f1; } -/* line 96, ../scss/modules/column-navigation.scss */ +/* line 97, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul li:hover .edit { display: block; } -/* line 100, ../scss/modules/column-navigation.scss */ +/* line 101, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul li.selected { background-color: #f1f1f1; } -/* line 103, ../scss/modules/column-navigation.scss */ +/* line 104, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul li.selected .item-text { font-weight: bold; } -/* line 108, ../scss/modules/column-navigation.scss */ +/* line 109, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul li.marked .markedIcon { display: block; } -/* line 111, ../scss/modules/column-navigation.scss */ +/* line 112, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul li.marked .edit { display: none; } -/* line 116, ../scss/modules/column-navigation.scss */ +/* line 117, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul .inactive { color: #cccccc; } -/* line 120, ../scss/modules/column-navigation.scss */ +/* line 121, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .column ul .not-published { color: #e8ce33; margin-top: 2px; font-size: 40px; } -/* line 128, ../scss/modules/column-navigation.scss */ +/* line 129, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .item-text { padding-top: 2px; display: block; overflow: hidden; word-wrap: break-word; } -/* line 135, ../scss/modules/column-navigation.scss */ +/* line 136, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .item-text.ghost { color: #999999; } -/* line 139, ../scss/modules/column-navigation.scss */ +/* line 140, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .ghost { background: #52b6ca; padding: 0 2px; @@ -8556,7 +8550,7 @@ a.btn-black, a.btn-highlight { border-radius: 3px; font-size: 10px; } -/* line 148, ../scss/modules/column-navigation.scss */ +/* line 149, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .markedIcon { width: 14px; margin-right: 5px; @@ -8564,39 +8558,39 @@ a.btn-black, a.btn-highlight { color: #999999; display: none; } -/* line 155, ../scss/modules/column-navigation.scss */ +/* line 156, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .icons-right { position: absolute; right: 5px; top: 15px; height: 20px; } -/* line 160, ../scss/modules/column-navigation.scss */ +/* line 161, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .icons-right * { line-height: 20px; } -/* line 164, ../scss/modules/column-navigation.scss */ +/* line 165, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .icons-left { display: block; float: left; height: 20px; font-size: 14px; } -/* line 169, ../scss/modules/column-navigation.scss */ +/* line 170, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .icons-left > * { line-height: 20px; } -/* line 172, ../scss/modules/column-navigation.scss */ +/* line 173, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .icons-left .ghost { line-height: 15px; } -/* line 175, ../scss/modules/column-navigation.scss */ +/* line 176, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .icons-left .custom-checkbox { line-height: 15px; margin-right: 8px; margin-top: 2px; } -/* line 181, ../scss/modules/column-navigation.scss */ +/* line 182, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .edit { background-color: #52b6ca; padding: 0px 10px; @@ -8606,14 +8600,14 @@ a.btn-black, a.btn-highlight { display: none; margin-right: 0; } -/* line 190, ../scss/modules/column-navigation.scss */ +/* line 191, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .husky-column-navigation-loader { position: relative; top: 2px; left: -2px; padding-left: 2px; } -/* line 197, ../scss/modules/column-navigation.scss */ +/* line 198, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .column-navigation .arrow { display: inline-block; width: 11px; @@ -8623,7 +8617,7 @@ a.btn-black, a.btn-highlight { color: black; margin: 0; } -/* line 208, ../scss/modules/column-navigation.scss */ +/* line 209, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .add { display: inline-block; width: 50%; @@ -8635,7 +8629,7 @@ a.btn-black, a.btn-highlight { -moz-border-radius-bottomleft: 5px; font-size: 16px; } -/* line 220, ../scss/modules/column-navigation.scss */ +/* line 221, ../scss/modules/column-navigation.scss */ .column-navigation-wrapper .settings { display: inline-block; width: 50%; @@ -8943,87 +8937,452 @@ a.btn-black, a.btn-highlight { .husky-datagrid { position: relative; padding-top: 10px; - /** - * Special styles - **/ } -/* line 9, ../scss/modules/datagrid.scss */ -.husky-datagrid table td { +/* line 8, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table { + position: relative; + top: 0px; + left: 0px; + /** After render effect (start) **/ + /** After render effect (end) **/ +} +/* line 13, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.sticky-header { + padding-top: 30px; +} +/* line 16, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.sticky-header table thead { + position: absolute; + top: 0px; + left: 0px; overflow: hidden; - text-overflow: ellipsis; } -/* line 11, ../scss/modules/datagrid.scss */ -.husky-datagrid table td input { - margin: 0 0 0 -2px; - border-color: white; +/* line 22, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.sticky-header table .header-clone { + position: static; + top: auto; + left: auto; + visibility: hidden; +} +/* line 27, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.sticky-header table .header-clone .checkbox-cell * { + display: none; +} +/* line 30, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.sticky-header table .header-clone .row-remover { + height: 0; +} +/* line 33, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.sticky-header table .header-clone th { + padding-top: 0; + padding-bottom: 0; + margin-top: 0; + margin-bottom: 0; +} +/* line 38, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.sticky-header table .header-clone th * { + height: 1px; +} +/* line 42, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.sticky-header table .header-clone tr { + padding-top: 0; + padding-bottom: 0; + margin-top: 0; + margin-bottom: 0; + height: 1px; + border: none; +} +/* line 54, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.overflow { + padding-right: 30px; +} +/* line 56, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.overflow + .pagination-wrapper { + padding-right: 30px; +} +/* line 59, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.overflow.fullwidth + .pagination-wrapper { + padding-right: 50px; +} +/* line 62, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.overflow td, .husky-datagrid .husky-table.overflow th { + max-width: 500px; + overflow: hidden; +} +/* line 67, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.overflow:after { + display: block; + content: "\f061"; + color: #999999; + font-size: 16px; + position: absolute; + right: 5px; + margin-top: -28px; + top: 50%; +} +/* line 81, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.fullwidth th:first-child, +.husky-datagrid .husky-table.fullwidth td:first-child { + padding-left: 50px; +} +/* line 85, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.fullwidth + .pagination-wrapper { + padding-left: 40px; + padding-right: 20px; +} +/* line 91, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.white-box { + padding: 0 20px; + overflow: hidden; +} +/* line 94, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.white-box .table-container { + margin-bottom: -1px; +} +/* line 98, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.white-box tbody tr:first-child { + height: 49px; +} +/* line 101, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.white-box tbody tr:last-child { + border-bottom: none; +} +/* line 104, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.white-box tbody td { + padding-left: 0; + padding-right: 0; +} +/* line 109, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.white-box .empty-list { + border: none; +} +/* line 114, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.minimal { + padding: 0; } -/* line 17, ../scss/modules/datagrid.scss */ -.husky-datagrid table td .server-validation-error { - border-color: #AD0710 !important; +/* line 118, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.minimal tbody td img { + height: 30px; +} +/* line 127, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.is-selectable .table tr { + cursor: pointer; +} +/* line 129, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.is-selectable .table tr:hover > td { + background-color: #fff; +} +/* line 134, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.is-selectable .table thead tr { + cursor: auto; +} +/* line 143, ../scss/modules/datagrid.scss */ +.husky-datagrid .husky-table.rendering table tbody tr { + opacity: 0.3; +} +/* line 151, ../scss/modules/datagrid.scss */ +.husky-datagrid table { + white-space: nowrap; +} +/* line 153, ../scss/modules/datagrid.scss */ +.husky-datagrid table .fit { + width: 1%; +} +/* line 157, ../scss/modules/datagrid.scss */ +.husky-datagrid table td .input-wrapper { + position: relative; + top: 0px; + left: 0px; + width: 100%; + margin: -30px 0 0 -4px; + height: 30px; + display: none; +} +/* line 166, ../scss/modules/datagrid.scss */ +.husky-datagrid table td .editable-input { + position: absolute; + top: 0px; + left: 0px; + padding: 0 7px; +} +/* line 173, ../scss/modules/datagrid.scss */ +.husky-datagrid table td .server-validation-error .editable-input { + border-color: #ea524e !important; border-width: 2px !important; } -/* line 25, ../scss/modules/datagrid.scss */ +/* line 178, ../scss/modules/datagrid.scss */ .husky-datagrid table .checkbox-cell { - box-sizing: content-box; - width: 15px; + width: 20px; } -/* line 29, ../scss/modules/datagrid.scss */ +/* line 181, ../scss/modules/datagrid.scss */ .husky-datagrid table .checkbox-placeholder { display: inline-block; width: 12px; height: 15px; } -/* line 37, ../scss/modules/datagrid.scss */ +/* line 187, ../scss/modules/datagrid.scss */ +.husky-datagrid .row-remover { + display: block; + cursor: pointer; +} +/* line 193, ../scss/modules/datagrid.scss */ +.husky-datagrid thead .cell-content { + display: block; +} +/* line 196, ../scss/modules/datagrid.scss */ +.husky-datagrid thead .row-remover { + display: block; +} +/* line 199, ../scss/modules/datagrid.scss */ .husky-datagrid thead tr { height: 30px; padding-top: 10px; border-color: #cccccc; } -/* line 42, ../scss/modules/datagrid.scss */ -.husky-datagrid thead th { +/* line 203, ../scss/modules/datagrid.scss */ +.husky-datagrid thead tr th { padding-top: 0; font-weight: normal; white-space: nowrap; } -/* line 47, ../scss/modules/datagrid.scss */ -.husky-datagrid thead th.is-sortable { +/* line 208, ../scss/modules/datagrid.scss */ +.husky-datagrid thead tr th.is-sortable { color: black; cursor: pointer; + padding-right: 20px; +} +/* line 212, ../scss/modules/datagrid.scss */ +.husky-datagrid thead tr th.is-sortable.is-loading { + padding-right: 10px; } -/* line 51, ../scss/modules/datagrid.scss */ -.husky-datagrid thead th.is-selected { +/* line 216, ../scss/modules/datagrid.scss */ +.husky-datagrid thead tr th.sorted-asc { font-weight: bold; } -/* line 56, ../scss/modules/datagrid.scss */ -.husky-datagrid thead .sort-loader { +/* line 218, ../scss/modules/datagrid.scss */ +.husky-datagrid thead tr th.sorted-asc .cell-content::after { + margin-left: 5px; + content: "\f0d8"; + font-size: 12px; +} +/* line 225, ../scss/modules/datagrid.scss */ +.husky-datagrid thead tr th.sorted-desc { + font-weight: bold; +} +/* line 227, ../scss/modules/datagrid.scss */ +.husky-datagrid thead tr th.sorted-desc .cell-content::after { + margin-left: 5px; + content: "\f0d7"; + font-size: 12px; +} +/* line 236, ../scss/modules/datagrid.scss */ +.husky-datagrid thead .header-loader { display: inline-block; + width: 10px; padding-left: 3px; } -/* line 61, ../scss/modules/datagrid.scss */ +/* line 242, ../scss/modules/datagrid.scss */ .husky-datagrid thead .table-container { width: 100%; } -/* line 67, ../scss/modules/datagrid.scss */ +/* line 247, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody { + /** after render effect (start) **/ + /** after render effect (end) **/ +} +/* line 250, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr { + opacity: 1; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(1) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 100ms; + -moz-transition: opacity 250ms 100ms; + -o-transition: opacity 250ms 100ms; + transition: opacity 250ms 100ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(2) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 140ms; + -moz-transition: opacity 250ms 140ms; + -o-transition: opacity 250ms 140ms; + transition: opacity 250ms 140ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(3) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 180ms; + -moz-transition: opacity 250ms 180ms; + -o-transition: opacity 250ms 180ms; + transition: opacity 250ms 180ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(4) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 220ms; + -moz-transition: opacity 250ms 220ms; + -o-transition: opacity 250ms 220ms; + transition: opacity 250ms 220ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(5) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 260ms; + -moz-transition: opacity 250ms 260ms; + -o-transition: opacity 250ms 260ms; + transition: opacity 250ms 260ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(6) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 300ms; + -moz-transition: opacity 250ms 300ms; + -o-transition: opacity 250ms 300ms; + transition: opacity 250ms 300ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(7) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 340ms; + -moz-transition: opacity 250ms 340ms; + -o-transition: opacity 250ms 340ms; + transition: opacity 250ms 340ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(8) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 380ms; + -moz-transition: opacity 250ms 380ms; + -o-transition: opacity 250ms 380ms; + transition: opacity 250ms 380ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(9) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 420ms; + -moz-transition: opacity 250ms 420ms; + -o-transition: opacity 250ms 420ms; + transition: opacity 250ms 420ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(10) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 460ms; + -moz-transition: opacity 250ms 460ms; + -o-transition: opacity 250ms 460ms; + transition: opacity 250ms 460ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(11) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 500ms; + -moz-transition: opacity 250ms 500ms; + -o-transition: opacity 250ms 500ms; + transition: opacity 250ms 500ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(12) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 540ms; + -moz-transition: opacity 250ms 540ms; + -o-transition: opacity 250ms 540ms; + transition: opacity 250ms 540ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(13) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 580ms; + -moz-transition: opacity 250ms 580ms; + -o-transition: opacity 250ms 580ms; + transition: opacity 250ms 580ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(14) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 620ms; + -moz-transition: opacity 250ms 620ms; + -o-transition: opacity 250ms 620ms; + transition: opacity 250ms 620ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(15) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 660ms; + -moz-transition: opacity 250ms 660ms; + -o-transition: opacity 250ms 660ms; + transition: opacity 250ms 660ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(16) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 700ms; + -moz-transition: opacity 250ms 700ms; + -o-transition: opacity 250ms 700ms; + transition: opacity 250ms 700ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(17) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 740ms; + -moz-transition: opacity 250ms 740ms; + -o-transition: opacity 250ms 740ms; + transition: opacity 250ms 740ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(18) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 780ms; + -moz-transition: opacity 250ms 780ms; + -o-transition: opacity 250ms 780ms; + transition: opacity 250ms 780ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(19) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 820ms; + -moz-transition: opacity 250ms 820ms; + -o-transition: opacity 250ms 820ms; + transition: opacity 250ms 820ms; +} +/* line 256, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(20) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 860ms; + -moz-transition: opacity 250ms 860ms; + -o-transition: opacity 250ms 860ms; + transition: opacity 250ms 860ms; +} +/* line 260, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody tr:nth-child(n+20) { + -webkit-transition: opacity 250ms; + -webkit-transition-delay: 900ms; + -moz-transition: opacity 250ms 900ms; + -o-transition: opacity 250ms 900ms; + transition: opacity 250ms 900ms; +} +/* line 266, ../scss/modules/datagrid.scss */ .husky-datagrid tbody tr:last-child { border-width: 2px; } -/* line 71, ../scss/modules/datagrid.scss */ -.husky-datagrid tbody .child-indent .toggle-icon { +/* line 270, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody .child-wrapper .toggle-icon { margin-right: 10px; } -/* line 74, ../scss/modules/datagrid.scss */ -.husky-datagrid tbody .child-indent .custom-checkbox { +/* line 273, ../scss/modules/datagrid.scss */ +.husky-datagrid tbody .child-wrapper .custom-checkbox { margin-right: 12px; } -/* line 79, ../scss/modules/datagrid.scss */ +/* line 278, ../scss/modules/datagrid.scss */ .husky-datagrid tbody tr:hover .grid-icon { opacity: 1; } -/* line 84, ../scss/modules/datagrid.scss */ +/* line 283, ../scss/modules/datagrid.scss */ .husky-datagrid tbody tr.selected { background: white; } -/* line 88, ../scss/modules/datagrid.scss */ +/* line 287, ../scss/modules/datagrid.scss */ .husky-datagrid tbody .grid-icon { width: 30px; height: 20px; @@ -9041,119 +9400,58 @@ a.btn-black, a.btn-highlight { -o-transition: opacity 100ms ease-in 200ms; transition: opacity 100ms ease-in 200ms; } -/* line 100, ../scss/modules/datagrid.scss */ +/* line 299, ../scss/modules/datagrid.scss */ .husky-datagrid tbody .grid-icon.right { float: right; } -/* line 106, ../scss/modules/datagrid.scss */ +/* line 305, ../scss/modules/datagrid.scss */ .husky-datagrid .table-container { overflow: auto; } -/* line 109, ../scss/modules/datagrid.scss */ -.husky-datagrid .table-container.overflow:after { - content: "\f061"; - color: #cccccc; - font-size: 22px; - position: absolute; - right: -26px; - top: 40%; -} -/* line 120, ../scss/modules/datagrid.scss */ +/* line 309, ../scss/modules/datagrid.scss */ .husky-datagrid .table-container.no-head thead { display: none; } -/* line 124, ../scss/modules/datagrid.scss */ +/* line 313, ../scss/modules/datagrid.scss */ .husky-datagrid .table-container.no-head tbody tr:last-child { border-bottom: none; } -/* line 131, ../scss/modules/datagrid.scss */ +/* line 320, ../scss/modules/datagrid.scss */ .husky-datagrid .datagrid-loader { position: absolute; top: 50%; left: 50%; margin: -50px 0 0 -50px; } -/* line 138, ../scss/modules/datagrid.scss */ +/* line 327, ../scss/modules/datagrid.scss */ .husky-datagrid .dropdown-menu { margin-top: 5px; } -/* line 141, ../scss/modules/datagrid.scss */ +/* line 330, ../scss/modules/datagrid.scss */ .husky-datagrid .dropdown-toggle::after { margin-left: 12px; } -/* line 145, ../scss/modules/datagrid.scss */ +/* line 334, ../scss/modules/datagrid.scss */ .husky-datagrid.loading { min-height: 150px; } -/* line 149, ../scss/modules/datagrid.scss */ +/* line 338, ../scss/modules/datagrid.scss */ .husky-datagrid .empty-list { text-align: center; color: #cccccc; padding: 20px 0; border-bottom: 2px solid #cccccc; } -/* line 155, ../scss/modules/datagrid.scss */ +/* line 344, ../scss/modules/datagrid.scss */ .husky-datagrid .empty-list .icon { display: block; font-size: 50px; padding-bottom: 5px; } -/* line 160, ../scss/modules/datagrid.scss */ +/* line 349, ../scss/modules/datagrid.scss */ .husky-datagrid .empty-list span { display: block; } -/* line 170, ../scss/modules/datagrid.scss */ -.husky-datagrid.fullwidth th:first-child, -.husky-datagrid.fullwidth td:first-child { - padding-left: 50px; -} -/* line 174, ../scss/modules/datagrid.scss */ -.husky-datagrid.fullwidth .pagination-wrapper { - padding-left: 40px; - padding-right: 30px; -} -/* line 178, ../scss/modules/datagrid.scss */ -.husky-datagrid.fullwidth .overflow + .pagination-wrapper { - padding-right: 0; -} -/* line 183, ../scss/modules/datagrid.scss */ -.husky-datagrid.white-box { - padding: 0 20px; - overflow: hidden; -} -/* line 186, ../scss/modules/datagrid.scss */ -.husky-datagrid.white-box .table-container { - margin-bottom: -1px; -} -/* line 190, ../scss/modules/datagrid.scss */ -.husky-datagrid.white-box tbody tr:first-child { - height: 49px; -} -/* line 193, ../scss/modules/datagrid.scss */ -.husky-datagrid.white-box tbody tr:last-child { - border-bottom: none; -} -/* line 196, ../scss/modules/datagrid.scss */ -.husky-datagrid.white-box tbody td { - padding-left: 0; - padding-right: 0; -} -/* line 201, ../scss/modules/datagrid.scss */ -.husky-datagrid.white-box .empty-list { - border: none; -} -/* line 206, ../scss/modules/datagrid.scss */ -.husky-datagrid.minimal { - padding: 0; -} -/* line 209, ../scss/modules/datagrid.scss */ -.husky-datagrid.minimal tbody td.thumb { - width: 1px; -} -/* line 211, ../scss/modules/datagrid.scss */ -.husky-datagrid.minimal tbody td.thumb img { - height: 30px; -} /* line 3, ../scss/modules/label.scss */ .husky-label-error, diff --git a/dist/husky.js b/dist/husky.js index f1adfa623..e85b9cadd 100644 --- a/dist/husky.js +++ b/dist/husky.js @@ -26983,7 +26983,7 @@ define('__component__$navigation@husky',[],function() { hide: 'navigation.hide', show: 'navigation.show' }, - resizeWidth: 1210, + resizeWidth: 1300, forceCollapse: false, systemName: 'Sulu 2.0', footer: true, @@ -28214,16 +28214,11 @@ define('__component__$column-options@husky',[],function() { * * @param {Object} [viewOptions] Configuration object * @param {Boolean} [options.editable] will not set class is-selectable to prevent hover effect for complete rows - * @param {String} [options.className] additional classname for the wrapping div * @param {Boolean} [options.removeRow] displays in the last column an icon to remove a row * @param {Object} [options.selectItem] Configuration object of select item (column) * @param {Boolean} [options.selectItem.inFirstCell] If true checkbox is in the first cell. If true checkbox gets its own cell * @param {String} [options.selectItem.type] Type of select [checkbox, radio] - * @param {Boolean} [options.validation] enables validation for datagrid - * @param {Boolean} [options.validationDebug] enables validation debug for datagrid * @param {Boolean} [options.addRowTop] adds row to the top of the table when add row is triggered - * @param {Boolean} [options.startTabIndex] start index for tabindex - * @param {String} [options.columnMinWidth] sets the minimal width of table columns * @param {String} [options.fullWidth] If true datagrid style will be full-width mode * @param {Array} [options.excludeFields=['id']] array of fields to exclude by the view * @param {Boolean} [options.showHead] if TRUE head would be showed @@ -28234,8 +28229,11 @@ define('__component__$column-options@husky',[],function() { * @param {Function} [options.icons.callback] a callback to execute if the icon got clicked. Gets the id of the data-record as first argument * @param {Boolean} [options.hideChildrenAtBeginning] if true children get hidden, if all children are loaded at the beginning * @param {String|Number|Null} [options.openChildId] the id of the children to open all parents for. (only relevant in a child-list) - * @param {String|Number|Null} [options.cssClass] css-class to give the the components element. (e.g. "white-box") + * @param {String|Number} [options.cssClass] css-class to give the the components element. (e.g. "white-box") * @param {Boolean} [options.highlightSelected] highlights the clicked row when selected + * @param {String} [options.removeIcon] icon to use for the remove-row item + * @param {Number} [options.croppedMaxLength] the length to which croppable cells will be cropped on overflow + * @param {Boolean} [options.stickyHeader] true to make the table header sticky * * @param {Boolean} [rendered] property used by the datagrid-main class * @param {Function} [initialize] function which gets called once at the start of the view @@ -28250,104 +28248,120 @@ define('husky_components/datagrid/decorators/table-view',[],function() { var defaults = { editable: false, - className: 'datagridcontainer', fullWidth: false, removeRow: false, selectItem: { - type: 'checkbox', // checkbox, radio button + type: 'checkbox', inFirstCell: false }, noItemsText: 'This list is empty', - validation: false, // TODO does not work for added rows - validationDebug: false, addRowTop: true, - startTabIndex: 99999, excludeFields: [''], - cssClass: null, - columnMinWidth: '70px', + cssClass: '', thumbnailFormat: '50x50', showHead: true, hideChildrenAtBeginning: true, openChildId: null, highlightSelected: false, - icons: [] + stickyHeader: false, + icons: [], + removeIcon: 'trash-o', + croppedMaxLength: 35 }, constants = { - viewClass: 'table-container', fullWidthClass: 'fullwidth', - // if datagrid is in fullwidth-mode (options.fullWidth is true) - // this number gets subracted from the tables final width in the resize listener - overflowIconSpacing: 30, - ascClass: 'fa-caret-up', - descClass: 'fa-caret-down', - additionalHeaderClasses: ' m-left-5 small-font', - rowRemoverClass: 'row-remover', - editableClass: 'editable', - selectAllName: 'select-all', - isSelectedClass: 'is-selected', + stickyHeaderClass: 'sticky-header', + selectedRowClass: 'selected', isSelectableClass: 'is-selectable', sortableClass: 'is-sortable', - tableClass: 'table', - serverValidationError: 'server-validation-error', - oversizedClass: 'oversized', + skeletonClass: 'husky-table', + containerClass: 'table-container', overflowClass: 'overflow', + emptyListElementClass: 'empty-list', + rowRemoverClass: 'row-remover', + checkboxClass: 'checkbox', + radioClass: 'radio', + cellFitClass: 'fit', + tableClass: 'table', + rowClass: 'row', thumbSrcKey: 'url', thumbAltKey: 'alt', - sortLoaderClass: 'sort-loader', - childrenSlideDownIcon: 'fa-caret-right', - childrenSlideUpIcon: 'fa-caret-down', - slideDownClass: 'children-toggler', + headerCellClass: 'header-cell', + ascSortedClass: 'sorted-asc', + descSortedClass: 'sorted-desc', + headerCellLoaderClass: 'header-loader', + headerLoadingClass: 'is-loading', + editableItemClass: 'editable', + editableInputClass: 'editable-input', + inputWrapperClass: 'input-wrapper', + editedErrorClass: 'server-validation-error', + newRecordId: 'newrecord', + gridIconClass: 'grid-icon', + childWrapperClass: 'child-wrapper', + parentClass: 'children-toggler', noChildrenClass: 'no-children', - childrenIndentClass: 'child-indent', - childrenLoadedClass: 'children-loaded', - noHeadClass: 'no-head', - selected: 'selected', - childrenIndentPx: 25 //px + toggleIconClass: 'toggle-icon', + collapsedIcon: 'fa-caret-right', + expandedIcon: 'fa-caret-down', + checkboxCellClass: 'checkbox-cell', + textContainerClass: 'cell-content', + renderingClass: 'rendering', + headerCloneClass: 'header-clone', + childIndent: 25 //px + }, + + selectItems = { + CHECKBOX: 'checkbox', + RADIO: 'radio' }, /** * Templates used by this class */ templates = { - removeRow: [ - '', - '', - '' + skeleton: [ + '
', + '
', + '
' ].join(''), - - checkboxPlaceholder: '', - - checkbox: [ - '
', - ' checked<% } %>/>', - '', + table: '
', + header: '', + body: '', + row: '', + headerCell: '', + cell: '', + textContainer: '<%= content %>', + headerCellLoader: '
', + removeCellContent: '', + editableCellContent: [ + '<%= value %>', + '
', + ' ', '
' ].join(''), - + img: '<%= alt %>', + childWrapper: '
', + toggleIcon: '', icon: [ - '', - '', + '', + ' ', '' ].join(''), - - checkboxCell: [ - '', - '<%= checkbox %>', - '' + checkbox: [ + '
', + ' ', + ' ', + '
' ].join(''), - radio: [ - '', '
', - '', - '', - '
', - '' + ' ', + ' ', + '
' ].join(''), - empty: [ - '
', + '
', '
', ' <%= text %>', '
' @@ -28358,7 +28372,7 @@ define('husky_components/datagrid/decorators/table-view',[],function() { * used to update the table width and its containers due to responsiveness * @event husky.datagrid.update.table */ - UPDATE_TABLE = function() { + UPDATE_TABLE = function() { return this.datagrid.createEventName.call(this.datagrid, 'update.table'); }, @@ -28367,7 +28381,7 @@ define('husky_components/datagrid/decorators/table-view',[],function() { * @event husky.datagrid.table.open-child * @param {Number|String} id The id of the data-record to open the parents for */ - OPEN_PARENTS = function() { + OPEN_PARENTS = function() { return this.datagrid.createEventName.call(this.datagrid, 'table.open-parents'); }, @@ -28377,7 +28391,7 @@ define('husky_components/datagrid/decorators/table-view',[],function() { * @param {Number|String} id The id of the data-record to open the parents for * @param {String} columnName column name */ - RADIO_SELECTED = function() { + RADIO_SELECTED = function() { return this.datagrid.createEventName.call(this.datagrid, 'radio.selected'); }, @@ -28385,7 +28399,7 @@ define('husky_components/datagrid/decorators/table-view',[],function() { * triggered when children were collapsed * @event husky.datagrid.table.children.collapsed */ - CHILDREN_COLLAPSED = function() { + CHILDREN_COLLAPSED = function() { return this.datagrid.createEventName.call(this.datagrid, 'children.collapsed'); }, @@ -28393,49 +28407,16 @@ define('husky_components/datagrid/decorators/table-view',[],function() { * triggered when children were expanded * @event husky.datagrid.table.children.expanded */ - CHILDREN_EXPANDED = function() { + CHILDREN_EXPANDED = function() { return this.datagrid.createEventName.call(this.datagrid, 'children.expanded'); - }, - - /** - * calculates the width of a text by creating a tablehead element and measure its width - * @param text - * @param classArray - * @param isSortable - */ - getTextWidth = function(text, classArray, isSortable) { - var elWidth, el, - sortIconWidth = 0, - paddings = 20; - // handle css classes - if (!classArray) { - classArray = []; - } - if (isSortable) { - classArray.push(constants.sortableClass); - sortIconWidth = 20; - } - classArray.push(constants.isSelectedClass); - - el = this.sandbox.dom.createElement('
' + text + '
'); - this.sandbox.dom.css(el, { - 'position': 'absolute', - 'visibility': 'hidden', - 'height': 'auto', - 'width': 'auto' - }); - this.sandbox.dom.append('body', el); - - // text width + paddings and sorting icon - elWidth = this.sandbox.dom.width(el) + paddings + sortIconWidth; - - this.sandbox.dom.remove(el); - - return elWidth; }; return { + /** + * Public methods used by the main datagrid class (start) + * -------------------------------------------------------------------- */ + /** * Initializes the view, gets called only once * @param {Object} context The context of the datagrid class @@ -28451,55 +28432,45 @@ define('husky_components/datagrid/decorators/table-view',[],function() { // merge defaults with options this.options = this.sandbox.util.extend(true, {}, defaults, options); - this.setVariables(); this.bindCustomEvents(); + this.setVariables(); + }, + + /** + * Binds custom related events + */ + bindCustomEvents: function() { + this.sandbox.on(UPDATE_TABLE.call(this), this.onResize.bind(this)); + this.sandbox.on(OPEN_PARENTS.call(this), this.openParents.bind(this)); + }, + + /** + * Unbinds custom events + */ + unbindCustomEvents: function() { + this.sandbox.off(UPDATE_TABLE.call(this), this.onResize.bind(this)); + this.sandbox.off(OPEN_PARENTS.call(this), this.openParents.bind(this)); }, /** * Method to render data in table view */ render: function(data, $container) { - var selected = null; + this.$el = this.sandbox.dom.createElement(templates.skeleton); + this.sandbox.dom.append($container, this.$el); + this.addViewClasses(); this.data = data; - this.$el = $container; - - this.$tableContainer = this.sandbox.dom.createElement('
'); - this.sandbox.dom.append(this.$el, this.$tableContainer); - this.sandbox.dom.append(this.$tableContainer, this.prepareTable()); - - // add full-width class if configured - if (this.options.fullWidth === true) { - this.sandbox.dom.addClass(this.$el, constants.fullWidthClass); - } - - // add custom-css class - if (!!this.options.cssClass) { - this.sandbox.dom.addClass(this.$el, this.options.cssClass); - } - + this.renderTable(); this.bindDomEvents(); if (this.datagrid.options.resizeListeners === true) { this.onResize(); } - - // initialize validation - if (!!this.options.validation) { - this.sandbox.form.create(this.datagrid.$el); - } - - this.setHeaderClasses(); - - // try to open all parents for a child if configured if (!!this.options.openChildId) { - this.openAllParents(this.options.openChildId); + this.openParents(this.options.openChildId); this.options.openChildId = null; } - // try to open all parents for selected records - selected = this.datagrid.getSelectedItemIds.call(this.datagrid); - this.sandbox.util.foreach(selected, function(recordId) { - this.openAllParents(recordId); - }.bind(this)); - + this.renderChildrenHidden = false; + this.sandbox.dom.removeClass(this.$el, constants.renderingClass); this.rendered = true; }, @@ -28507,1313 +28478,1096 @@ define('husky_components/datagrid/decorators/table-view',[],function() { * Destroys the view */ destroy: function() { - this.unbindDomEvents(); - //this.sandbox.stop(this.sandbox.dom.find('*', this.$tableContainer)); - // remove full-width class if configured - if (this.options.fullWidth === true) { - this.sandbox.dom.removeClass(this.$el, constants.fullWidthClass); - } - // remove configured css-class - if (!!this.options.cssClass) { - this.sandbox.dom.removeClass(this.options.cssClass); - } - // remove inline-styles - this.sandbox.dom.removeAttr(this.$el, 'style'); - this.sandbox.dom.empty(this.$el); - }, - - /** - * Binds custom events to the datagrid related to this - * view (like an extension) - */ - bindCustomEvents: function() { - // checks table widths - this.sandbox.on(UPDATE_TABLE.call(this), this.onResize.bind(this)); - // opens all parents for a given child - this.sandbox.on(OPEN_PARENTS.call(this), this.openAllParents.bind(this)); + this.sandbox.dom.remove(this.$el); + this.sandbox.stop(this.sandbox.dom.find('*', this.$el)); + this.setVariables(); }, /** - * Unbinds the custom-events by this view + * Adds a row to the datagrid + * @param record {Object} the new record to add */ - unBindCustomEvents: function() { - this.sandbox.off(UPDATE_TABLE.call(this)); + addRecord: function (record) { + this.removeEmptyIndicator(); + this.renderBodyRow(record, this.options.addRowTop); }, /** - * Unbinds the Dom-Events of the view + * Removes a record from the view + * @param recordId {Number|String} */ - unbindDomEvents: function() { - this.sandbox.dom.unbind(this.sandbox.dom.find('*', this.$tableContainer)); - this.sandbox.dom.unbind(this.$tableContainer); + removeRecord: function(recordId) { + this.datagrid.removeRecord.call(this.datagrid, recordId); + this.sandbox.dom.remove(this.table.rows[recordId].$el); + delete this.table.rows[recordId]; + if (Object.keys(this.table.rows).length === 0) { + this.toggleSelectAllItem(false); + this.renderEmptyIndicator(); + } }, /** - * Binds Dom related events for this table-view + * Handles the responsiveness */ - bindDomEvents: function() { - // select events for checkboxes and radio buttons - if (!!this.options.selectItem.type) { - this.sandbox.dom.on(this.$tableContainer, 'click', this.selectItem.bind(this), - 'input[type="checkbox"], input[type="radio"]' - ); - //select all event - this.sandbox.dom.on( - this.sandbox.dom.find('#' + constants.selectAllName, this.$tableContainer), - 'click', - this.selectAllItems.bind(this) - ); + onResize: function() { + if (this.containerIsOverflown() === true) { + this.overflowHandler(); + } else { + this.underflowHandler(); } - - // events for removing row - if (this.options.removeRow) { - this.sandbox.dom.on( - this.$tableContainer, 'click', this.prepareRemoveRow.bind(this), '.' + constants.rowRemoverClass - ); + if (this.options.stickyHeader === true) { + this.setHeaderCellWidthFromClone(); + this.fixContainerMaxHeight(); } + }, - // emits an event when a table row gets clicked - this.sandbox.dom.on( - this.$tableContainer, 'click', - this.emitRowClickedEvent.bind(this), 'tbody tr' - ); - - // calls the icon-callback on click on an icon - this.sandbox.dom.on( - this.$tableContainer, 'click', - this.callIconCallback.bind(this), 'tr .grid-icon' - ); - - // calls the radio-clicked event and stops further event-propagation - // needs tbody selector to be called before the general listener on - // on checkboxes and radio - this.sandbox.dom.on( - this.sandbox.dom.find('tbody', this.$tableContainer), 'click', - this.radioClickedCallback.bind(this), '.custom-radio.custom-filter' - ); - - this.sandbox.dom.on(this.$tableContainer, 'click', function(event) { - this.sandbox.dom.stopPropagation(event); - }.bind(this)); + /** + * Public methods used by the main datagrid class (end) + * -------------------------------------------------------------------- */ - // add editable events if configured - if (!!this.options.editable) { - this.sandbox.dom.on( - this.$tableContainer, 'click', this.editCellValues.bind(this), '.' + constants.editableClass - ); - this.sandbox.dom.on(this.$tableContainer, 'click', this.focusOnRow.bind(this), 'tbody tr'); + /** + * Sets the components starting properties + */ + setVariables: function() { + this.rendered = false; + this.$el = null; + this.table = {}; + this.data = null; + this.rowClicked = false; + this.preventFocusoutHandler = false; + this.renderChildrenHidden = this.options.hideChildrenAtBeginning; + this.tableCropped = false; + this.cropBreakPoint = null; + }, - // save on "blur" - this.sandbox.dom.on(window, 'click', function() { - if (!!this.lastFocusedRow) { - this.prepareSave(); - } - }.bind(this)); + /** + * Adds css classes to the view element + */ + addViewClasses: function () { + this.sandbox.dom.addClass(this.$el, this.options.cssClass); + this.sandbox.dom.addClass(this.$el, constants.renderingClass); + if (this.options.stickyHeader === true) { + this.sandbox.dom.addClass(this.$el, constants.stickyHeaderClass); } - - // add sortable events if configured - if (this.datagrid.options.sortable) { - this.sandbox.dom.on( - this.sandbox.dom.find('thead th[data-attribute]', this.$tableContainer), - 'click', - this.prepareSort.bind(this) - ); + if (this.options.fullWidth === true) { + this.sandbox.dom.addClass(this.$el, constants.fullWidthClass); } - - // add load-children events if configured - if (!!this.datagrid.options.childrenPropertyName) { - this.sandbox.dom.on(this.$tableContainer, 'click', - this.prepareChildrenLoad.bind(this), 'tbody tr' - ); + if ((this.options.highlightSelected === true || !!this.options.selectItem) && this.options.editable !== true) { + this.sandbox.dom.addClass(this.$el, constants.isSelectableClass); } }, /** - * Highlights clicked row and removes highlighting from the previously - * highlighted - * @param event - */ - highlightRow: function(event) { - var $row = event.currentTarget, - $selectedRow = this.sandbox.dom.find( - 'tbody tr.' + constants.selected, - this.$el - ); - - this.sandbox.dom.removeClass($selectedRow, constants.selected); - this.sandbox.dom.addClass($row, constants.selected); - }, + * Render methods (start) + * -------------------------------------------------------------------- */ /** - * emits radio-clicked event and stops event propagation + * Renders the table */ - radioClickedCallback: function(event) { - var parentTr = this.sandbox.dom.closest(event.currentTarget, 'tr'), - parentTd = this.sandbox.dom.closest(event.currentTarget, 'td'), - id = this.sandbox.dom.data(parentTr, 'id'), - field = this.sandbox.dom.data(parentTd, 'field'); - this.sandbox.emit(RADIO_SELECTED.call(this), id, field); - event.stopPropagation(); + renderTable: function() { + this.table.$el = this.sandbox.dom.createElement(templates.table); + this.table.$container = this.sandbox.dom.find('.' + constants.containerClass, this.$el); + if (this.options.showHead === true) { + this.renderHeader(); + } + this.renderBody(); + this.sandbox.dom.append(this.table.$container, this.table.$el); }, /** - * Emits the row-clicked event + * Renders the table header */ - emitRowClickedEvent: function(event) { - if (!this.rowClicked) { - this.rowClicked = true; - var id = this.sandbox.dom.$(event.currentTarget).data('id'); - - if (!!this.options.highlightSelected) { - this.highlightRow(event); - } - - if (!!id) { - this.datagrid.emitItemClickedEvent.call(this.datagrid, id); - } else { - this.datagrid.emitItemClickedEvent.call(this.datagrid, event); - } - - // set row clicked back to prevent multiple emits on double click - setTimeout(function(){ - this.rowClicked = false; - }.bind(this), 500); + renderHeader: function() { + this.table.header = {}; + this.table.header.$el = this.sandbox.dom.createElement(templates.header); + this.table.header.$row = this.sandbox.dom.createElement(templates.row); + this.sandbox.dom.append(this.table.header.$el, this.table.header.$row); + this.renderHeaderSelectItem(); + this.renderHeaderCells(); + this.renderHeaderRemoveItem(); + if (this.options.stickyHeader === true) { + this.cloneHeader(); } + this.sandbox.dom.append(this.table.$el, this.table.header.$el); }, /** - * Sets the components starting properties + * Creates a clone for the actual header */ - setVariables: function() { - this.rendered = false; - this.$tableContainer = null; - this.$table = null; - this.$el = null; - this.data = null; - this.rowId = 0; - this.rowStructure = []; - this.errorInRow = []; - this.bottomTabIndex = this.options.startTabIndex || 49999; - this.topTabIndex = this.options.startTabIndex || 50000; - this.contentMarginRight = 0; - this.contentPaddings = 0; - this.rowClicked = false; + cloneHeader: function() { + this.table.header.$clone = this.sandbox.dom.clone(this.table.header.$el); + this.sandbox.dom.addClass(this.table.header.$clone, constants.headerCloneClass); + this.sandbox.dom.append(this.table.$el, this.table.header.$clone); }, /** - * Sets the header classes used for sorting purposes - * uses this.datagrid.sort + * Renders the select-all checkbox of the header */ - setHeaderClasses: function() { - var attribute = this.datagrid.sort.attribute, - direction = this.datagrid.sort.direction, - $element = this.sandbox.dom.find('thead th[data-attribute=' + attribute + ']', this.$tableContainer), - $span = this.sandbox.dom.children($element, 'span')[0]; - - if (!!attribute) { - this.sandbox.dom.addClass($element, constants.isSelectedClass); - - if (direction === 'asc') { - this.sandbox.dom.addClass($span, constants.ascClass + constants.additionalHeaderClasses); - } else { - this.sandbox.dom.addClass($span, constants.descClass + constants.additionalHeaderClasses); + renderHeaderSelectItem: function() { + if (!!this.options.selectItem && !!this.options.selectItem.type) { + var $cell = this.sandbox.dom.createElement(templates.headerCell); + this.sandbox.dom.addClass($cell, constants.checkboxCellClass); + if (this.options.selectItem.type === selectItems.CHECKBOX) { + this.sandbox.dom.html($cell, templates.checkbox); } + this.sandbox.dom.prepend(this.table.header.$row, $cell); } }, /** - * Perapres the structure of the datagrid when element type is table - * @returns {table} returns table element + * Renderes the cells in the header */ - prepareTable: function() { - var $table, $thead, $tbody, tblClasses; - - this.$table = $table = this.sandbox.dom.createElement(''); - - if (!!this.data.head || !!this.datagrid.matchings) { - $thead = this.sandbox.dom.createElement(''); - this.sandbox.dom.append($thead, this.prepareTableHead()); - this.sandbox.dom.append($table, $thead); - } - - if (this.options.showHead === false) { - this.sandbox.dom.addClass(this.$tableContainer, constants.noHeadClass); - } - - if (!!this.data.embedded) { - $tbody = this.sandbox.dom.createElement(''); - this.prepareTableRows($tbody); - this.sandbox.dom.append($table, $tbody); - } - - // set html classes - tblClasses = []; - tblClasses.push( - (!!this.options.className && this.options.className !== constants.tableClass) ? constants.tableClass + - ' ' + this.options.className : constants.tableClass - ); + renderHeaderCells: function() { + var $headerCell; + this.table.header.cells = {}; - // when list should not have the hover effect for whole rows do not set the is-selectable class - if (!this.options.editable) { - tblClasses.push((this.options.selectItem && this.options.selectItem.type === 'checkbox') ? constants.isSelectableClass : ''); - } - - this.sandbox.dom.addClass($table, tblClasses.join(' ')); - - return $table; + this.sandbox.util.foreach(this.datagrid.matchings, function(column) { + $headerCell = this.sandbox.dom.createElement(templates.headerCell); + this.sandbox.dom.html($headerCell, this.sandbox.util.template(templates.textContainer)({ + content: this.sandbox.translate(column.content) + })); + this.sandbox.dom.data($headerCell, 'attribute', column.attribute); + this.table.header.cells[column.attribute] = { + $el: $headerCell, + sortable: column.sortable + }; + this.sandbox.dom.append(this.table.header.$row, this.table.header.cells[column.attribute].$el); + this.setHeaderCellClasses(column.attribute); + }.bind(this)); }, /** - * Prepares table head - * @returns {string} returns table head + * Sets css classes on a header cell + * @param column {String} the column attribute of the column to set the classes for */ - - prepareTableHead: function() { - var tblColumns, tblCellClass, headData, widthValues, dataAttribute, - tblColumnStyle, minWidth, count = 0; - - tblColumns = []; - headData = this.datagrid.matchings || this.data.head; - - // add a checkbox to head row - if (!!this.options.selectItem && !!this.options.selectItem.type) { - tblColumns.push( - '' - ); - - if (this.options.selectItem.type === 'checkbox') { - tblColumns.push(this.sandbox.util.template(templates.checkbox)({ - id: constants.selectAllName, - checked: false - })); - } - - tblColumns.push(''); - } - - this.rowStructure = []; - - // value used for correct tabindex when row added at top of table - this.tabIndexParam = 1; - - this.sandbox.util.foreach(headData, function(column) { - if (this.options.excludeFields.indexOf(column.attribute) < 0) { - - // calculate width - tblColumnStyle = []; - tblCellClass = ''; - if (column.width) { - minWidth = column.width; - } else if (column.minWidth) { - minWidth = column.minWidth; - } else { - minWidth = getTextWidth.call(this, column.content, [], column.sortable); - minWidth = (minWidth > this.datagrid.getNumberAndUnit(this.options.columnMinWidth).number) ? minWidth + 'px' : this.options.columnMinWidth; - - } - tblColumnStyle.push('min-width:' + minWidth); - column.minWidth = minWidth; - - // get width and measureunit - if (!!column.width) { - widthValues = this.datagrid.getNumberAndUnit(column.width); - tblColumnStyle.push('max-width:' + widthValues.number + widthValues.unit); - tblColumnStyle.push('width:' + widthValues.number + widthValues.unit); - } - - // add to row structure when valid entry - if (column.attribute !== undefined) { - this.rowStructure.push({ - attribute: column.attribute, - editable: column.editable, - validation: column.validation, - type: column.type - }); - - if (!!column.editable) { - this.tabIndexParam++; - } - } - - // add children-toggler class if children toggle is enabled - if (!!this.datagrid.options.childrenPropertyName && count === 0) { - tblCellClass = constants.slideDownClass; - } - - // add html to table header cell if sortable - if (!!column.sortable) { - dataAttribute = ' data-attribute="' + column.attribute + '"'; - tblCellClass += ((!!column.class) ? ' ' + column.class + ' ' + constants.sortableClass : ' ' + constants.sortableClass + ''); - tblColumns.push('' + column.content + ''); - } else { - tblCellClass += ((!!column.class) ? ' ' + column.class + '' : ''); - tblColumns.push('' + column.content + ''); + setHeaderCellClasses: function(column) { + if (this.datagrid.options.sortable === true) { + var $element = this.table.header.cells[column].$el, + sortedClass; + if (this.table.header.cells[column].sortable === true) { + this.sandbox.dom.addClass($element, constants.sortableClass); + if (column === this.datagrid.sort.attribute) { + sortedClass = (this.datagrid.sort.direction === 'asc') ? constants.ascSortedClass : constants.descSortedClass; + this.sandbox.dom.addClass($element, sortedClass); } } - count++; - }.bind(this)); - - // remove-row entry - if (!!this.options.removeRow) { - tblColumns.push(''); } - - return '' + tblColumns.join('') + ''; }, /** - * Itterates over all items and prepares the rows - * @param {Object} container to insert the table-rows the table-rows to + * Renderes an empty remove-row cell into the header */ - prepareTableRows: function($container) { - var $row, $parent; + renderHeaderRemoveItem: function () { + if (this.options.removeRow === true) { + var $cell = this.sandbox.dom.createElement(templates.headerCell); + this.sandbox.dom.addClass($cell, constants.cellFitClass); + this.sandbox.dom.append(this.table.header.$row, $cell); + } + }, - if (!!this.data.embedded && this.data.embedded.length > 0) { - this.sandbox.util.foreach(this.data.embedded, function(row) { - $parent = null; - $row = this.prepareTableRow(row, false); - if (!!row.parent) { - $parent = this.sandbox.dom.find('tr[data-id="' + row.parent + '"]', $container); - } - if (!!$parent && !!$parent.length) { - this.insertChild($row, $parent, row.parent, this.options.hideChildrenAtBeginning); - } else { - this.sandbox.dom.append($container, $row); - } + /** + * Renders the table body + */ + renderBody: function() { + this.table.$body = this.sandbox.dom.createElement(templates.body); + this.table.rows = {}; + if (this.data.embedded.length > 0) { + this.sandbox.util.foreach(this.data.embedded, function(record) { + this.renderBodyRow(record); }.bind(this)); } else { - this.sandbox.dom.append(this.$el, this.sandbox.util.template(templates.empty)({ - text: this.sandbox.translate(this.options.noItemsText) - })); + this.renderEmptyIndicator(); } + this.sandbox.dom.append(this.table.$el, this.table.$body); }, /** - * Responsible for creating a single table-row - * @param row {Object} the data for a row - * @param triggeredByAddRow - * @returns {*} + * Renders the checkbox or radio button for a row in the tbody + * @param id {Number|String} the id of the row to add the select-item for + * @param norender {Boolean} if true the select-item doesn't get rendered as a cell but returned as string + * @returns {String} html if no render is set to true */ - prepareTableRow: function(row, triggeredByAddRow) { - var $tableRow, radioPrefix, key, i; - - if (!!(this.options.template && this.options.template.row)) { - $tableRow = this.sandbox.template.parse(this.options.template.row, row); - - } else { - this.tblColumns = []; - this.tblRowAttributes = ' data-dom-id="dom-' + this.datagrid.options.instanceName + '-' + this.rowId + '"'; - this.rowId++; - - if (!!this.options.className && this.options.className !== '') { - radioPrefix = '-' + this.options.className; - } else { - radioPrefix = ''; - } - - // if the select-item should have its own cell add a checkbox or a radio-button - if (this.options.selectItem.inFirstCell !== true) { - // and don't display the checkbox anyway if only leaves should have a checkbox and record has children - if (!(this.datagrid.options.onlySelectLeaves === true && row[this.datagrid.options.childrenPropertyName] > 0)) { - // add a checkbox-cell to each row - if (!!this.options.selectItem.type && this.options.selectItem.type === 'checkbox') { - this.tblColumns.push(this.sandbox.util.template(templates.checkboxCell)({ - checkbox: this.sandbox.util.template(templates.checkbox)({ - id: '', - checked: !!row.selected - }) - })); - - // add a radio to each row - } else if (!!this.options.selectItem.type && this.options.selectItem.type === 'radio') { - this.tblColumns.push(this.sandbox.util.template(templates.radio)({ - name: 'husky-radio' + radioPrefix - })); - } - } else { - this.tblColumns.push(''); - } - } - - // when row structure contains more elements than the id then use the structure to set values - if (this.rowStructure.length) { - - if (!!triggeredByAddRow && !!this.options.addRowTop) { - this.bottomTabIndex -= (this.tabIndexParam + 1); - } - - this.sandbox.util.foreach(this.rowStructure, function(key, index) { - key.editable = key.editable || false; - this.createRowCell(key.attribute, row[key.attribute], key.type, key.editable, key.validation, triggeredByAddRow, index, row); - }.bind(this)); - - } else { - i = 0; - for (key in row) { - if (row.hasOwnProperty(key)) { - this.createRowCell(key, row[key], null, false, null, triggeredByAddRow, i, row); - i++; - } - } + renderRowSelectItem: function(id, norender) { + if (this.datagrid.options.onlySelectLeaves === true && this.table.rows[id].numberChildren > 0) { + return ''; + } + if (!!this.options.selectItem && !!this.options.selectItem.type && + (this.options.selectItem.inFirstCell === false || norender === true)) { + var $cell = this.sandbox.dom.createElement(templates.cell); + this.sandbox.dom.addClass($cell, constants.cellFitClass); + if (this.options.selectItem.type === selectItems.CHECKBOX) { + this.sandbox.dom.html($cell, templates.checkbox); + } else if (this.options.selectItem.type === selectItems.RADIO) { + this.sandbox.dom.html($cell, this.sandbox.util.template(templates.radio)({ + name: 'datagrid-' + this.datagrid.options.instanceName + })); } - - if (!!this.options.removeRow) { - this.tblColumns.push(this.sandbox.util.template(templates.removeRow)()); - } - - $tableRow = this.sandbox.dom.createElement('' + this.tblColumns.join('') + ''); - - if (!!row.id) { - this.sandbox.dom.data($tableRow, 'id', row.id); - this.sandbox.dom.attr($tableRow, 'data-id', row.id); + if (norender === true) { + return this.sandbox.dom.html($cell); } + this.sandbox.dom.prepend(this.table.rows[id].$el, $cell); } - return $tableRow; }, /** - * Sets the value of row cell and the data-id attribute for the row - * @param key attribute name - * @param value attribute value - * @param type {String} The type of the cell. Used to call a function to manipulate the content - * @param editable flag whether field is editable or not - * @param validation information for field - * @param triggeredByAddRow triggered trough add row - * @param index - * @param row {Object} the row object + * Renderes a single table row. If the row already exists it replaces the exiting one + * @param record {Object} the record + * @param prepend {Boolean} if true row gets prepended */ - createRowCell: function (key, value, type, editable, validation, triggeredByAddRow, index, row) { - var tblCellClasses, - tblCellContent, - tblCellStyle, - tblCellClass, - k, - validationAttr = '', - $cell, - $innerContainer; + renderBodyRow: function (record, prepend) { + this.removeNewRecordRow(); + var $row = this.sandbox.dom.createElement(templates.row), + $overrideElement = (!!this.table.rows[record.id]) ? this.table.rows[record.id].$el : null; + record.id = (!!record.id) ? record.id : constants.newRecordId + this.sandbox.dom.data($row, 'id', record.id); - if (!value) { - value = ''; + if (!!record.parent) { + this.table.rows[record.parent].childrenLoaded = true; } - if (this.options.excludeFields.indexOf(key) < 0) { - tblCellClasses = []; - tblCellContent = value; - - // prepare table cell classes - !!value.class && tblCellClasses.push(value.class); - (type === this.datagrid.types.THUMBNAILS) && tblCellClasses.push('thumb'); - - tblCellClass = (!!tblCellClasses.length) ? 'class="' + tblCellClasses.join(' ') + '"' : ''; - - if (!!this.options.validation && !!validation) { - for (k in validation) { - validationAttr += ['data-validation-', k, '="', validation[k], '" '].join(''); - } - } - tblCellStyle = 'style="max-width:' + this.datagrid.matchings[index].minWidth + '"'; - - // call the type manipulate to manipulate the content of the cell - if (!!type && type === this.datagrid.types.THUMBNAILS) { - tblCellContent = this.datagrid.manipulateContent(tblCellContent, type, this.options.thumbnailFormat); - tblCellContent = '' + tblCellContent[constants.thumbAltKey] + ''; - } else { - tblCellContent = this.datagrid.processContentFilter(key, tblCellContent, type, index); - } - - if (!!editable) { - if (!!triggeredByAddRow) { - // differentiate for tab index - if (!!this.options.addRowTop) { - $cell = ''; - this.bottomTabIndex++; - } else { - $cell = ''; - this.topTabIndex++; - } - } else { - $cell = '' + tblCellContent + ''; - this.topTabIndex++; - - } - // if record has children and is first element in row add an icon - } else if (index === 0 && !!this.datagrid.options.childrenPropertyName) { - if (row[this.datagrid.options.childrenPropertyName] > 0) { - $cell = '
' + tblCellContent + '
'; - } else { - $cell = '
' + tblCellContent + '
'; - } - $cell = this.sandbox.dom.createElement($cell); - $innerContainer = this.sandbox.dom.find('.' + constants.childrenIndentClass, $cell); + this.table.rows[record.id] = { + $el: $row, + cells: {}, + childrenLoaded: false, + childrenExpanded: false, + parent: !!(record.parent) ? record.parent : null, + numberChildren: (!!record[this.datagrid.options.childrenPropertyName]) ? record[this.datagrid.options.childrenPropertyName] : 0, + level: 1 + }; + this.renderRowSelectItem(record.id); + this.renderBodyCellsForRow(record); + this.renderRowRemoveItem(record.id); + this.insertBodyRow(record, $overrideElement, prepend); + this.executeRowPostRenderActions(record); + }, + + /** + * Inserts a body row into the dom. Looks if a row needs to be overriden, or if a parent exists etc. + * @param record {Object} the data object of the record + * @param prepend {Boolean} true to prepend + */ + insertBodyRow: function(record, $overrideElement, prepend) { + var $parentElement = (!!this.table.rows[record.parent]) ? this.table.rows[record.parent].$el : null, + insertMethod = (prepend === true) ? this.sandbox.dom.prepend : this.sandbox.dom.append; + + // if there already was a row with the same id, override it with the new one + if (!!$overrideElement && !!$overrideElement.length) { + this.sandbox.dom.after($overrideElement, this.table.rows[record.id].$el); + this.sandbox.dom.remove($overrideElement); + // if there is a parent insert it after the parent row + } else if (!!$parentElement && !!$parentElement.length) { + if (this.renderChildrenHidden === true) { + this.sandbox.dom.hide(this.table.rows[record.id].$el); + this.changeChildrenToggleIcon(record.parent, false); } else { - $cell = '' + tblCellContent + ''; + this.table.rows[record.parent].childrenExpanded = true; + this.changeChildrenToggleIcon(record.parent, true); } - $cell = this.sandbox.dom.createElement($cell); - - // add checkbox to first cell if configured - if (index === 0 && this.options.selectItem.inFirstCell === true) { - // dont display a checkbox if only leaves should get a checkbox and record has children - if (!(this.datagrid.options.onlySelectLeaves === true && row[this.datagrid.options.childrenPropertyName] > 0)) { - this.sandbox.dom.prepend($innerContainer || $cell, this.sandbox.util.template(templates.checkbox)({ - id: '', - checked: !!row.selected - })); - } else { - this.sandbox.dom.prepend($innerContainer || $cell, templates.checkboxPlaceholder); - } - // double the colspan because of the extra cell in the header - this.sandbox.dom.prop($cell, 'colspan', 2); - } - - this.addIconsToCell($innerContainer || $cell, key, row); - - // push the html string to the global array - this.tblColumns.push(this.sandbox.dom.outerHTML($cell)); + this.sandbox.dom.after($parentElement, this.table.rows[record.id].$el); + // else just append or prepend it } else { - this.tblRowAttributes += ' data-' + key + '="' + value + '"'; + insertMethod(this.table.$body, this.table.rows[record.id].$el); } }, /** - * Adds configured icons to a cell - * @param $container {Object} the dom-object to append the icons to - * @param column {String} the identifier of the column - * @ + * Manipulates a row of a rendered after it has been rendered. For examples checks the checkbox or focuses an input + * @param record {Object} the data of the record */ - addIconsToCell: function($container, column) { - if (!!this.options.icons) { - var i, length, $icon; + executeRowPostRenderActions: function(record) { + if (record.selected === true) { + this.toggleSelectRecord(record.id, true); + } else { + this.toggleSelectAllItem(false); + } + // select first input if record is new and editable is true + if (this.options.editable === true && record.id === constants.newRecordId) { + this.showInput(record.id); + } + }, - for (i = -1, length = this.options.icons.length; ++i < length;) { - if (column === this.options.icons[i].column) { - $icon = this.sandbox.dom.createElement(this.sandbox.util.template(templates.icon)({ - icon: this.options.icons[i].icon, - align: this.options.icons[i].align || 'left' - })); - this.sandbox.dom.attr($icon, 'data-icon-index', i); - this.sandbox.dom.append($container, $icon); - } + /** + * Renderes the all the content cells in a body row + * @param record {Object} the data for the row + */ + renderBodyCellsForRow: function(record) { + // foreach matching grab the corresponding data and render the cell with it + this.sandbox.util.foreach(this.datagrid.matchings, function(column, index) { + if (this.options.excludeFields.indexOf(column.attribute) === -1) { + this.renderBodyCell(record, column, index); } - } + }.bind(this)); }, /** - * Handles the click an an icon (calls the defined callback) - * @param event + * Renders the remove item for a row in the tbody + * @param id {Number|String} the id of the row to add the select-item for */ - callIconCallback: function(event) { - this.sandbox.dom.stopPropagation(event); - var index = this.sandbox.dom.data(event.currentTarget, 'iconIndex'), - recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, 'tr'), 'id'); - // call the callback - if (!!this.options.icons[index] && typeof this.options.icons[index].callback === 'function') { - this.options.icons[index].callback(recordId); + renderRowRemoveItem: function (id) { + if (this.options.removeRow === true) { + var $cell = this.sandbox.dom.createElement(templates.cell); + this.sandbox.dom.html($cell, this.sandbox.util.template(templates.removeCellContent)({ + icon: this.options.removeIcon + })); + this.sandbox.dom.addClass($cell, constants.cellFitClass); + this.sandbox.dom.append(this.table.rows[id].$el, $cell); } }, /** - * Adds a row to the datagrid - * @param row + * Renders a single cell + * @param record {Object} the record to render the cell for + * @param column {Object} the column which should be rendered + * @param index {Number} the index of the cell in the row */ - addRecord: function(row) { - var $row, $firstInputField, $checkbox, $parent; - this.removeEmptyListElement(); - // check for other element types when implemented - $row = this.sandbox.dom.$(this.prepareTableRow(row, true)); - - // when unsaved new row exists - save it - this.prepareSave(); - - if (!!row.parent) { - $parent = this.sandbox.dom.find('tr[data-id="' + row.parent + '"]', this.$tableContainer); + renderBodyCell: function(record, column, index) { + var $cell = this.sandbox.dom.createElement(templates.cell), + content = this.getCellContent(record, column), + selectItem, isCroppable = false; + if (!!this.datagrid.options.childrenPropertyName && index === 0) { + content = this.wrapChildrenCellContent(content, record); + } + if (!!this.options.selectItem && !!this.options.selectItem.inFirstCell === true) { + this.sandbox.dom.attr($cell, 'colspan', 2); + selectItem = this.renderRowSelectItem(record.id, true); + if (typeof content === 'string') { + content = selectItem + content; + } else { + this.sandbox.dom.prepend(content, selectItem); + } } + this.sandbox.dom.html($cell, content); + this.sandbox.dom.data($cell, 'attribute', column.attribute); - // if record has a parent insert it after its parent else prepend or append row - if (!!$parent) { - this.insertChild($row, $parent, row.parent, false); - } else if (!!this.options.addRowTop) { - this.sandbox.dom.prepend(this.$table, $row); - } else { - this.sandbox.dom.append(this.$table, $row); + if (!!this.sandbox.dom.find('.' + constants.textContainerClass, $cell).length && + this.sandbox.dom.children(this.sandbox.dom.find('.' + constants.textContainerClass, $cell)).length === 0) { + isCroppable = true; } - $firstInputField = this.sandbox.dom.find('input[type=text]', $row)[0]; - this.sandbox.dom.focus($firstInputField); + this.table.rows[record.id].cells[column.attribute] = { + $el: $cell, + originalData: record[column.attribute], + originalContent: this.sandbox.dom.html(content), + editable: !!column.editable, + croppable: isCroppable + }; + // append cell to corresponding row + this.sandbox.dom.append( + this.table.rows[record.id].$el, + this.table.rows[record.id].cells[column.attribute].$el + ); + }, - if (!!this.options.editable) { - this.lastFocusedRow = this.getInputValuesOfRow($row); + /** + * Gets the actual content for a cell + * @param record {Object} the record to get the content for + * @param column {Object} the column for which the content should be returned + * @returns {String|Object} the dom object for the cell content or html + */ + getCellContent: function(record, column) { + var content = record[column.attribute]; + if (!!column.type && column.type === this.datagrid.types.THUMBNAILS) { + content = this.datagrid.manipulateContent(content, column.type, this.options.thumbnailFormat); + content = this.sandbox.util.template(templates.img)({ + alt: content[constants.thumbAltKey], + src: content[constants.thumbSrcKey] + }); + } else { + content = this.datagrid.processContentFilter( + column.attribute, + content, + column.type, + Object.keys(this.table.rows).length + ); } - - // if allchecked then disable top checkbox after adding new row - if (!!this.options.selectItem.type && this.options.selectItem.type === 'checkbox') { - $checkbox = this.sandbox.dom.find('#' + constants.selectAllName, this.$tableContainer); - if (this.sandbox.dom.hasClass($checkbox, constants.isSelectedClass)) { - this.sandbox.dom.prop($checkbox, 'checked', false); - this.sandbox.dom.removeClass($checkbox, constants.isSelectedClass); - } + if (this.options.editable === true && column.editable === true) { + content = this.getEditableCellContent(content); + } else { + content = this.sandbox.util.template(templates.textContainer)({ + content: content + }); + } + if (!!this.options.icons) { + content = this.addIconsToCellContent(content, column); } + return content; }, /** - * Inserts a child-element after a parent element and indents it - * @param $child - * @param $parent - * @param parentId {Number|String} the id of the parent - * @param hidden {Boolean} if true child gets hidden + * Wraps content into an additional container an indent and a toggler icon (for children and parents) + * @param content {String|Object} the content to wrap. html or dom object + * @param record {Object} the record data object */ - insertChild: function ($child, $parent, parentId, hidden) { - var $parentIcon, depth = this.sandbox.dom.data($parent, 'depth') || 0; - depth = parseInt(depth, 10); - $parentIcon = this.sandbox.dom.find('.' + constants.slideDownClass + ' .toggle-icon', $parent); - - // make sure parent has children-loaded class - this.sandbox.dom.addClass($parent, constants.childrenLoadedClass); - - this.sandbox.dom.attr($child, 'data-parent', parentId); - this.sandbox.dom.data($child, 'depth', depth + 1); - - // indent the children-rows - this.sandbox.dom.css(this.sandbox.dom.find('.' + constants.childrenIndentClass, $child), { - 'margin-left': (constants.childrenIndentPx * (depth + 1)) + 'px' - }); - - if (hidden === true) { - // change the icon of the parent - this.sandbox.dom.removeClass($parentIcon, constants.childrenSlideUpIcon); - this.sandbox.dom.prependClass($parentIcon, constants.childrenSlideDownIcon); - this.sandbox.dom.hide($child); + wrapChildrenCellContent: function(content, record) { + var $wrappedContent = this.sandbox.dom.createElement(templates.childWrapper), + $icon; + // if has children + if (!!record[this.datagrid.options.childrenPropertyName] + && record[this.datagrid.options.childrenPropertyName] > 0) { + this.sandbox.dom.addClass($wrappedContent, constants.parentClass); + $icon = this.sandbox.dom.createElement(templates.toggleIcon); + this.sandbox.dom.prependClass($icon, constants.collapsedIcon); + this.sandbox.dom.prepend($wrappedContent, $icon); } else { - // change the icon of the parent - this.sandbox.dom.removeClass($parentIcon, constants.childrenSlideDownIcon); - this.sandbox.dom.prependClass($parentIcon, constants.childrenSlideUpIcon); + this.sandbox.dom.addClass($wrappedContent, constants.noChildrenClass); + } + // if has parent + if (!!record.parent) { + this.table.rows[record.id].level = this.table.rows[record.parent].level + 1; + // give that child an indent, children love indents + this.sandbox.dom.css($wrappedContent, { + 'padding-left': constants.childIndent * (this.table.rows[record.id].level - 1) + 'px' + }); } - - this.sandbox.dom.after($parent, $child); + this.sandbox.dom.append($wrappedContent, content); + return $wrappedContent; }, /** - * Perparse to save new/changed data includes validation + * Takes a string and retruns the markup for an editable cell + * @param content {String} the original value + * @returns {String|Object} html or a dom object */ - prepareSave: function() { - if (!!this.lastFocusedRow) { - - var $tr = this.sandbox.dom.find('tr[data-dom-id=' + this.lastFocusedRow.domId + ']', this.$tableContainer), - lastFocusedRowCurrentData = this.getInputValuesOfRow($tr), - - data = {}, - key, - url, - isValid = true, - valuesChanged = false, - isDataEmpty; - - data.id = lastFocusedRowCurrentData.id; - - // validate locally - if (!!this.options.validation && !this.sandbox.form.validate('#' + this.datagrid.elId)) { - isValid = false; - } - - isDataEmpty = this.isDataRowEmpty(lastFocusedRowCurrentData.fields); - - // do nothing when data is not valid or no data exists - if (!!isValid && !isDataEmpty) { - - // check which values changed and remember these - for (key in lastFocusedRowCurrentData.fields) { - if (this.lastFocusedRow.fields.hasOwnProperty(key) && this.lastFocusedRow.fields[key] !== lastFocusedRowCurrentData.fields[key]) { - data[key] = lastFocusedRowCurrentData.fields[key]; - valuesChanged = true; - } - } - - // trigger save action when data changed - if (!!valuesChanged || true) { - url = this.datagrid.getUrlWithoutParams(); - - // pass data to datagrid to save it - this.datagrid.saveGrid.call(this.datagrid, data, url, - this.saveSuccess.bind(this, this.lastFocusedRow.domId, $tr), - this.saveFail.bind(this, this.lastFocusedRow.domId, $tr), - this.options.addRowTop); - - // reset last focused row after save - this.lastFocusedRow = null; - - } else if (this.errorInRow.indexOf(this.lastFocusedRow.domId) !== -1) { - this.sandbox.logger.log("Error in table row!"); + getEditableCellContent: function(content) { + var returnHTML = this.sandbox.util.template(templates.editableCellContent)({ + value: content + }); + return returnHTML; + }, - } else { - // nothing changed - reset immediately - this.sandbox.logger.log("No data changed!"); - this.resetRowInputFields($tr); - this.unlockWidthsOfColumns(this.sandbox.dom.find('table th', this.$el)); + /** + * Adds icons to a cell content + * @param content {String|Object} html or a dom object. If its a string icons get added to the string, if its an object it gets appended + * @param column {Object} the column data object + * @returns content {String|Object} html or a dom object + */ + addIconsToCellContent: function(content, column) { + var iconStr; + this.sandbox.util.foreach(this.options.icons, function(icon, index) { + if (icon.column === column.attribute) { + iconStr = this.sandbox.util.template(templates.icon)({ + icon: icon.icon, + align: icon.align, + index: index + }); + if (typeof content === 'object') { + this.sandbox.dom.append(content, iconStr); + } else if (typeof content === 'string') { + content += iconStr; } - - } else { - this.sandbox.logger.log("There seems to be some invalid or empty data!"); } - - } + }.bind(this)); + return content; }, /** - * Checks wether data is in row or not - * @param data fields object + * Renders the empty list element */ - isDataRowEmpty: function(data) { - var isEmpty = true, field; - - for (field in data) { - if (data[field] !== '') { - isEmpty = false; - break; - } - } - - return isEmpty; + renderEmptyIndicator: function() { + this.sandbox.dom.append(this.$el, this.sandbox.util.template(templates.empty)({ + text: this.sandbox.translate(this.options.noItemsText) + })); }, /** - * Callback for save success - * @param $tr - * @param domId - * @param data + * Removes the "new" record row */ - saveSuccess: function(domId, $tr, data) { - // remove row from error list - if (this.errorInRow.indexOf(domId) !== -1) { - this.errorInRow.splice(this.errorInRow.indexOf(domId), 1); + removeNewRecordRow: function() { + if (!!this.table.rows[constants.newRecordId]) { + this.preventFocusoutHandler = true; + this.sandbox.dom.remove(this.table.rows[constants.newRecordId].$el); + delete this.table.rows[constants.newRecordId]; } + }, - this.resetRowInputFields($tr); - this.unlockWidthsOfColumns(this.sandbox.dom.find('table th', this.$el)); + /** + * Render methods (end) + * -------------------------------------------------------------------- */ - // set new returned data - this.setDataForRow($tr[0], data); + /** + * Returns true or false whether the container is overflown or not + * @returns {boolean} + */ + containerIsOverflown: function() { + return this.sandbox.dom.get(this.table.$container, 0).scrollWidth > this.sandbox.dom.width(this.table.$container); }, /** - * Callback for save fail - * @param domId - * @param $tr - * @param jqXHR + * Gets executed when the table width is bigger than its container width */ - saveFail: function(domId, $tr, jqXHR) { - var message = JSON.parse(jqXHR.responseText); - - // remember row with error - if (this.errorInRow.indexOf(domId) === -1) { - this.errorInRow.push(domId); + overflowHandler: function() { + this.toggleCropTable(true); + // if still overflown add a css class + if (this.containerIsOverflown() === true) { + this.sandbox.dom.addClass(this.$el, constants.overflowClass); } - // error in context with database constraints - if (!!message.field) { - this.showValidationError($tr, message.field); + if (this.options.stickyHeader === true) { + this.sandbox.dom.width(this.table.header.$el, this.sandbox.dom.width(this.table.$container)); } }, /** - * Hides input fields and displays new content for table row - * @param $tr + * Gets executed when the table width is not bigger than its container width */ - resetRowInputFields: function($tr) { - var $inputFields = this.sandbox.dom.find('input[type=text]:not(.hidden)', $tr), - content, $span; - - this.sandbox.util.each($inputFields, function(index, $field) { - - // remove css class for server side validation error - // TODO: remove this when server-validation validation type is implemented - if (this.sandbox.dom.hasClass($field, constants.serverValidationError)) { - this.sandbox.dom.removeClass($field, constants.serverValidationError); + underflowHandler: function() { + if (this.tableCropped === true) { + if (this.sandbox.dom.width(this.table.$container) > this.cropBreakPoint) { + this.toggleCropTable(false); } - - content = this.sandbox.dom.val(this.sandbox.dom.$($field)); - $span = this.sandbox.dom.prev($field, '.' + constants.editableClass); - $span.text(content); - - this.sandbox.dom.addClass($field, 'hidden'); - this.sandbox.dom.show($span); - - }.bind(this)); + if (this.containerIsOverflown() === false) { + this.sandbox.dom.removeClass(this.$el, constants.overflowClass); + } + if (this.options.stickyHeader === true) { + this.sandbox.dom.width(this.table.header.$el, ''); + } + } }, /** - * Resets the min-width of columns and - * @param $th array of th elements + * Crops or uncropps all croppable cells in the table */ - unlockWidthsOfColumns: function($th) { - if (!!this.columnWidths) { - this.sandbox.util.each($th, function(index, $el) { - // skip columns without data-attribute because the have min/max and normal widths by default - if (!!this.sandbox.dom.data($el, 'attribute')) { - this.sandbox.dom.css($el, 'min-width', this.columnWidths[index]); - this.sandbox.dom.css($el, 'max-width', ''); - this.sandbox.dom.css($el, 'width', ''); - } + toggleCropTable: function(crop) { + if (this.tableCropped !== crop) { + var $contentContainer, + content; + this.sandbox.util.each(this.table.rows, function(rowId, row) { + this.sandbox.util.each(row.cells, function(cellId, cell) { + if (cell.croppable === true) { + $contentContainer = this.sandbox.dom.find('.' + constants.textContainerClass, cell.$el); + if (crop === true) { + content = this.sandbox.util.cropMiddle(cell.originalContent, this.options.croppedMaxLength); + this.sandbox.dom.attr($contentContainer, 'title', cell.originalContent); + this.tableCropped = true; + this.cropBreakPoint = this.sandbox.dom.width(this.table.$container); + } else { + content = cell.originalContent; + this.sandbox.dom.removeAttr($contentContainer, 'title'); + this.tableCropped = false; + } + this.sandbox.dom.html($contentContainer, content); + } + }.bind(this)); }.bind(this)); } }, /** - * Makes the widths of columns fixed when the table is in edit mode - * prevents changes in column width - * @param $th array of th elements + * Bindes dom related events */ - lockWidthsOfColumns: function($th) { - var width, minwidth; - this.columnWidths = []; - - this.sandbox.dom.each($th, function(index, $el) { - minwidth = this.sandbox.dom.css($el, 'min-width'); - this.columnWidths.push(minwidth); - - width = this.sandbox.dom.outerWidth($el); - this.sandbox.dom.css($el, 'min-width', width); - this.sandbox.dom.css($el, 'max-width', width); - this.sandbox.dom.css($el, 'width', width); - }.bind(this)); + bindDomEvents: function () { + // select or deselect items if the body recognizes a change event + this.sandbox.dom.on( + this.table.$body, 'click', this.selectItemChangeHandler.bind(this), + '.' + constants.checkboxClass + ', .' + constants.radioClass + ); + // handle click on body row + this.sandbox.dom.on(this.table.$body, 'click', this.bodyRowClickHandler.bind(this), '.' + constants.rowClass); + // remove row event + if (this.options.removeRow === true) { + this.sandbox.dom.on(this.table.$body, 'click', this.removeItemClickHandler.bind(this), '.' + constants.rowRemoverClass); + } + if (!!this.table.header) { + // select all + this.sandbox.dom.on(this.table.header.$el, 'change', this.allSelectItemChangeHandler.bind(this)); + + // click on sortable item + if (this.datagrid.options.sortable === true) { + this.sandbox.dom.on( + this.table.header.$el, 'click', this.sortItemClickHandler.bind(this), + '.' + constants.headerCellClass + '.' + constants.sortableClass + ); + } + } + // click on editable item + if (this.options.editable === true) { + this.sandbox.dom.on(this.table.$body, 'click', this.editableItemClickHandler.bind(this), '.' + constants.editableItemClass); + this.sandbox.dom.on(this.table.$body, 'focusout', this.editableInputFocusoutHandler.bind(this), '.' + constants.editableInputClass); + this.sandbox.dom.on(this.table.$body, 'keypress', this.editableInputKeyHandler.bind(this), '.' + constants.editableInputClass); + } + if (!!this.options.icons) { + this.sandbox.dom.on(this.table.$body, 'click', this.iconClickHandler.bind(this), '.' + constants.gridIconClass); + } + this.sandbox.dom.on(this.table.$container, 'scroll', this.containerScrollHandler.bind(this)); + this.sandbox.dom.on(this.table.$body, 'click', this.radioButtonClickHandler.bind(this), 'input[type="radio"]'); }, /** - * Sets the data for a row - * @param $tr dom row - * @param data + * Gets the width of the cells in the header clone and applies them to the actual header */ - setDataForRow: function($tr, data) { - var editables, field, $input; - - // set id - this.sandbox.dom.data($tr, 'id', data.id); - this.sandbox.dom.attr($tr, 'data-id', data.id); - - this.sandbox.util.each(this.sandbox.dom.find('td', $tr), function(index, $el) { - - editables = this.sandbox.dom.find('.' + constants.editableClass, $el); - field = this.sandbox.dom.data($el, 'field'); - $input = this.sandbox.dom.find('input[type=text]', $el); - - if (!!field) { - if (!!editables && editables.length === 1) { // set values in spans - this.sandbox.dom.html(editables[0], data[field]); - this.sandbox.dom.val($input, data[field]); - } else { // set values in td - this.sandbox.dom.html($el, data[field]); - } - } - - }.bind(this)); + setHeaderCellWidthFromClone: function() { + if (!!this.table.header && !!this.table.header.$clone.length) { + var cloneThs = this.sandbox.dom.find('th', this.table.header.$clone), + originalThs = this.sandbox.dom.find('th', this.table.header.$el); + this.sandbox.dom.width(this.table.header.$row, this.sandbox.dom.width( + this.sandbox.dom.find('tr', this.table.header.$clone) + )); + this.sandbox.util.foreach(cloneThs, function(cloneTh, index) { + // min- and max-width because for table-cells normal width has no effect + this.sandbox.dom.css(originalThs[index], { + 'min-width': this.sandbox.dom.outerWidth(cloneTh) + 'px', + 'max-width': this.sandbox.dom.outerWidth(cloneTh) + 'px' + }); + }.bind(this)); + } }, /** - * Sets the validation error class for a dom element - * @param $domElement - * @param field name of field which caused the error + * Sets the max-height of the container to the maximum available space */ - showValidationError: function($domElement, field) { - var $td = this.sandbox.dom.find('td[data-field=' + field + ']', $domElement)[0], - $input = this.sandbox.dom.find('input', $td)[0]; + fixContainerMaxHeight: function() { + this.sandbox.dom.css(this.table.$container, 'max-height', this.datagrid.getRemainingViewHeight.call(this.datagrid)); + }, - if (this.sandbox.dom.hasClass($td, 'husky-validate-success')) { - this.sandbox.dom.removeClass($td, 'husky-validate-success'); + /** + * Gets executed when the table-container gets scrolled + */ + containerScrollHandler: function() { + if (this.options.stickyHeader === true) { + this.sandbox.dom.scrollLeft(this.table.header.$el, this.sandbox.dom.scrollLeft(this.table.$container)); } + }, - this.sandbox.dom.addClass($td, 'husky-validate-error'); + /** + * Gets executed if a radio button in the table body gets clicked + * @param event + */ + radioButtonClickHandler: function(event) { + this.sandbox.dom.stopPropagation(event); + var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id'), + attribute = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, 'td'), 'attribute'); + this.sandbox.emit(RADIO_SELECTED.call(this), recordId, attribute); + }, - // add class for serverside validation error - // TODO remove this when correct validation type is implmented - if (!this.sandbox.dom.hasClass($input, constants.serverValidationError)) { - this.sandbox.dom.addClass($input, constants.serverValidationError); + /** + * Handles the click on grid-icons + * @param event {Object} the event object + */ + iconClickHandler: function(event) { + var icon = this.options.icons[this.sandbox.dom.data(event.currentTarget, 'icon-index')], + recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id'); + if (typeof recordId !== 'undefined' && !!icon && typeof icon.callback === 'function') { + icon.callback(recordId); } }, /** - * Returns an object with the current values of the inputfields, id and domId - * @param $tr table row - * @returns {{domId: *, id: *, fields: Array}} + * Handles the click on a sortable item + * @param event {Object} the event object */ - getInputValuesOfRow: function($tr) { - var id = this.sandbox.dom.data($tr, 'id'), - domId = this.sandbox.dom.data($tr, 'dom-id'), - $inputs = this.sandbox.dom.find('input[type=text]', $tr), - fields = [], field, $td; + sortItemClickHandler: function(event) { + this.sandbox.dom.stopPropagation(event); + var attribute = this.sandbox.dom.data(event.currentTarget, 'attribute'), + direction = 'asc'; + if (this.datagrid.sort.attribute === attribute && this.datagrid.sort.direction === direction) { + direction = 'desc'; + } + this.startHeaderCellLoader(attribute); + // delegate sorting to datagrid + this.datagrid.sortGrid.call(this.datagrid, attribute, direction); + }, - this.sandbox.dom.each($inputs, function(index, $input) { - $td = this.sandbox.dom.parent($input, 'td'); - field = this.sandbox.dom.data($td, 'field'); + /** + * Handles the click on an editable item + * @param event {Object} the event object + */ + editableItemClickHandler: function(event) { + this.sandbox.dom.stopPropagation(event); + var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id'), + attribute = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, 'td'), 'attribute'); + this.showInput(recordId, attribute); + }, - fields[field] = $input.value; + /** + * Shows a edit-input for a row and an attribute if no attribute passed shows the first input in row + * @param recordId {Number|String} + * @param attribute {String} + */ + showInput: function(recordId, attribute) { + var $cell, $inputs; + if (!attribute) { + $inputs = this.sandbox.dom.find('.' + constants.editableInputClass, this.table.rows[recordId].$el); + attribute = this.sandbox.dom.data(this.sandbox.dom.parents($inputs[0], 'td'), 'attribute'); + } + $cell = this.table.rows[recordId].cells[attribute].$el + this.sandbox.dom.show(this.sandbox.dom.find('.' + constants.inputWrapperClass, $cell)); + this.sandbox.dom.focus(this.sandbox.dom.find('.' + constants.editableInputClass, $cell)); + this.sandbox.dom.select(this.sandbox.dom.find('.' + constants.editableInputClass, $cell)); + }, - }.bind(this)); - return { - domId: domId, - id: id, - fields: fields - }; + /** + * Handles keypress events of the editable inputs + * @param event {Object} the event object + */ + editableInputKeyHandler: function(event) { + var recordId; + // on enter + if (event.keyCode === 13) { + this.sandbox.dom.stopPropagation(event); + recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id'); + this.preventFocusoutHandler = true; + this.editRow(recordId); + } + }, + /** + * Handles the focusout of an editable input + * @param event {Object} the event object + */ + editableInputFocusoutHandler: function (event) { + if (this.preventFocusoutHandler === false) { + this.sandbox.dom.stopPropagation(event); + var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id'); + this.editRow(recordId); + } }, /** - * Prepares for removing a row - * Raises the husky.datagrid.record.remove-click event when auto remove handling is not set to true - * @param event + * Gets the data of all edit-inputs in a row and saves their values + * @param recordId {String|Number} the id of the row to edit */ - prepareRemoveRow: function(event) { - this.sandbox.dom.stopPropagation(event); - this.removeRecord(event); + editRow: function(recordId) { + var modifiedRecord = {}; + // build new record object out of the inputs in the row + this.sandbox.util.each(this.table.rows[recordId].cells, function (attribute, cell) { + if (!!this.sandbox.dom.find('.' + constants.editableInputClass, cell.$el).length) { + modifiedRecord[attribute] = this.sandbox.dom.val( + this.sandbox.dom.find('.' + constants.editableInputClass, cell.$el) + ); + } + }.bind(this)); + this.saveRow(recordId, modifiedRecord, this.editedSuccessCallback.bind(this), this.editedErrorCallback.bind(this, recordId)); }, /** - * Removes a row from the datagrid - * @param event + * Clears everything up after a row was edited. (hides the input and updates the values) + * @param record {Object} the changed data record */ - removeRecord: function(event) { - var $element, $tblRow, id, $editableElements, $checkboxes; - - if (typeof event === 'object') { - $element = this.sandbox.dom.$(event.currentTarget); - $tblRow = this.sandbox.dom.closest($element, 'tr')[0]; - id = this.sandbox.dom.data($tblRow, 'id'); - } else { - id = event; - $tblRow = this.sandbox.dom.find('tr[data-id="' + id + '"]')[0]; - } - - // remove row elements from validation - if (!!this.options.validation) { - $editableElements = this.sandbox.dom.find('.' + constants.editableClass, $tblRow); - this.sandbox.util.each($editableElements, function(index, $element) { - this.sandbox.form.removeField('#' + this.datagrid.elId, $element); - }.bind(this)); - } - - if (!!id) { - this.datagrid.removeRecord.call(this.datagrid, id); + editedSuccessCallback: function (record) { + var $row; + if (!!record.id && !!this.table.rows[record.id]) { + $row = this.table.rows[record.id].$el + } else if (!!this.table.rows[constants.newRecordId]) { + $row = this.table.rows[constants.newRecordId].$el } - this.sandbox.dom.remove($tblRow); - - // when last table row was removed, uncheck thead checkbox if exists - $checkboxes = this.sandbox.dom.find('input[type=checkbox]', this.$el); - if ($checkboxes.length === 1) { - this.sandbox.dom.removeClass(constants.isSelectedClass, $checkboxes[0]); - this.sandbox.dom.prop($checkboxes[0], 'checked', false); + if (!!$row && !!$row.length) { + this.sandbox.dom.hide(this.sandbox.dom.find('.' + constants.inputWrapperClass, $row)); + this.renderBodyRow(record, this.options.addRowTop); } - }, /** - * Gets automatically executed on window resize - * responsible for the responsiveness + * Adds a css class to all inputs in a row, if the editing request returned with an error + * @param recordId */ - onResize: function() { - var finalWidth, - contentPaddings = 0, - tableWidth = this.sandbox.dom.width(this.$table), - tableOffset = this.sandbox.dom.offset(this.$table), - contentWidth = this.sandbox.dom.width(content), - windowWidth = this.sandbox.dom.width(this.sandbox.dom.window), - overlaps = false, - originalMaxWidth = contentWidth; - - tableOffset.right = tableOffset.left + tableWidth; - - // if table is greater than max content width - if (tableWidth > originalMaxWidth - contentPaddings && contentWidth < windowWidth - tableOffset.left) { - // adding this class, forces table cells to shorten long words - this.sandbox.dom.addClass(this.$el, constants.oversizedClass); - overlaps = true; - // reset table width and offset - tableWidth = this.sandbox.dom.width(this.$table); - tableOffset.right = tableOffset.left + tableWidth; - } - - // tablecontainer should have width of table in normal cases - finalWidth = tableWidth; - - // if table > window-size set width to available space - if (tableOffset.right + this.contentMarginRight > windowWidth) { - finalWidth = windowWidth - tableOffset.left; - } else { - // set scroll position back - this.sandbox.dom.scrollLeft(this.$el, 0); - } - - // width is not allowed to be smaller than the width of content - if (finalWidth < contentWidth) { - finalWidth = contentWidth; - } - - // now set width - this.sandbox.dom.width(this.$el, finalWidth); - - // check scrollwidth and add class - if (this.sandbox.dom.get(this.$tableContainer, 0).scrollWidth > finalWidth) { - this.sandbox.dom.addClass(this.$tableContainer, constants.overflowClass); + editedErrorCallback: function (recordId) { + var $row = this.table.rows[recordId].$el; + this.sandbox.dom.addClass( + this.sandbox.dom.find('.' + constants.inputWrapperClass, $row), + constants.editedErrorClass + ); + }, - // if overflown and in full width mode reduce list-width - if (this.options.fullWidth === true) { - finalWidth = finalWidth - constants.overflowIconSpacing; - this.sandbox.dom.width(this.$el, finalWidth); + /** + * Replaces a record with an existing one and sends it to a server + * @param recordId {Number|String} the id of the record to override + * @param newRecordData {Object} the new record data + * @param successCallback {Function} gets executed after success + * @param errorCallback {Function} gets executed after error + */ + saveRow: function (recordId, newRecordData, successCallback, errorCallback) { + var hasChanged = false, + record; + this.sandbox.util.each(this.table.rows[recordId].cells, function (attribute, cell) { + if (cell.editable === true && cell.originalData !== newRecordData[attribute]) { + hasChanged = true; } - + }.bind(this)); + // merge record data + record = this.sandbox.util.extend( + true, {}, this.data.embedded[this.datagrid.getRecordIndexById(recordId)], newRecordData + ); + if (recordId === constants.newRecordId) { + delete record.id; + } + if (hasChanged === true) { + // pass data to datagrid to save it + this.datagrid.saveGrid.call(this.datagrid, + record, this.datagrid.getUrlWithoutParams.call(this.datagrid), + successCallback, errorCallback, this.options.addRowTop); } else { - this.sandbox.dom.removeClass(this.$tableContainer, constants.overflowClass); + typeof successCallback === 'function' && successCallback(record); } }, /** - * Selectes or deselects the clicked item - * @param event + * Starts a loader in a cell in the header + * @param column {String} the attribute of the header cell to insert the loader in */ - selectItem: function(event) { - this.sandbox.dom.stopPropagation(event); - - var $element, itemId, parentTr; - $element = this.sandbox.dom.$(event.currentTarget); - - if (!this.sandbox.dom.is($element, 'input')) { - $element = this.sandbox.dom.find('input', this.sandbox.dom.parent($element)); - } - - parentTr = this.sandbox.dom.parents($element, 'tr'); - itemId = this.sandbox.dom.data(parentTr, 'id'); - - if (this.sandbox.dom.attr($element, 'type') === 'checkbox') { - if (this.sandbox.dom.prop($element, 'checked') === false) { - this.sandbox.dom.removeClass($element, constants.isSelectedClass); - this.sandbox.dom.prop($element, 'checked', false); - - // uncheck 'Select All'-checkbox - this.sandbox.dom.prop( - this.sandbox.dom.find('#' + constants.selectAllName, this.$tableContainer), - 'checked', false - ); - - this.datagrid.setItemUnselected.call(this.datagrid, itemId); - } else { - this.sandbox.dom.addClass($element, constants.isSelectedClass); - this.sandbox.dom.prop($element, 'checked', true); - if (!!itemId) { - this.datagrid.setItemSelected.call(this.datagrid, itemId); + startHeaderCellLoader: function(column) { + var $container = this.sandbox.dom.createElement(templates.headerCellLoader); + this.sandbox.dom.addClass(this.table.header.cells[column].$el, constants.headerLoadingClass); + this.sandbox.dom.append(this.sandbox.dom.find('.' + constants.textContainerClass, this.table.header.cells[column].$el), $container); + this.sandbox.start([ + { + name: 'loader@husky', + options: { + el: $container, + size: '10px', + color: '#999999' } } + ]); + }, - } else if (this.sandbox.dom.attr($element, 'type') === 'radio') { - - this.datagrid.deselectAllItems.call(this.datagrid); - - this.sandbox.dom.removeClass( - this.sandbox.dom.find('tr input[type="radio"]', this.$tableContainer), constants.isSelectedClass); - this.sandbox.dom.prop( - this.sandbox.dom.find('tr input[type="radio"]', this.$tableContainer), 'checked', false); - - this.sandbox.dom.addClass($element, constants.isSelectedClass); - this.sandbox.dom.prop($element, 'checked', true); - - if (!!itemId) { - this.datagrid.setItemSelected.call(this.datagrid, itemId); + /** + * Handles the click on a body row + * @param event {Object} the event object + */ + bodyRowClickHandler: function (event) { + this.sandbox.dom.stopPropagation(event); + var recordId = this.sandbox.dom.data(event.currentTarget, 'id'); + this.emitRowClickedEvent(event); + if (!!this.table.rows[recordId]) { + if (this.options.highlightSelected === true) { + this.uniqueHighlightRecord(recordId); + } + if (!!this.table.rows[recordId].numberChildren > 0) { + this.toggleChildren(recordId); } } - }, /** - * Shows input and hides span - * @param event + * Emits the row clicked event + * @param event {Object} the original click event */ - editCellValues: function(event) { - var $target = event.currentTarget, - $input = this.sandbox.dom.next($target, 'input'); - - this.lockWidthsOfColumns(this.sandbox.dom.find('table th', this.$tableContainer)); - - this.sandbox.dom.hide($target); - this.sandbox.dom.removeClass($input, 'hidden'); - this.sandbox.dom.select($input[0]); + emitRowClickedEvent: function (event) { + if (this.rowClicked === false) { + this.rowClicked = true; + var recordId = this.sandbox.dom.data(event.currentTarget, 'id'), + parameter = recordId || event; + this.datagrid.emitItemClickedEvent.call(this.datagrid, parameter); + // delay to prevent multiple emits on double click + this.sandbox.util.delay(function () { + this.rowClicked = false; + }.bind(this), 500); + } }, /** - * Put focus on table row and remember values + * Handles the click on the remove item + * @param event {Object} the event object */ - focusOnRow: function(event) { - var $tr = event.currentTarget, - domId = this.sandbox.dom.data($tr, 'dom-id'); - + removeItemClickHandler: function(event) { this.sandbox.dom.stopPropagation(event); - this.sandbox.logger.log("focus on row", domId); + var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id'); + this.removeRecord(recordId); + }, - if (!!this.lastFocusedRow && this.lastFocusedRow.domId !== domId) { // new focus - this.prepareSave(); - this.lastFocusedRow = this.getInputValuesOfRow($tr); - this.sandbox.logger.log("focused " + this.lastFocusedRow.domId + " now!"); - } else if (!this.lastFocusedRow) { // first focus - this.lastFocusedRow = this.getInputValuesOfRow($tr); - this.sandbox.logger.log("focused " + this.lastFocusedRow.domId + " now!"); + /** + * Handles the change event of the select items + * @param event {Object} the event object + */ + selectItemChangeHandler: function (event) { + this.sandbox.dom.stopPropagation(event); + var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.target, '.' + constants.rowClass), 'id'), + isChecked = this.sandbox.dom.is(event.target, ':checked'); + if (this.options.selectItem.type === selectItems.CHECKBOX) { + this.toggleSelectRecord(recordId, isChecked); + } else if (this.options.selectItem.type === selectItems.RADIO) { + this.uniqueSelectRecord(recordId); } }, /** - * Selects or deselect all available items of the list - * @param event + * Handles the change event of a select item in the header + * @param event {Object} the event object */ - selectAllItems: function(event) { + allSelectItemChangeHandler: function (event) { this.sandbox.dom.stopPropagation(event); - - var $headCheckbox = this.sandbox.dom.find('th input[type="checkbox"]', this.$tableContainer)[0], - $checkboxes = this.sandbox.dom.find('input[type="checkbox"]:visible', this.$tableContainer); - - if (this.sandbox.dom.prop($headCheckbox, 'checked') === false) { - this.sandbox.dom.prop($checkboxes, 'checked', false); - this.sandbox.dom.removeClass($checkboxes, constants.isSelectedClass); - this.datagrid.deselectAllItems.call(this.datagrid); + var isChecked = this.sandbox.dom.is(event.target, ':checked'); + if (isChecked === true) { + this.selectAllRecords(); } else { - this.sandbox.dom.prop($checkboxes, 'checked', true); - this.sandbox.dom.addClass($checkboxes, constants.isSelectedClass); - this.datagrid.selectAllItems.call(this.datagrid); + this.deselectAllRecords(); } }, /** - * Takes a record id and returns true if the record has children - * @param id {Number|String} the id of the record - * @returns {boolean} true if has children, false if not + * Highlights a record an unhighlights all other rows + * @param id {Number|String} the id of the record to highlight */ - recordHasChildren: function(id) { - for (var i = -1, length = this.data.embedded.length; ++i < length;) { - if (this.data.embedded[i].id === id) { - return this.data.embedded[i][this.datagrid.options.childrenPropertyName] > 0; - } - } - return false; + uniqueHighlightRecord: function (id) { + this.sandbox.dom.removeClass( + this.sandbox.dom.find('.' + constants.rowClass + '.' + constants.selectedRowClass, this.table.$body), + constants.selectedRowClass + ); + this.sandbox.dom.addClass(this.table.rows[id].$el, constants.selectedRowClass); }, /** - * Handles the click on a table row if elements in the grid have children - * - * @param event + * Selejcts all records */ - prepareChildrenLoad: function(event) { - this.sandbox.dom.stopPropagation(event); - var recordId = this.sandbox.dom.data(event.currentTarget, 'id'); - - // only load children if they are not already loaded - if (!this.sandbox.dom.hasClass(event.currentTarget, constants.childrenLoadedClass)) { - if (!!recordId && this.recordHasChildren(recordId)) { - this.sandbox.dom.addClass(event.currentTarget, constants.childrenLoadedClass); - this.datagrid.loadChildren.call(this.datagrid, recordId); - } - } else { - this.toggleChildren(event.currentTarget, recordId); - } + selectAllRecords: function () { + this.datagrid.selectAllItems.call(this.datagrid); + this.sandbox.dom.prop(this.sandbox.dom.find('.' + constants.checkboxClass, this.table.$body), 'checked', true); }, /** - * Toggles the already loaded children of a parent element - * @param $parent - * @param parentId + * Deselects all records */ - toggleChildren: function($parent, parentId) { - var $children = this.sandbox.dom.find('tr[data-parent="'+ parentId +'"]', this.$tableContainer); + deselectAllRecords: function () { + this.datagrid.deselectAllItems.call(this.datagrid); + this.sandbox.dom.prop(this.sandbox.dom.find('.' + constants.checkboxClass, this.table.$body), 'checked', false); + }, - if (this.sandbox.dom.is($children, ':visible')) { - this.hideChildren($parent, parentId); - this.sandbox.emit(CHILDREN_COLLAPSED.call(this)); + /** + * Selects or deselects a record with a given id + * @param id {Number|String} the id of the record to select or deselect + * @param select {Boolean} true to select false to deselect + */ + toggleSelectRecord: function (id, select) { + var areAllSelected; + if (select === true) { + this.datagrid.setItemSelected.call(this.datagrid, id); + // ensure that checkboxes are checked + this.sandbox.dom.prop( + this.sandbox.dom.find('.' + constants.checkboxClass, this.table.rows[id].$el), 'checked', true + ); } else { - this.showChildren($parent, parentId); - this.sandbox.emit(CHILDREN_EXPANDED.call(this)); + this.datagrid.setItemUnselected.call(this.datagrid, id); + // ensure that checkboxes are unchecked + this.sandbox.dom.prop( + this.sandbox.dom.find('.' + constants.checkboxClass, this.table.rows[id].$el), 'checked', false + ); + } + // check or uncheck checkboxes in the header + if (!!this.table.header) { + areAllSelected = this.datagrid.getSelectedItemIds.call(this.datagrid).length === this.data.embedded.length; + this.toggleSelectAllItem(areAllSelected); } }, /** - * Opens all parents of a data-record and because of that makes sure that the data-record is visible in the table - * @param id {Number|String} the id of the data-record + * Selects or deselects the select all item + * @param select {Boolean} true to select false to deselect the select all item */ - openAllParents: function(id) { - var $child = this.sandbox.dom.find('tr[data-id="'+ id +'"]', this.$tableContainer), - parentId = this.sandbox.dom.data($child, 'parent'), - $parent = this.sandbox.dom.find('tr[data-id="'+ parentId +'"]', this.$tableContainer); - if (!!parentId && !!$parent) { - if (!!this.sandbox.dom.data($parent, 'parent')) { - this.openAllParents(parentId); - } - this.showChildren($parent, parentId); + toggleSelectAllItem: function (select) { + if (!!this.table.header) { + this.sandbox.dom.prop( + this.sandbox.dom.find('.' + constants.checkboxClass, this.table.header.$el), 'checked', select + ); } }, /** - * Shows the first-level of children of a record - * @param $parent - * @param parentId + * Selects a record and deselects all other records + * @param id {Number|String} the id of the record to select */ - showChildren: function($parent, parentId) { - var $children = this.sandbox.dom.find('tr[data-parent="'+ parentId +'"]', this.$tableContainer), - $parentIcon = this.sandbox.dom.find('.' + constants.slideDownClass + ' .toggle-icon', $parent); - - this.sandbox.dom.removeClass($parentIcon, constants.childrenSlideDownIcon); - this.sandbox.dom.prependClass($parentIcon, constants.childrenSlideUpIcon); - this.sandbox.dom.show($children); + uniqueSelectRecord: function (id) { + this.datagrid.deselectAllItems.call(this.datagrid); + this.datagrid.setItemSelected.call(this.datagrid, id); }, /** - * Recursivly hides all children of a given parent (with nested ones) - * @param $parent - * @param parentId + * Removes the empty list element */ - hideChildren: function($parent, parentId) { - var $children = this.sandbox.dom.find('tr[data-parent="'+ parentId +'"]', this.$tableContainer), - $parentIcon = this.sandbox.dom.find('.' + constants.slideDownClass + ' .toggle-icon', $parent); + removeEmptyIndicator: function() { + this.sandbox.dom.remove(this.sandbox.dom.find('.' + constants.emptyListElementClass, this.$el)); + }, - this.sandbox.util.foreach($children, function($child) { - if (this.sandbox.dom.hasClass($child, constants.childrenLoadedClass)) { - this.hideChildren($child, this.sandbox.dom.data($child, 'id')); + /** + * Changes the toggle-icon of a parent row + * @param recordId {String|Number} the id of the parent row + * @param isExpanded {Boolean} the state for which the icon should be set + */ + changeChildrenToggleIcon: function(recordId, isExpanded) { + var $icon = this.sandbox.dom.find('.' + constants.toggleIconClass, this.table.rows[recordId].$el); + if (!!$icon && !!$icon.length) { + if (isExpanded === true) { + this.sandbox.dom.removeClass($icon, constants.collapsedIcon); + this.sandbox.dom.prependClass($icon, constants.expandedIcon); + } else { + this.sandbox.dom.removeClass($icon, constants.expandedIcon); + this.sandbox.dom.prependClass($icon, constants.collapsedIcon); } - }.bind(this)); - this.sandbox.dom.removeClass($parentIcon, constants.childrenSlideUpIcon); - this.sandbox.dom.prependClass($parentIcon, constants.childrenSlideDownIcon); - this.sandbox.dom.hide($children); + } }, /** - * Handles the click on a sortable column title - * - * Creates the function parameters need for sorting - * and delegates the sorting itself to the datagrid - * @param event {Object} the event object + * Toggles the children of a parent + * @param recordId {Number|String} the id of the parent to toggle the children for */ - prepareSort: function(event) { - var $element = event.currentTarget, - $span = this.sandbox.dom.children($element, 'span')[0], - attribute = this.sandbox.dom.data($element, 'attribute'), - direction = this.sandbox.dom.hasClass($span, constants.ascClass) ? 'desc' : 'asc', - $loaderContainer = this.sandbox.dom.createElement(''); - - this.sandbox.dom.stopPropagation(event); + toggleChildren: function(recordId) { + if (this.table.rows[recordId].childrenLoaded === true) { + if (this.table.rows[recordId].childrenExpanded === true) { + this.hideChildren(recordId); + this.sandbox.emit(CHILDREN_COLLAPSED.call(this)); + } else { + this.showChildren(recordId); + this.sandbox.emit(CHILDREN_EXPANDED.call(this)); + } + } else { + this.loadChildren(recordId); + } + }, - // start loader beneath th - this.sandbox.dom.removeClass($span); - this.sandbox.dom.append($span, $loaderContainer); - this.sandbox.start([ - { - name: 'loader@husky', - options: { - el: $loaderContainer, - size: '10px', - color: '#999999' + /** + * Hides the children of a parent + * @param recordId {Number|String} the id of the parent to hide the children for + */ + hideChildren: function(recordId) { + this.sandbox.util.each(this.table.rows, function(rowId, row) { + if (row.parent == recordId) { + if (row.numberChildren > 0) { + this.hideChildren(rowId); } + this.sandbox.dom.hide(row.$el); } - ]); + }.bind(this)); + this.table.rows[recordId].childrenExpanded = false; + this.changeChildrenToggleIcon(recordId, false); + }, - // delegate sorting to datagrid - this.datagrid.sortGrid.call(this.datagrid, attribute, direction); + /** + * Shows the children of a parent + * @param recordId {Number|String} the id of the parent to show the children for + */ + showChildren: function(recordId) { + this.sandbox.util.each(this.table.rows, function(rowId, row) { + if (row.parent == recordId) { + this.sandbox.dom.show(row.$el); + } + }.bind(this)); + this.table.rows[recordId].childrenExpanded = true; + this.changeChildrenToggleIcon(recordId, true); }, /** - * Removes the dom-element which indicates the list as empty + * Loads the children of a parent and displays them + * @param recordId {Number|String} the id of the parent to load the children for */ - removeEmptyListElement: function() { - this.sandbox.dom.remove(this.sandbox.dom.find('.empty-list', this.$el)); + loadChildren: function(recordId) { + this.datagrid.loadChildren.call(this.datagrid, recordId); + this.table.rows[recordId].childrenLoaded = true; }, - }; + + /** + * Opens all parents of a record + * @param id {Number|String} the id of the record + */ + openParents: function(recordId) { + if (!!this.table.rows[recordId]) { + var parentId = this.table.rows[recordId].parent; + if (!!parentId) { + if (!!this.table.rows[parentId].parent) { + this.openParents(parentId); + } + this.showChildren(parentId); + } + } + } + }; }); /** @@ -30395,6 +30149,7 @@ define('husky_components/datagrid/decorators/group-view',[],function () { * @param {Function} [initialize] function which gets called once at the start of the view * @param {Function} [render] function to render data * @param {Function} [destroy] function to destroy the pagination and unbind events + * @param {Function} [getHeight] function which returns the height of the pagination */ define('husky_components/datagrid/decorators/dropdown-pagination',[],function() { @@ -30411,7 +30166,8 @@ define('husky_components/datagrid/decorators/dropdown-pagination',[],function() prevClass: 'previous', nextClass: 'next', pageChangeClass: 'page-change', - sizeChangeClass: 'size-change' + sizeChangeClass: 'size-change', + loaderClass: 'pagination-loader' }, /** @@ -30433,6 +30189,10 @@ define('husky_components/datagrid/decorators/dropdown-pagination',[],function() '
' ].join(''), + loader: [ + '
' + ].join(''), + showElements: [ '
', '