table resize: stay inside 100% table width
- edge case: can excede 100% width because columns have a minimum width
This commit is contained in:
parent
2e84756b0e
commit
3ff6bb17db
6 changed files with 107 additions and 25 deletions
|
@ -1,5 +1,9 @@
|
||||||
class App.ControllerTable extends App.Controller
|
class App.ControllerTable extends App.Controller
|
||||||
minColWidth: 20
|
minColWidth: 40
|
||||||
|
baseColWidth: 130
|
||||||
|
|
||||||
|
checkBoxColWidth: 40
|
||||||
|
radioColWidth: 22
|
||||||
|
|
||||||
constructor: (params) ->
|
constructor: (params) ->
|
||||||
for key, value of params
|
for key, value of params
|
||||||
|
@ -19,7 +23,7 @@ class App.ControllerTable extends App.Controller
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
render: =>
|
render: =>
|
||||||
@html(@tableGen())
|
@html @tableGen()
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
@ -164,17 +168,23 @@ class App.ControllerTable extends App.Controller
|
||||||
for item in overview
|
for item in overview
|
||||||
headerFound = false
|
headerFound = false
|
||||||
for attributeName, attribute of attributes
|
for attributeName, attribute of attributes
|
||||||
|
|
||||||
|
if !attribute.style
|
||||||
|
attribute.style = {}
|
||||||
|
|
||||||
if attributeName is item
|
if attributeName is item
|
||||||
|
# e.g. column: owner
|
||||||
headerFound = true
|
headerFound = true
|
||||||
if @headerWidth[attribute.name]
|
if @headerWidth[attribute.name]
|
||||||
attribute.style = "width: #{@headerWidth[attribute.name]}px"
|
attribute.style.width = "#{@headerWidth[attribute.name]}px"
|
||||||
headers.push attribute
|
headers.push attribute
|
||||||
else
|
else
|
||||||
|
# e.g. column: owner_id
|
||||||
rowWithoutId = item + '_id'
|
rowWithoutId = item + '_id'
|
||||||
if attributeName is rowWithoutId
|
if attributeName is rowWithoutId
|
||||||
headerFound = true
|
headerFound = true
|
||||||
if @headerWidth[attribute.name]
|
if @headerWidth[attribute.name]
|
||||||
attribute.style = "width: #{@headerWidth[attribute.name]}px"
|
attribute.style.width = "#{@headerWidth[attribute.name]}px"
|
||||||
headers.push attribute
|
headers.push attribute
|
||||||
|
|
||||||
if @orderDirection && @orderBy
|
if @orderDirection && @orderBy
|
||||||
|
@ -208,6 +218,8 @@ class App.ControllerTable extends App.Controller
|
||||||
for callback in @callbackHeader
|
for callback in @callbackHeader
|
||||||
headers = callback(headers)
|
headers = callback(headers)
|
||||||
|
|
||||||
|
headers = @adjustHeaderWidths headers
|
||||||
|
|
||||||
# get content
|
# get content
|
||||||
@log 'debug', 'table', 'header', headers, 'overview', 'objects', @objects
|
@log 'debug', 'table', 'header', headers, 'overview', 'objects', @objects
|
||||||
table = App.view('generic/table')(
|
table = App.view('generic/table')(
|
||||||
|
@ -332,6 +344,66 @@ class App.ControllerTable extends App.Controller
|
||||||
|
|
||||||
table
|
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) =>
|
stopPropagation: (event) =>
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
|
@ -345,6 +417,8 @@ class App.ControllerTable extends App.Controller
|
||||||
$(document).on 'mousemove.resizeCol', @onColResizeMousemove
|
$(document).on 'mousemove.resizeCol', @onColResizeMousemove
|
||||||
$(document).one 'mouseup', @onColResizeMouseup
|
$(document).one 'mouseup', @onColResizeMouseup
|
||||||
|
|
||||||
|
@tableWidth = @el.width()
|
||||||
|
|
||||||
onColResizeMousemove: (event) =>
|
onColResizeMousemove: (event) =>
|
||||||
# use pixels while moving for max precision
|
# use pixels while moving for max precision
|
||||||
difference = event.pageX - @resizeStartX
|
difference = event.pageX - @resizeStartX
|
||||||
|
@ -374,6 +448,8 @@ class App.ControllerTable extends App.Controller
|
||||||
|
|
||||||
@log 'debug', @table_id, 'leftColumnKey', leftColumnKey, leftWidth, 'rightColumnKey', rightColumnKey, rightWidth
|
@log 'debug', @table_id, 'leftColumnKey', leftColumnKey, leftWidth, 'rightColumnKey', rightColumnKey, rightWidth
|
||||||
@preferencesStore('headerWidth', leftColumnKey, leftWidth)
|
@preferencesStore('headerWidth', leftColumnKey, leftWidth)
|
||||||
|
|
||||||
|
if rightColumnKey
|
||||||
@preferencesStore('headerWidth', rightColumnKey, rightWidth)
|
@preferencesStore('headerWidth', rightColumnKey, rightWidth)
|
||||||
|
|
||||||
sortByColumn: (event) =>
|
sortByColumn: (event) =>
|
||||||
|
|
|
@ -269,7 +269,8 @@ class Table extends App.Controller
|
||||||
name: 'icon'
|
name: 'icon'
|
||||||
display: ''
|
display: ''
|
||||||
translation: false
|
translation: false
|
||||||
style: 'width: 28px'
|
style: { width: '28px' }
|
||||||
|
unresizable: true
|
||||||
headers.unshift(0)
|
headers.unshift(0)
|
||||||
headers[0] = attribute
|
headers[0] = attribute
|
||||||
headers
|
headers
|
||||||
|
|
|
@ -25,7 +25,8 @@ class App.TicketList extends App.Controller
|
||||||
name: 'icon'
|
name: 'icon'
|
||||||
display: ''
|
display: ''
|
||||||
translation: false
|
translation: false
|
||||||
style: 'width: 28px'
|
style: { width: '28px' }
|
||||||
|
unresizable: true
|
||||||
headers.unshift(0)
|
headers.unshift(0)
|
||||||
headers[0] = attribute
|
headers[0] = attribute
|
||||||
headers
|
headers
|
||||||
|
|
|
@ -3,26 +3,26 @@ class App.Ticket extends App.Model
|
||||||
@extend Spine.Model.Ajax
|
@extend Spine.Model.Ajax
|
||||||
@url: @apiPath + '/tickets'
|
@url: @apiPath + '/tickets'
|
||||||
@configure_attributes = [
|
@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: '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: '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: '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: '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: '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: '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: '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: '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: '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: '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', 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_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: '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: '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: '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_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_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: ->
|
uiUrl: ->
|
||||||
|
|
|
@ -14,7 +14,12 @@
|
||||||
<th style="width: 22px"></th>
|
<th style="width: 22px"></th>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% for item, i in @header: %>
|
<% for item, i in @header: %>
|
||||||
<th<%= " class='#{ item.className }'" if item.className %><%= " style='#{ item.style }'" if item.style %> data-column-key="<%= item.name %>">
|
<th
|
||||||
|
<%= " class='#{ item.className }'" if item.className %>
|
||||||
|
<% if item.style: %> style="<% for key, value of item.style: %>
|
||||||
|
<%= key %>: <%= value %>;
|
||||||
|
<% end %>"<% end %>
|
||||||
|
data-column-key="<%= item.name %>">
|
||||||
<div <% if @table_id: %>class="table-column-head js-sort"<% end %>>
|
<div <% if @table_id: %>class="table-column-head js-sort"<% end %>>
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
<%- @T( item.display ) %>
|
<%- @T( item.display ) %>
|
||||||
|
@ -25,7 +30,7 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% if @table_id && i < @header.length - 1: %>
|
<% if @table_id && !item.unresizable && i < @header.length: %>
|
||||||
<div class="table-col-resize js-col-resize"></div>
|
<div class="table-col-resize js-col-resize"></div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</th>
|
</th>
|
||||||
|
|
|
@ -696,7 +696,6 @@ table {
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
box-sizing: content-box;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-column-head {
|
.table-column-head {
|
||||||
|
|
Loading…
Reference in a new issue