4 コミット

作成者 SHA1 メッセージ 日付
  Bing Sun ffca627f48
make_model: fix typo 5年前
  Bing Sun 49f6911143
make_model: support order 5年前
  Bing Sun 6410ce1b5a
Table: support simple pagination 5年前
  Bing Sun 82133fed3b
make_model: be aware of array with empyt slots 5年前
2個のファイルの変更71行の追加110行の削除
分割表示
  1. +54
    -103
      src/components/Table.js
  2. +17
    -7
      src/util/make_model.js

+ 54
- 103
src/components/Table.js ファイルの表示

@@ -8,25 +8,67 @@ export default initial_vnode => {
// component state
let model = initial_vnode.attrs.model

// displayed columns
let columns = initial_vnode.attrs.columns
if (!columns) {
if (model.configs().selects) {
_columns = model.configs().selects.map(select => ({
columns = model.configs().selects.map(select => ({
label: select.alias || select.label
}))
}
}

// pagination
let offset = 0
let limit = initial_vnode.attrs.pagination && initial_vnode.attrs.pagination.size
console.log(limit)
let limit = initial_vnode.attrs.pagination && initial_vnode.attrs.pagination.size || Infinity
let pages = [] // [{activie: boolean, show: boolean}]

function generate_pages() {
if (limit > 0 && limit < Infinity && model.cache().count) {
let active_page = offset / limit

pages = Array.from(Array(Math.floor(model.cache().count / limit) + 1))
.map((_,i) => active_page == i ? {active: true} : {active: false})

if (pages.length <= 10) {
pages.forEach((_, i, pages) => pages[i].show = true)
} else {
// some wicked calculation
let _showed_pages = new Set([0, active_page, pages.length -1])
let l = active_page, r = active_page
while (_showed_pages.size < 10) {
l -= 1
if (l < 0) {
_showed_pages.add(r+1)
r += 1
} else {
_showed_pages.add(l)
}
if (_showed_pages.size < 10) {
r += 1
if (r >= pages.length) {
_showed_pages.add(l-1)
l -= 1
} else {
_showed_pages.add(r)
}
}
}
_showed_pages.forEach(v => pages[v].show = true)
}
}
}

return {
view: vnode => {
// try to generate columns if it is still undefined
// try to generate columns if it is still uninitialized
if (typeof columns == 'undefined' || columns.length == 0) {
columns = model.data() && model.data().length && Object.keys(model.data()[0]).map(key => ({label: key})) || []
columns = model.data(offset, limit) && model.data(offset, limit).length && Object.keys(model.data(offset, limit)[0]).map(key => ({label: key})) || []
}

// refresh paginations
generate_pages()

return [
m('table', [
// always show table header
@@ -34,7 +76,7 @@ export default initial_vnode => {
vnode.attrs.serial ? m('th.centered', '序号') : undefined,
...columns.map(column => m('th.centered', column.label))
])),
model.data(offset).length ? m('tbody', model.data(offset).map((row, i) => m('tr', [
model.data(offset, limit).length ? m('tbody', model.data(offset, limit).map((row, i) => m('tr', [
vnode.attrs.serial ? m('td.centered', offset+i+1) : undefined,
...columns.map(column => {
let v = row[column.label] || ''
@@ -46,107 +88,16 @@ export default initial_vnode => {
})
]))) : m('', 'Empty')
]),
/* _pages.length > 1 ?
m('.centered',
m('.pagination.centered', _pages.map((page, i) => page.show ? m('a', {
class: page.active ? 'active' : '',
onclick: e => {
_offset = i * vnode.attrs.options.paging.size
vnode.attrs.options.model.load({begin: i * vnode.attrs.options.paging.size, end: (i+1) * vnode.attrs.options.paging.size})
}
}, i + 1) : undefined))
)
: undefined*/
]
}
}
}

export const default1 = _ => {
let _columns = []
let _offset= 0
let _pages = []
let _model_slice = []

return {
view: vnode => {
// prepare columns by merging model.selects and columns config
_columns = Array.from(vnode.attrs.options.model.configs().selects, ([label, select]) => ({
label: select.alias || label,
tag: vnode.attrs.options.columns[label] && vnode.attrs.options.columns[label].tag
}))

if (vnode.attrs.options.model.ambient_changed())
_offset = 0

// prepare pages
if (vnode.attrs.options.paging && vnode.attrs.options.model.count) {
_offset = ~~_offset
let _active_page = _offset / vnode.attrs.options.paging.size
_pages = Array.from(Array(vnode.attrs.options.paging.size ? Math.floor(~~vnode.attrs.options.model.count / vnode.attrs.options.paging.size) + 1 : 0)).map((_,i) => _active_page == i ? {active: true} : {active: false})

if (_pages.length <= 10) {
_pages.forEach((page, i, pages) => pages[i].show = true)
} else {
let _showed_pages = new Set([0, _active_page, _pages.length -1])
let l = _active_page, r = _active_page
while (_showed_pages.size < 10) {
l -= 1
if (l < 0) {
_showed_pages.add(r+1)
r += 1
} else {
_showed_pages.add(l)
}
if (_showed_pages.size < 10) {
r += 1
if (r >= _pages.length) {
_showed_pages.add(l-1)
l -= 1
} else {
_showed_pages.add(r)
}
}
}
_showed_pages.forEach(v => _pages[v].show = true)
}
}

// visible model
function get_model_slice() {
return vnode.attrs.options.model.list.slice(_offset, _offset + ~~(vnode.attrs.options.paging && vnode.attrs.options.paging.size || 200))
}

return [
m('table', [
// always show table header
m('thead', m('tr', [
vnode.attrs.options.prepend_serial ? m('th.centered', '序号') : undefined,
..._columns.map(col => m('th.centered', col.label))
])),
get_model_slice().length ? m('tbody', get_model_slice().map((row, i) => m('tr', [
vnode.attrs.options.prepend_serial ? m('td.centered', _offset+i+1) : undefined,
..._columns.map(col => {
let v = row[col.label] || ''

if (typeof col.tag != 'undefined')
v = m(col.tag, v)

return m(`td${col.class || ''}`, v)
})
]))) : m('', 'Empty')
]),
_pages.length > 1 ?
pages.length > 1 ?
m('.centered',
m('.pagination.centered', _pages.map((page, i) => page.show ? m('a', {
m('.pagination.centered', pages.map((page, i) => page.show ? m('a', {
class: page.active ? 'active' : '',
onclick: e => {
_offset = i * vnode.attrs.options.paging.size
vnode.attrs.options.model.load({begin: i * vnode.attrs.options.paging.size, end: (i+1) * vnode.attrs.options.paging.size})
offset = i*limit
model.select(offset, limit)
}
}, i + 1) : undefined))
)
: undefined
}, i+1) : undefined))
) : undefined
]
}
}


+ 17
- 7
src/util/make_model.js ファイルの表示

@@ -30,7 +30,8 @@ export default (options={}) => {
endpoint: options.endpoint,
endpoint_type: options.endpoint.startsWith('/rpc') ? 'function' : 'relation',
// query part
selects: options.selects
selects: options.selects,
order: options.order
}

// private model cache & meta-info
@@ -61,11 +62,8 @@ export default (options={}) => {
limit = _cache.upstream_limit || _cache.count || Infinity

// be lazy 1: if the data is presented, return the value immediately
//
// note that Array.every(list) == true if list is empty
let _portion = _cache.data.slice(offset, offset+limit)
if (_portion.length > 0 && _portion.every(data => data != undefined))
return Promise.resolve(_portion)
if (this.data(offset, limit).length > 0 && !this.data(offset, limit).includes(undefined))
return Promise.resolve(this.data(offset, limit))

// be lazy 2: if there is a promise, return it if ambient matches or
// cancel it
@@ -83,11 +81,24 @@ export default (options={}) => {
queries: [
// transform model state to postgest queries
//...ambient_queries,
// selects
...(_configs.selects ? [{
label: 'select',
value: _configs.selects.map(select => select.alias ? select.alias + ':' + select.label : select.label).join(',')
}] : []),

// order
...(_configs.order ? [{
label: 'order',
value: _configs.order
.map(o => [
o.label,
o.direction,
o.nulls ? 'nulls'+o.nulls : ''
].filter(a => a).join('.'))
.join(',')
}] : []),

// limit/offset
offset == 0 ? undefined : {label: 'offset', value: offset},
limit == Infinity ? undefined : {label: 'limit', value: limit}
@@ -136,7 +147,6 @@ export const default1 = (args) => {
// private model configs
let _configs = {
queries: args.queries || [],
order: args.order || []
}
return {
// model & model state


読み込み中…
キャンセル
保存