waibu-db 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/waibuBootstrap/theme/component/factory/btn-add.js +24 -0
  3. package/waibuBootstrap/theme/component/factory/btn-back.js +22 -0
  4. package/waibuBootstrap/theme/component/factory/btn-clone.js +39 -0
  5. package/waibuBootstrap/theme/component/factory/btn-columns.js +55 -0
  6. package/waibuBootstrap/theme/component/factory/btn-delete.js +52 -0
  7. package/waibuBootstrap/theme/component/factory/btn-details.js +41 -0
  8. package/waibuBootstrap/theme/component/factory/btn-edit.js +67 -0
  9. package/waibuBootstrap/theme/component/factory/btn-export.js +138 -0
  10. package/waibuBootstrap/theme/component/factory/echarts.js +68 -0
  11. package/waibuBootstrap/theme/component/factory/pagination.js +69 -0
  12. package/waibuBootstrap/theme/component/factory/query.js +145 -0
  13. package/waibuBootstrap/theme/component/factory/recs-info.js +58 -0
  14. package/waibuBootstrap/theme/component/factory/table.js +168 -0
  15. package/waibuBootstrap/theme/component/wdb-base.js +6 -0
  16. package/waibuBootstrap/theme/component/btn-add.js +0 -15
  17. package/waibuBootstrap/theme/component/btn-back.js +0 -13
  18. package/waibuBootstrap/theme/component/btn-clone.js +0 -30
  19. package/waibuBootstrap/theme/component/btn-columns.js +0 -45
  20. package/waibuBootstrap/theme/component/btn-delete.js +0 -43
  21. package/waibuBootstrap/theme/component/btn-details.js +0 -32
  22. package/waibuBootstrap/theme/component/btn-edit.js +0 -56
  23. package/waibuBootstrap/theme/component/btn-export.js +0 -129
  24. package/waibuBootstrap/theme/component/echarts.js +0 -58
  25. package/waibuBootstrap/theme/component/pagination.js +0 -60
  26. package/waibuBootstrap/theme/component/query.js +0 -137
  27. package/waibuBootstrap/theme/component/recs-info.js +0 -50
  28. package/waibuBootstrap/theme/component/table.js +0 -161
@@ -1,129 +0,0 @@
1
- async function btnExport (params = {}) {
2
- const { isEmpty, get } = this.plugin.app.bajo.lib._
3
- params.noTag = true
4
- const schema = get(this, 'locals.schema', {})
5
- if (schema.view.disabled.includes('find')) {
6
- params.html = ''
7
- return
8
- }
9
- if (isEmpty(params.attr.launch)) params.attr.launch = this.req.t('Export')
10
- params.attr.launchColor = params.attr.launchColor ?? 'secondary-outline'
11
- params.attr.title = this.req.t('Data Export')
12
- const html = await this.buildSentence(`
13
- <c:div x-data="{
14
- delivery: 'clipboard',
15
- options: [],
16
- ftype: 'json',
17
- toggle (val) {
18
- if (val === 'clipboard') {
19
- $refs.zip.setAttribute('disabled', '')
20
- $refs.xlsx.setAttribute('disabled', '')
21
- $refs.xml.setAttribute('disabled', '')
22
- _.pull(this.options, 'zip')
23
- if (!['json', 'csv'].includes(this.ftype)) this.ftype = 'json'
24
- } else {
25
- $refs.zip.removeAttribute('disabled')
26
- $refs.xlsx.removeAttribute('disabled')
27
- $refs.xml.removeAttribute('disabled')
28
- }
29
- },
30
- extractForm (selector) {
31
- let item = {}
32
- const els = document.querySelectorAll(selector + ' [data-value]')
33
- for (const el of els) {
34
- const value = this.options.includes('fvalue') ? el.getAttribute('value') : wmpa.parseValue(el.dataset.value, el.dataset.type)
35
- let key = el.getAttribute('name')
36
- if (this.options.includes('fkey')) {
37
- try {
38
- const elLabel = document.querySelector('label[for=' + el.getAttribute('id') + ']')
39
- key = elLabel.innerText
40
- } catch (err) {}
41
- }
42
- item[key] = value
43
- }
44
- return this.ftype === 'csv' ? CSVJSON.json2csv(item) : JSON.stringify(item)
45
- },
46
- extractTable (selector) {
47
- let items = []
48
- let checker = false
49
- const keys = []
50
- const types = []
51
- let els = document.querySelectorAll(selector + ' thead th')
52
- for (const el of els) {
53
- keys.push(this.options.includes('fkey') ? el.innerText : el.dataset.key)
54
- types.push(el.dataset.type)
55
- }
56
- if (_.isEmpty(keys[0])) {
57
- checker = true
58
- keys.shift()
59
- types.shift()
60
- }
61
- els = document.querySelectorAll(selector + ' tbody tr')
62
- for (const el of els) {
63
- let data = []
64
- _.each(el.children, (v, i) => {
65
- if ((i + '') === '0' && checker) return undefined
66
- data.push(this.options.includes('fvalue') ? v.innerText : wmpa.parseValue(v.dataset.value, types[parseInt(i - 1)]))
67
- })
68
- const item = {}
69
- for (const i in keys) {
70
- item[keys[i]] = data[i]
71
- }
72
- items.push(item)
73
- }
74
- return this.ftype === 'csv' ? CSVJSON.json2csv(items) : JSON.stringify(items)
75
- },
76
- async submit () {
77
- const instance = wbs.getInstance('Modal', $refs.export)
78
- const handler = '${params.attr.handler ?? ''}'
79
- if (this.delivery === 'clipboard') {
80
- const selector = '${params.attr.selector}'
81
- if (_.isEmpty(selector)) {
82
- await wbs.notify('Cant get data selector', { type: 'danger' })
83
- } else {
84
- const item = handler === 'list' ? this.extractTable(selector) : this.extractForm(selector)
85
- await wbs.copyToClipboard(item)
86
- }
87
- instance.hide()
88
- return
89
- }
90
- wmpa.postForm({ options: this.options.join(','), ftype: this.ftype, handler }, '${this._buildUrl({ base: 'export' })}')
91
- instance.hide()
92
- }
93
- }" x-init="
94
- toggle(delivery)
95
- $watch('delivery', val => toggle(val))
96
- ">
97
- <c:grid-row gutter="2">
98
- <c:grid-col col="6-md">
99
- <c:fieldset t:legend="Delivery" legend-type="6">
100
- <c:form-radio x-model="delivery" value="file" t:label="Save as File" />
101
- <c:form-radio x-model="delivery" value="clipboard" t:label="Copy to Clipboard" />
102
- </c:fieldset>
103
- <c:fieldset t:legend="Options" legend-type="6" margin="top-2">
104
- <c:form-check x-ref="fkey" x-model="options" value="fkey" t:label="Formatted Field" />
105
- <c:form-check x-ref="fvalue" x-model="options" value="fvalue" t:label="Formatted Value" />
106
- <c:form-check x-ref="zip" x-model="options" value="zip" t:label="Zipped" />
107
- </c:fieldset>
108
- </c:grid-col>
109
- <c:grid-col col="6-md">
110
- <c:fieldset t:legend="File Type" legend-type="6">
111
- <c:form-radio x-ref="xlsx" x-model="ftype" value="xlsx" t:label="Excel XLSX" />
112
- <c:form-radio x-ref="csv" x-model="ftype" value="csv" t:label="CSV" />
113
- <c:form-radio x-ref="xml" x-model="ftype" value="xml" t:label="XML" />
114
- <c:form-radio x-ref="json" x-model="ftype" value="json" t:label="JSON" />
115
- </c:fieldset />
116
- </c:grid-col>
117
- </c:grid-row>
118
- <c:div flex="justify-content:end" margin="top-3">
119
- <c:btn color="secondary" t:content="Close" dismiss />
120
- <c:btn color="primary" t:content="Submit" margin="start-2" @click="await submit()" />
121
- </c:div>
122
- </c:div>
123
- `)
124
- params.attr['x-data'] = true
125
- params.attr['x-ref'] = 'export'
126
- params.html = await this.buildTag({ tag: 'modal', attr: params.attr, html })
127
- }
128
-
129
- export default btnExport
@@ -1,58 +0,0 @@
1
- const defOption = {
2
- grid: {
3
- top: 8,
4
- bottom: 20,
5
- left: 25,
6
- right: 0
7
- }
8
- }
9
-
10
- const chart = {
11
- scripts: [
12
- '^waibuDb.virtual:/echarts/echarts.min.js'
13
- ],
14
- handler: async function (params = {}) {
15
- const { defaultsDeep, generateId } = this.plugin.app.bajo
16
- const { base64JsonDecode, jsonStringify } = this.plugin.app.waibuMpa
17
- const { cloneDeep } = this.plugin.app.bajo.lib._
18
- this._normalizeAttr(params, { tag: 'div' })
19
- params.attr.dim = params.attr.dim ?? 'width:100 height:100'
20
- params.attr.id = generateId('alpha')
21
- params.attr['x-data'] = `chart${params.attr.id}`
22
- params.attr['@resize.window.debounce.500ms'] = `
23
- if (chart) {
24
- chart.resize()
25
- }
26
- `
27
- let option = cloneDeep(defOption)
28
- if (params.attr.option === true) params.attr.option = 'e30='
29
- if (params.attr.option) option = defaultsDeep(base64JsonDecode(params.attr.option), defOption)
30
- params.attr['x-init'] = `
31
- $watch('option', val => {
32
- if (chart) chart.setOption(val)
33
- })
34
- `
35
- params.append = `
36
- <script>
37
- document.addEventListener('alpine:init', () => {
38
- Alpine.data('chart${params.attr.id}', () => {
39
- let chart
40
- return {
41
- init () {
42
- const el = document.getElementById('${params.attr.id}')
43
- chart = echarts.init(el, null, { renderer: 'canvas' })
44
- chart.setOption(this.option)
45
- },
46
- get chart () {
47
- return chart
48
- },
49
- option: ${jsonStringify(option, true)}
50
- }
51
- })
52
- })
53
- </script>
54
- `
55
- }
56
- }
57
-
58
- export default chart
@@ -1,60 +0,0 @@
1
- export function getUrlOpts (params = {}) {
2
- const { get } = this.plugin.app.bajo.lib._
3
- return {
4
- params,
5
- excludes: [
6
- get(this, 'plugin.app.waibu.config.qsKey.lang', 'lang'),
7
- get(this, 'plugin.app.waibuMpa.config.darkMode.qsKey', 'dark-mode')
8
- ]
9
- }
10
- }
11
-
12
- async function pagination (params = {}) {
13
- const { attrToObject, paginationLayout, groupAttrs } = this.plugin.app.waibuMpa
14
- const { get, isNumber } = this.plugin.app.bajo.lib._
15
- const schema = get(this, 'locals.schema', {})
16
- if (schema.view.disabled.includes('find')) {
17
- params.html = ''
18
- return
19
- }
20
- let { count, limit, page } = attrToObject(params.attr.options)
21
- count = count ?? get(this, 'locals.list.count', 0)
22
- limit = limit ?? get(this, 'locals.list.limit', 25)
23
- page = page ?? get(this, 'locals.list.page', 1)
24
- const pages = paginationLayout(count, limit, page) ?? []
25
- params.noTag = true
26
- const group = groupAttrs(params.attr, ['pagination'])
27
- const html = []
28
- let icon
29
- let attr
30
- if (params.attr.first) {
31
- icon = await this.buildTag({ tag: 'icon', attr: { name: params.attr.firstIcon ?? 'playSkipStart' } })
32
- attr = { disabled: page <= pages[0], href: this._buildUrl(getUrlOpts.call(this, { page: 1 })) }
33
- html.push(await this.buildTag({ tag: 'paginationItem', attr, html: icon }))
34
- }
35
- if (params.attr.prev) {
36
- icon = await this.buildTag({ tag: 'icon', attr: { name: params.attr.prevIcon ?? 'playFastBackward' } })
37
- attr = { disabled: page <= pages[0], href: this._buildUrl(getUrlOpts.call(this, { page: page - 1 })) }
38
- html.push(await this.buildTag({ tag: 'paginationItem', attr, html: icon }))
39
- }
40
- if (!params.attr.noPages) {
41
- for (const p of pages) {
42
- attr = { disabled: p === '...', href: this._buildUrl(getUrlOpts.call(this, { page: p })), active: p === page }
43
- html.push(await this.buildTag({ tag: 'paginationItem', attr, html: isNumber(p) ? this.req.format(p, 'integer') : p }))
44
- }
45
- }
46
- if (params.attr.next) {
47
- icon = await this.buildTag({ tag: 'icon', attr: { name: params.attr.nextIcon ?? 'playFastForward' } })
48
- attr = { disabled: page >= pages[pages.length - 1], href: this._buildUrl(getUrlOpts.call(this, { page: page + 1 })) }
49
- html.push(await this.buildTag({ tag: 'paginationItem', attr, html: icon }))
50
- }
51
- if (params.attr.last) {
52
- icon = await this.buildTag({ tag: 'icon', attr: { name: params.attr.lastIcon ?? 'playSkipEnd' } })
53
- attr = { disabled: page >= pages[pages.length - 1], href: this._buildUrl(getUrlOpts.call(this, { page: pages[pages.length - 1] })) }
54
- html.push(await this.buildTag({ tag: 'paginationItem', attr, html: icon }))
55
- }
56
- params.attr = group.pagination
57
- params.html = await this.buildTag({ tag: 'pagination', attr: params.attr, html: html.join('\n') })
58
- }
59
-
60
- export default pagination
@@ -1,137 +0,0 @@
1
- async function query (params = {}) {
2
- const { generateId } = this.plugin.app.bajo
3
- const { jsonStringify } = this.plugin.app.waibuMpa
4
- const { find, get, without, isEmpty, filter, upperFirst } = this.plugin.app.bajo.lib._
5
- const qsKey = this.plugin.app.waibu.config.qsKey
6
- const schema = get(this, 'locals.schema', {})
7
- if (schema.view.disabled.includes('find')) {
8
- params.html = ''
9
- return
10
- }
11
- let fields = without(get(this, `locals._meta.query.${qsKey.fields}`, '').split(','), '')
12
- if (isEmpty(fields)) fields = schema.view.fields
13
- fields = filter(fields, f => schema.sortables.includes(f))
14
- const id = generateId('alpha')
15
- const columns = []
16
- const models = []
17
- const selects = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'contains', 'starts', 'ends', '!in', '!contains', '!starts', '!ends']
18
- for (const f of schema.view.fields) {
19
- if (!fields.includes(f)) continue
20
- const prop = find(schema.properties, { name: f })
21
- const ops = []
22
- if (['float', 'double', 'integer', 'smallint'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
23
- else if (['datetime', 'date', 'time'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
24
- else if (['boolean'].includes(prop.type)) ops.push('eq', 'neq')
25
- else ops.push(...selects)
26
- if (ops.length === 0) continue
27
- const sels = ops.map(o => `<c:option>${o}</c:option>`)
28
- models.push(`${f}Op: 'eq'`, `${f}Val: ''`)
29
- columns.push(`
30
- <c:grid-col col="4-md" flex="align-items:center">
31
- <c:form-check x-model="selected" t:label="field.${f}" value="${f}" />
32
- </c:grid-col>
33
- <c:grid-col col="3-md">
34
- <c:form-select x-model="${f}Op">
35
- ${sels.join('\n')}
36
- </c:form-select>
37
- </c:grid-col>
38
- <c:grid-col col="5-md">
39
- <c:form-input x-model="${f}Val" />
40
- </c:grid-col>
41
- `)
42
- }
43
- params.noTag = true
44
- const container = params.attr.modal ? 'modal' : 'drawer'
45
- params.html = await this.buildSentence(`
46
- <c:form-input type="search" t:placeholder="Query" id="${id}" x-data="{ query: '' }" x-init="
47
- const url = new URL(window.location.href)
48
- query = url.searchParams.get('${qsKey.query}') ?? ''
49
- " x-model="query" @on-query.window="query = $event.detail ?? ''" @keyup.enter="$dispatch('on-submit')">
50
- <c:form-input-addon>
51
- <c:${container} launch-icon="${params.attr.icon ?? 'dotsThree'}" launch-on-end t:title="Query Builder" x-ref="query" x-data="{
52
- fields: ${jsonStringify(fields, true)},
53
- builder: '',
54
- selected: [],
55
- ${models.join(',\n')},
56
- ops: { eq: ':', neq: ':-', gt: ':>', gte: ':>=', lt: ':<', lte: ':<=' },
57
- opsIn (v, neg) { return ':' + (neg ? '-' : '') + '[' + this.expandArray(v) + ']' },
58
- opsExt (v, neg, ext) {
59
- let prefix = (neg ? '-' : '') + '~'
60
- if (ext) prefix += ext
61
- return ':' + prefix + '\\'' + v + '\\''
62
- },
63
- expandArray (val = '') {
64
- return _.map(val.split(','), item => {
65
- item = _.trim(item)
66
- if (Number(item)) return item
67
- return '\\'' + item + '\\''
68
- })
69
- },
70
- rebuild () {
71
- const items = []
72
- for (const sel of this.selected) {
73
- const key = this[sel + 'Op']
74
- let val = this[sel + 'Val']
75
- if (_.isEmpty(val)) continue
76
- let item
77
- if (key === 'in') item = this.opsIn(val)
78
- else if (key === '!in') item = this.opsIn(val, true)
79
- else if (key === 'contains') item = this.opsExt(val)
80
- else if (key === '!contains') item = this.opsExt(val, true)
81
- else if (key === 'starts') item = this.opsExt(val, false, '^')
82
- else if (key === '!starts') item = this.opsExt(val, true, '^')
83
- else if (key === 'ends') item = this.opsExt(val, false, '$$')
84
- else if (key === '!ends') item = this.opsExt(val, true, '$$')
85
- else if (val.includes(' ')) item = this.ops[key] + '\\'' + val + '\\''
86
- else item = this.ops[key] + val
87
- items.push(sel + item)
88
- }
89
- this.builder = items.join('+')
90
- },
91
- submit (run) {
92
- if (run) {
93
- const url = new URL(window.location.href)
94
- const params = new URLSearchParams(url.search)
95
- params.set('${qsKey.page}', 1)
96
- params.set('${qsKey.query}', this.builder ?? '')
97
- window.location.href = '?' + params.toString()
98
- } else $dispatch('on-query', this.builder)
99
- const instance = wbs.getInstance('${upperFirst(container)}', $refs.query)
100
- instance.hide()
101
- }
102
- }" x-init="
103
- const ops = _.map(fields, f => (f + 'Op'))
104
- const vals = _.map(fields, f => (f + 'Val'))
105
- const watcher = ['selected', ...ops, ...vals].join(',')
106
- $watch(watcher, v => rebuild())
107
- ">
108
- <c:grid-row gutter="2">
109
- <c:grid-col col="12">
110
- <c:form-textarea x-model="builder" readonly rows="4"/>
111
- </c:grid-col>
112
- ${columns.join('\n')}
113
- </c:grid-row>
114
- <c:div flex="justify-content:end" margin="top-3">
115
- <c:btn color="secondary" t:content="Close" dismiss="${container}" />
116
- <c:btn color="primary" t:content="Apply" margin="start-2" @click="submit()" />
117
- <c:btn color="primary" t:content="Submit Query" margin="start-2" @click="submit(true)" />
118
- </c:div>
119
- </c:${container}>
120
- </c:form-input-addon>
121
- <c:form-input-addon>
122
- <c:btn t:content="Submit" x-data="{
123
- submit () {
124
- const val = document.getElementById('${id}').value ?? ''
125
- const url = new URL(window.location.href)
126
- const params = new URLSearchParams(url.search)
127
- params.set('${qsKey.page}', 1)
128
- params.set('${qsKey.query}', val)
129
- window.location.href = '?' + params.toString()
130
- }
131
- }" @click="submit" @on-submit.window="submit()" />
132
- </c:form-input-addon>
133
- </c:form-input>
134
- `)
135
- }
136
-
137
- export default query
@@ -1,50 +0,0 @@
1
- import { getUrlOpts } from './pagination.js'
2
-
3
- async function recordsInfo (params = {}) {
4
- const { attrToObject, groupAttrs, attrToArray } = this.plugin.app.waibuMpa
5
- const { get, isEmpty, omit, merge } = this.plugin.app.bajo.lib._
6
- const schema = get(this, 'locals.schema', {})
7
- if (schema.view.disabled.includes('find')) {
8
- params.html = ''
9
- return
10
- }
11
- let { count, limit, page, pages } = attrToObject(params.attr.options)
12
- count = count ?? get(this, 'locals.list.count', 0)
13
- page = page ?? get(this, 'locals.list.page', 1)
14
- limit = limit ?? get(this, 'locals.list.limit', 25)
15
- pages = pages ?? get(this, 'locals.list.pages', 0)
16
-
17
- params.tag = 'div'
18
- params.attr.flex = 'justify-center:start align-items:center'
19
- if (count === 0) {
20
- params.html = this.req.t('No record found')
21
- return
22
- }
23
- if (!params.attr.dropdown) params.attr.dropdown = true
24
- const group = groupAttrs(params.attr, ['dropdown'])
25
- const html = []
26
- if (params.attr.count) html.push(this.req.t('%s record(s) found', this.req.format(count, 'integer')))
27
- if (params.attr.pages) {
28
- if (!isEmpty(html)) html[html.length - 1] += '.'
29
- html.push(this.req.t('Page %s of %s pages', this.req.format(page, 'integer'), this.req.format(pages, 'integer')))
30
- }
31
- if (params.attr.recsPerPage) {
32
- params.attr.recsPerPageValues = params.attr.recsPerPageValues ?? '10 25 50'
33
- params.attr.recsPerPageValues = attrToArray(params.attr.recsPerPageValues)
34
- if (!isEmpty(html)) html[html.length - 1] += ','
35
- const items = []
36
- for (const i of params.attr.recsPerPageValues) {
37
- const attr = { href: this._buildUrl(merge(getUrlOpts.call(this), { params: { limit: i, page: 1 } })), disabled: i === limit }
38
- items.push(await this.buildTag({ tag: 'dropdownItem', attr, html: i + '' }))
39
- }
40
- const attr = group.dropdown
41
- attr.content = limit + ''
42
- attr.color = attr.color ?? 'secondary-outline'
43
- html.push(await this.buildTag({ tag: 'dropdown', attr, html: items.join('\n') }))
44
- html.push(' ', this.req.t('recs per page'))
45
- }
46
- params.attr = omit(params.attr, ['count', 'pages', 'recsPerPage', 'dropdown', 'recsPerPageValues'])
47
- params.html = html.map(h => `<div class="me-1">${h}</div>`).join('\n')
48
- }
49
-
50
- export default recordsInfo
@@ -1,161 +0,0 @@
1
- function isRightAligned (type) {
2
- return ['smallint', 'integer', 'float', 'double'].includes(type)
3
- }
4
-
5
- const table = {
6
- handler: async function (params = {}) {
7
- const { escape } = this.plugin.app.waibu
8
- const { attrToArray, groupAttrs } = this.plugin.app.waibuMpa
9
- const { get, omit, set, find, isEmpty, without } = this.plugin.app.bajo.lib._
10
- const group = groupAttrs(params.attr, ['body', 'head', 'foot'])
11
- params.attr = group._
12
-
13
- const data = get(this, 'locals.list.data', [])
14
- const schema = get(this, 'locals.schema', {})
15
- if (schema.view.disabled.includes('find')) {
16
- params.html = ''
17
- return
18
- }
19
- const qsKey = this.plugin.app.waibu.config.qsKey
20
- let fields = without(get(this, `locals._meta.query.${qsKey.fields}`, '').split(','), '')
21
- if (isEmpty(fields)) fields = schema.view.fields
22
- const sort = params.attr.sort ? attrToArray(params.attr.sort) : get(this, `locals._meta.query.${qsKey.sort}`, '')
23
-
24
- let [sortCol, sortDir] = sort.split(':')
25
- if (!['-1', '1'].includes(sortDir)) sortDir = '1'
26
-
27
- let selection
28
- const canDelete = !schema.view.disabled.includes('remove')
29
- const canEdit = !schema.view.disabled.includes('update')
30
- if (canEdit) selection = 'single'
31
- if (canDelete) selection = 'multi'
32
- if (selection) params.attr.hover = true
33
-
34
- params.noTag = true
35
- const html = []
36
- let items = []
37
- // head
38
- for (const f of schema.view.fields) {
39
- if (!fields.includes(f)) continue
40
- const prop = find(schema.properties, { name: f })
41
- let head = this.req.t(`field.${f}`)
42
- if (!params.attr.noSort && (schema.sortables ?? []).includes(f)) {
43
- let sortItem = `${f}:-1`
44
- let icon = params.attr.sortUpIcon ?? 'caretUp'
45
- if (f === sortCol) {
46
- sortItem = `${f}:${sortDir === '1' ? '-1' : '1'}`
47
- icon = sortDir === '1' ? (params.attr.sortUpIcon ?? 'caretUp') : (params.attr.sortDownIcon ?? 'caretDown')
48
- }
49
- const item = set({ page: 1 }, qsKey.sort, sortItem)
50
- const href = this._buildUrl({ params: item })
51
- const attr = isRightAligned(prop.type) ? { text: 'align:end' } : {}
52
- const content = [
53
- await this.buildTag({ tag: 'div', attr, html: this.req.t(`field.${f}`) }),
54
- await this.buildTag({ tag: 'a', attr: { icon, href }, prepend: '<div class="ms-1">', append: '</div>' })
55
- ]
56
- head = await this.buildTag({ tag: 'div', attr: { flex: 'justify-content:between align-items:end' }, html: content.join('\n') })
57
- }
58
- let text = params.attr.headerNowrap ? '' : 'nowrap'
59
- if (isRightAligned(prop.type)) text += ' align:end'
60
- const attr = { dataKey: f, dataType: prop.type, text }
61
- items.push(await this.buildTag({ tag: 'th', attr, html: head }))
62
- }
63
- if (items.length > 0 && selection) {
64
- let item = '<th></th>'
65
- if (selection === 'multi') {
66
- const attr = { 'x-model': 'toggleAll', name: '_rtm', noWrapper: true, noLabel: true }
67
- item = await this.buildTag({ tag: 'formCheck', attr, prepend: '<th>', append: '</th>' })
68
- } else {
69
- const attr = { name: 'remove', '@click': 'selected = \'\'', style: { cursor: 'pointer' } }
70
- item = await this.buildTag({ tag: 'icon', attr, prepend: '<th>', append: '</th>' })
71
- }
72
- items.unshift(item)
73
- }
74
- const header = await this.buildTag({ tag: 'tr', html: items.join('\n') })
75
- html.push(await this.buildTag({ tag: 'thead', attr: group.head, html: header }))
76
- // body
77
- items = []
78
- for (const d of data) {
79
- const lines = []
80
- if (selection) {
81
- const tag = selection === 'single' ? 'formRadio' : 'formCheck'
82
- const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
83
- lines.push(await this.buildTag({ tag, attr, prepend: '<td>', append: '</td>' }))
84
- }
85
- for (const f of schema.view.fields) {
86
- const prop = find(schema.properties, { name: f })
87
- if (!fields.includes(f)) continue
88
- const opts = {}
89
- if (f === 'lng') opts.longitude = true
90
- else if (f === 'lat') opts.latitude = true
91
- let value = this.req.format(d[f], prop.type, opts)
92
- if (prop.type === 'boolean') {
93
- value = (await this.buildTag({ tag: 'icon', attr: { name: `circle${d[f] ? 'Check' : ''}` } })) +
94
- ' ' + (this.req.t(d[f] ? 'Yes' : 'No'))
95
- } else value = escape(value)
96
- let dataValue = d[f] ?? ''
97
- if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
98
- if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
99
- const attr = { dataValue }
100
- if (!['object', 'array'].includes(prop.type)) {
101
- if (isRightAligned(prop.type)) attr.text = 'align:end nowrap'
102
- else attr.text = 'nowrap'
103
- }
104
- const line = await this.buildTag({ tag: 'td', attr, html: value })
105
- lines.push(line)
106
- }
107
- const attr = {}
108
- if (!schema.view.disabled.includes('update') || !schema.view.disabled.includes('remove')) attr['@click'] = `toggle('${d.id}')`
109
- if (!schema.view.disabled.includes('get')) attr['@dblclick'] = `goDetails('${d.id}')`
110
- items.push(await this.buildTag({ tag: 'tr', attr, html: lines.join('\n') }))
111
- }
112
- html.push(await this.buildTag({ tag: 'tbody', attr: group.body, html: items.join('\n') }))
113
- params.attr = omit(params.attr, ['sortUpIcon', 'sortDownIcon', 'noSort', 'selection', 'headerNowrap'])
114
- const goDetails = `
115
- goDetails (id) {
116
- window.location.href = '${this._buildUrl({ base: 'details' })}&id=' + id
117
- }
118
- `
119
- if (selection === 'multi') {
120
- params.attr['x-data'] = `{
121
- toggleAll: false,
122
- selected: [],
123
- toggle (id) {
124
- if (this.selected.includes(id)) {
125
- const idx = this.selected.indexOf(id)
126
- this.selected.splice(idx, 1)
127
- } else this.selected.push(id)
128
- },
129
- ${goDetails}
130
- }`
131
- params.attr['x-init'] = `
132
- $watch('toggleAll', val => {
133
- if (val) {
134
- const els = document.getElementsByName('_rt')
135
- const items = Array.from(els)
136
- selected = items.map(el => el.value)
137
- } else selected = []
138
- })
139
- $watch('selected', val => $dispatch('on-selection', val))
140
- `
141
- } else if (selection === 'single') {
142
- params.attr['x-data'] = `{
143
- selected: '',
144
- toggle (id) {
145
- this.selected = id
146
- },
147
- ${goDetails}
148
- }`
149
- params.attr['x-init'] = `
150
- $watch('selected', val => $dispatch('on-selection', [val]))
151
- `
152
- } else {
153
- params.attr['x-data'] = `{
154
- ${goDetails}
155
- }`
156
- }
157
- params.html = await this.buildTag({ tag: 'table', attr: params.attr, html: html.join('\n') })
158
- }
159
- }
160
-
161
- export default table