From 3ff6bb17db00c22155ca0b04ad1f1bf8c2bfc4cc Mon Sep 17 00:00:00 2001 From: Felix Niklas Date: Tue, 27 Oct 2015 10:35:02 +0100 Subject: [PATCH] table resize: stay inside 100% table width - edge case: can excede 100% width because columns have a minimum width --- .../_application_controller_table.coffee | 86 +++++++++++++++++-- .../app/controllers/ticket_overview.coffee | 3 +- .../app/controllers/widget/ticket_list.coffee | 3 +- .../javascripts/app/models/ticket.coffee | 30 +++---- .../app/views/generic/table.jst.eco | 9 +- app/assets/stylesheets/zammad.scss | 1 - 6 files changed, 107 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/app/controllers/_application_controller_table.coffee b/app/assets/javascripts/app/controllers/_application_controller_table.coffee index 784bba761..3c73e244d 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_table.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_table.coffee @@ -1,5 +1,9 @@ class App.ControllerTable extends App.Controller - minColWidth: 20 + minColWidth: 40 + baseColWidth: 130 + + checkBoxColWidth: 40 + radioColWidth: 22 constructor: (params) -> for key, value of params @@ -19,7 +23,7 @@ class App.ControllerTable extends App.Controller @render() render: => - @html(@tableGen()) + @html @tableGen() ### @@ -164,17 +168,23 @@ class App.ControllerTable extends App.Controller for item in overview headerFound = false for attributeName, attribute of attributes + + if !attribute.style + attribute.style = {} + if attributeName is item + # e.g. column: owner headerFound = true if @headerWidth[attribute.name] - attribute.style = "width: #{@headerWidth[attribute.name]}px" + attribute.style.width = "#{@headerWidth[attribute.name]}px" headers.push attribute else + # e.g. column: owner_id rowWithoutId = item + '_id' if attributeName is rowWithoutId headerFound = true if @headerWidth[attribute.name] - attribute.style = "width: #{@headerWidth[attribute.name]}px" + attribute.style.width = "#{@headerWidth[attribute.name]}px" headers.push attribute if @orderDirection && @orderBy @@ -208,6 +218,8 @@ class App.ControllerTable extends App.Controller for callback in @callbackHeader headers = callback(headers) + headers = @adjustHeaderWidths headers + # get content @log 'debug', 'table', 'header', headers, 'overview', 'objects', @objects table = App.view('generic/table')( @@ -332,6 +344,66 @@ class App.ControllerTable extends App.Controller table + adjustHeaderWidths: (headers) -> + widths = @getHeaderWidths headers + difference = widths - @el.width() + + if difference > 0 + # convert percentages to pixels + headers = _.map headers, (col) => + unit = col.style.width.match(/[px|%]+/)[0] + + if unit is '%' + percentage = parseInt col.style.width, 10 + col.style.width = percentage / 100 * @el.width() + "px" + + return col + + widths = @getHeaderWidths headers + shrinkBy = Math.ceil (widths - @el.width()) / @getShrinkableHeadersCount(headers) + + # make all cols evenly smaller + headers = _.map headers, (col) => + if !col.unresizable + value = parseInt col.style.width, 10 + col.style.width = Math.max(@minColWidth, value - shrinkBy) + "px" + return col + + # give left-over space from rounding to last column to get to 100% + roundingLeftOver = @el.width() - @getHeaderWidths headers + # but only if there is something left over (will get negative when there are too many columns for each column to stay in their min width) + if roundingLeftOver > 0 + headers[headers.length - 1].style.width = parseInt(headers[headers.length - 1].style.width, 10) + roundingLeftOver + "px" + + return headers + + getShrinkableHeadersCount: (headers) -> + _.reduce headers, (memo, col) -> + return if col.unresizable then memo else memo+1 + , 0 + + getHeaderWidths: (headers) -> + widths = _.reduce headers, (memo, col, i) => + if col.style && col.style.width + value = parseInt col.style.width, 10 + unit = col.style.width.match(/[px|%]+/)[0] + else + # !!! sets the width to default width if not set + headers[i].style = { width: @baseColWidth+'px' } + value = @baseColWidth + unit = 'px' + + return if unit is 'px' then memo + value else memo + , 0 + + if @checkbox + widths += @checkBoxColWidth + + if @radio + widths += @radioColWidth + + return widths + stopPropagation: (event) => event.stopPropagation() @@ -345,6 +417,8 @@ class App.ControllerTable extends App.Controller $(document).on 'mousemove.resizeCol', @onColResizeMousemove $(document).one 'mouseup', @onColResizeMouseup + @tableWidth = @el.width() + onColResizeMousemove: (event) => # use pixels while moving for max precision difference = event.pageX - @resizeStartX @@ -374,7 +448,9 @@ class App.ControllerTable extends App.Controller @log 'debug', @table_id, 'leftColumnKey', leftColumnKey, leftWidth, 'rightColumnKey', rightColumnKey, rightWidth @preferencesStore('headerWidth', leftColumnKey, leftWidth) - @preferencesStore('headerWidth', rightColumnKey, rightWidth) + + if rightColumnKey + @preferencesStore('headerWidth', rightColumnKey, rightWidth) sortByColumn: (event) => column = $(event.currentTarget).closest('[data-column-key]').attr('data-column-key') diff --git a/app/assets/javascripts/app/controllers/ticket_overview.coffee b/app/assets/javascripts/app/controllers/ticket_overview.coffee index c96b097b9..bd189553c 100644 --- a/app/assets/javascripts/app/controllers/ticket_overview.coffee +++ b/app/assets/javascripts/app/controllers/ticket_overview.coffee @@ -269,7 +269,8 @@ class Table extends App.Controller name: 'icon' display: '' translation: false - style: 'width: 28px' + style: { width: '28px' } + unresizable: true headers.unshift(0) headers[0] = attribute headers diff --git a/app/assets/javascripts/app/controllers/widget/ticket_list.coffee b/app/assets/javascripts/app/controllers/widget/ticket_list.coffee index 616f4b978..ba0bb0f0d 100644 --- a/app/assets/javascripts/app/controllers/widget/ticket_list.coffee +++ b/app/assets/javascripts/app/controllers/widget/ticket_list.coffee @@ -25,7 +25,8 @@ class App.TicketList extends App.Controller name: 'icon' display: '' translation: false - style: 'width: 28px' + style: { width: '28px' } + unresizable: true headers.unshift(0) headers[0] = attribute headers diff --git a/app/assets/javascripts/app/models/ticket.coffee b/app/assets/javascripts/app/models/ticket.coffee index 070a4d284..0beaea296 100644 --- a/app/assets/javascripts/app/models/ticket.coffee +++ b/app/assets/javascripts/app/models/ticket.coffee @@ -3,26 +3,26 @@ class App.Ticket extends App.Model @extend Spine.Model.Ajax @url: @apiPath + '/tickets' @configure_attributes = [ - { name: 'number', display: '#', tag: 'input', type: 'text', limit: 100, null: true, readonly: 1, style: 'width: 68px' }, + { name: 'number', display: '#', tag: 'input', type: 'text', limit: 100, null: true, readonly: 1, style: { 'width': '68px' } }, { name: 'title', display: 'Title', tag: 'input', type: 'text', limit: 100, null: false }, { name: 'customer_id', display: 'Customer', tag: 'input', type: 'text', limit: 100, null: false, autocapitalize: false, relation: 'User' }, { name: 'organization_id', display: 'Organization', tag: 'select', relation: 'Organization', readonly: 1 }, - { name: 'group_id', display: 'Group', tag: 'select', multiple: false, limit: 100, null: false, relation: 'Group', style: 'width: 10%', edit: true }, - { name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, limit: 100, null: true, relation: 'User', style: 'width: 12%', edit: true }, - { name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, relation: 'TicketState', default: 'new', style: 'width: 12%', edit: true, customer: true }, - { name: 'pending_time', display: 'Pending Time', tag: 'datetime', null: true, style: 'width: 130px' }, - { name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, relation: 'TicketPriority', default: '2 normal', style: 'width: 12%', edit: true, customer: true }, - { name: 'article_count', display: 'Article#', readonly: 1, style: 'width: 12%' }, - { name: 'escalation_time', display: 'Escalation', tag: 'datetime', null: true, readonly: 1, style: 'width: 130px', class: 'escalation' }, - { name: 'last_contact', display: 'Last contact', tag: 'datetime', null: true, readonly: 1, style: 'width: 130px' }, - { name: 'last_contact_agent', display: 'Last contact (Agent)', tag: 'datetime', null: true, readonly: 1, style: 'width: 130px' }, - { name: 'last_contact_customer', display: 'Last contact (Customer)', tag: 'datetime', null: true, readonly: 1, style: 'width: 130px' }, - { name: 'first_response', display: 'First response', tag: 'datetime', null: true, readonly: 1, style: 'width: 130px' }, - { name: 'close_time', display: 'Close time', tag: 'datetime', null: true, readonly: 1, style: 'width: 130px' }, + { name: 'group_id', display: 'Group', tag: 'select', multiple: false, limit: 100, null: false, relation: 'Group', style: { 'width': '10%' }, edit: true }, + { name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, limit: 100, null: true, relation: 'User', style: { 'width': '12%' }, edit: true }, + { name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, relation: 'TicketState', default: 'new', style: { 'width': '12%' }, edit: true, customer: true }, + { name: 'pending_time', display: 'Pending Time', tag: 'datetime', null: true, style: { 'width': '130px' } }, + { name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, relation: 'TicketPriority', default: '2 normal', style: { 'width': '12%' }, edit: true, customer: true }, + { name: 'article_count', display: 'Article#', readonly: 1, style: { 'width': '12%' } }, + { name: 'escalation_time', display: 'Escalation', tag: 'datetime', null: true, readonly: 1, style: { 'width': '130px' }, class: 'escalation' }, + { name: 'last_contact', display: 'Last contact', tag: 'datetime', null: true, readonly: 1, style: { 'width': '130px' } }, + { name: 'last_contact_agent', display: 'Last contact (Agent)', tag: 'datetime', null: true, readonly: 1, style: { 'width': '130px' } }, + { name: 'last_contact_customer', display: 'Last contact (Customer)', tag: 'datetime', null: true, readonly: 1, style: { 'width': '130px' } }, + { name: 'first_response', display: 'First response', tag: 'datetime', null: true, readonly: 1, style: { 'width': '130px' } }, + { name: 'close_time', display: 'Close time', tag: 'datetime', null: true, readonly: 1, style: { 'width': '130px' } }, { name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 }, - { name: 'created_at', display: 'Created at', tag: 'datetime', style: 'width: 130px', readonly: 1 }, + { name: 'created_at', display: 'Created at', tag: 'datetime', style: { 'width': '130px', 'text-align': 'right' }, readonly: 1 }, { name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 }, - { name: 'updated_at', display: 'Updated at', tag: 'datetime', style: 'width: 130px', readonly: 1 }, + { name: 'updated_at', display: 'Updated at', tag: 'datetime', style: { 'width': '130px', 'text-align': 'right' }, readonly: 1 }, ] uiUrl: -> diff --git a/app/assets/javascripts/app/views/generic/table.jst.eco b/app/assets/javascripts/app/views/generic/table.jst.eco index 2e9ec84b7..92d0c8bb6 100644 --- a/app/assets/javascripts/app/views/generic/table.jst.eco +++ b/app/assets/javascripts/app/views/generic/table.jst.eco @@ -14,7 +14,12 @@ <% end %> <% for item, i in @header: %> - <%= " style='#{ item.style }'" if item.style %> data-column-key="<%= item.name %>"> + + <% if item.style: %> style="<% for key, value of item.style: %> + <%= key %>: <%= value %>; + <% end %>"<% end %> + data-column-key="<%= item.name %>">
class="table-column-head js-sort"<% end %>>
<%- @T( item.display ) %> @@ -25,7 +30,7 @@ <% end %>
- <% if @table_id && i < @header.length - 1: %> + <% if @table_id && !item.unresizable && i < @header.length: %>
<% end %> diff --git a/app/assets/stylesheets/zammad.scss b/app/assets/stylesheets/zammad.scss index 601066751..c0a336784 100644 --- a/app/assets/stylesheets/zammad.scss +++ b/app/assets/stylesheets/zammad.scss @@ -696,7 +696,6 @@ table { letter-spacing: 0.05em; position: relative; user-select: none; - box-sizing: content-box; } .table-column-head {