waibu-db 1.0.1 → 1.0.3

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 +67 -0
  11. package/waibuBootstrap/theme/component/factory/pagination.js +69 -0
  12. package/waibuBootstrap/theme/component/factory/query.js +177 -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
@@ -0,0 +1,177 @@
1
+ import wdbBase from '../wdb-base.js'
2
+
3
+ async function query () {
4
+ const WdbBase = await wdbBase.call(this)
5
+
6
+ return class WdbQuery extends WdbBase {
7
+ async build () {
8
+ const { generateId } = this.plugin.app.bajo
9
+ const { jsonStringify } = this.plugin.app.waibuMpa
10
+ const { find, get, without, isEmpty, filter, upperFirst } = this.plugin.app.bajo.lib._
11
+ const qsKey = this.plugin.app.waibu.config.qsKey
12
+ const schema = get(this, 'component.locals.schema', {})
13
+ if (schema.view.disabled.includes('find')) {
14
+ this.params.html = ''
15
+ return
16
+ }
17
+ let fields = without(get(this, `component.locals._meta.query.${qsKey.fields}`, '').split(','), '')
18
+ if (isEmpty(fields)) fields = schema.view.fields
19
+ fields = filter(fields, f => schema.sortables.includes(f))
20
+ const id = generateId('alpha')
21
+ const columns = []
22
+ const models = []
23
+ const selects = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'contains', 'starts', 'ends', '!in', '!contains', '!starts', '!ends']
24
+ for (const f of schema.view.fields) {
25
+ if (!fields.includes(f)) continue
26
+ const prop = find(schema.properties, { name: f })
27
+ const ops = []
28
+ if (['float', 'double', 'integer', 'smallint'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
29
+ else if (['datetime', 'date', 'time'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
30
+ else if (['boolean'].includes(prop.type)) ops.push('eq', 'neq')
31
+ else ops.push(...selects)
32
+ if (ops.length === 0) continue
33
+ const sels = ops.map(o => `<c:option>${o}</c:option>`)
34
+ models.push(`${f}Op: 'eq'`, `${f}Val: ''`)
35
+ columns.push(`
36
+ <c:grid-col col="4-md" flex="align-items:center">
37
+ <c:form-check x-model="selected" t:label="field.${f}" value="${f}" />
38
+ </c:grid-col>
39
+ <c:grid-col col="3-md">
40
+ <c:form-select x-model="${f}Op">
41
+ ${sels.join('\n')}
42
+ </c:form-select>
43
+ </c:grid-col>
44
+ <c:grid-col col="5-md">
45
+ <c:form-input x-model="${f}Val" />
46
+ </c:grid-col>
47
+ `)
48
+ }
49
+ this.params.noTag = true
50
+ const container = this.params.attr.modal ? 'modal' : 'drawer'
51
+ this.params.html = await this.component.buildSentence(`
52
+ <c:form-input type="search" t:placeholder="Query" id="${id}" x-data="{ query: '' }" x-init="
53
+ const url = new URL(window.location.href)
54
+ query = url.searchParams.get('${qsKey.query}') ?? ''
55
+ " x-model="query" @on-query.window="query = $event.detail ?? ''" @keyup.enter="$dispatch('on-submit')">
56
+ <c:form-input-addon>
57
+ <c:${container} launch-icon="${this.params.attr.icon ?? 'dotsThree'}" launch-on-end t:title="Query Builder" x-ref="query" x-data="{
58
+ fields: ${jsonStringify(fields, true)},
59
+ builder: '',
60
+ selected: [],
61
+ ${models.join(',\n')},
62
+ ops: { eq: ':', neq: ':-', gt: ':>', gte: ':>=', lt: ':<', lte: ':<=' },
63
+ opsIn (v, neg) { return ':' + (neg ? '-' : '') + '[' + this.expandArray(v) + ']' },
64
+ opsExt (v, neg, ext) {
65
+ let prefix = (neg ? '-' : '') + '~'
66
+ if (ext) prefix += ext
67
+ return ':' + prefix + '\\'' + v + '\\''
68
+ },
69
+ initBuilder () {
70
+ this.builder = document.getElementById('${id}').value
71
+ if (_.isEmpty(this.builder)) return
72
+ const tokens = _.merge({}, this.ops, {
73
+ in: ':[',
74
+ contains: ':~',
75
+ starts: ':~^',
76
+ ends: ':~$$',
77
+ '!in': ':-[',
78
+ '!contains': ':-~',
79
+ '!starts': ':-~^',
80
+ '!ends': ':-~$$'
81
+ })
82
+ for (const part of this.builder.split('+')) {
83
+ let [f, opv] = part.split(':')
84
+ opv = ':' + opv
85
+ this.selected.push(f)
86
+ let op
87
+ let val
88
+ _.each(tokens, (v, k) => {
89
+ if (opv.slice(0, v.length) === v) {
90
+ op = k
91
+ val = opv.slice(v.length).replaceAll('[', '').replaceAll(']', '').replaceAll('\\'', '')
92
+ }
93
+ })
94
+ console.log(op, val)
95
+ if (_.isEmpty(op)) continue
96
+ this[f + 'Op'] = op
97
+ this[f + 'Val'] = val
98
+ }
99
+ },
100
+ expandArray (val = '') {
101
+ return _.map(val.split(','), item => {
102
+ item = _.trim(item)
103
+ if (Number(item)) return item
104
+ return '\\'' + item + '\\''
105
+ })
106
+ },
107
+ rebuild () {
108
+ const items = []
109
+ for (const sel of this.selected) {
110
+ const key = this[sel + 'Op']
111
+ let val = this[sel + 'Val']
112
+ if (_.isEmpty(val)) continue
113
+ let item
114
+ if (key === 'in') item = this.opsIn(val)
115
+ else if (key === '!in') item = this.opsIn(val, true)
116
+ else if (key === 'contains') item = this.opsExt(val)
117
+ else if (key === '!contains') item = this.opsExt(val, true)
118
+ else if (key === 'starts') item = this.opsExt(val, false, '^')
119
+ else if (key === '!starts') item = this.opsExt(val, true, '^')
120
+ else if (key === 'ends') item = this.opsExt(val, false, '$$')
121
+ else if (key === '!ends') item = this.opsExt(val, true, '$$')
122
+ else if (val.includes(' ')) item = this.ops[key] + '\\'' + val + '\\''
123
+ else item = this.ops[key] + val
124
+ items.push(sel + item)
125
+ }
126
+ this.builder = items.join('+')
127
+ },
128
+ submit (run) {
129
+ if (run) {
130
+ const url = new URL(window.location.href)
131
+ const params = new URLSearchParams(url.search)
132
+ params.set('${qsKey.page}', 1)
133
+ params.set('${qsKey.query}', this.builder ?? '')
134
+ window.location.href = '?' + params.toString()
135
+ } else $dispatch('on-query', this.builder)
136
+ const instance = wbs.getInstance('${upperFirst(container)}', $refs.query)
137
+ instance.hide()
138
+ }
139
+ }" x-init="
140
+ initBuilder()
141
+ const ops = _.map(fields, f => (f + 'Op'))
142
+ const vals = _.map(fields, f => (f + 'Val'))
143
+ const watcher = ['selected', ...ops, ...vals].join(',')
144
+ $watch(watcher, v => rebuild())
145
+ ">
146
+ <c:grid-row gutter="2">
147
+ <c:grid-col col="12">
148
+ <c:form-textarea x-model="builder" readonly rows="4"/>
149
+ </c:grid-col>
150
+ ${columns.join('\n')}
151
+ </c:grid-row>
152
+ <c:div flex="justify-content:end" margin="top-3">
153
+ <c:btn color="secondary" t:content="Close" dismiss="${container}" />
154
+ <c:btn color="primary" t:content="Apply" margin="start-2" @click="submit()" />
155
+ <c:btn color="primary" t:content="Submit Query" margin="start-2" @click="submit(true)" />
156
+ </c:div>
157
+ </c:${container}>
158
+ </c:form-input-addon>
159
+ <c:form-input-addon>
160
+ <c:btn t:content="Submit" x-data="{
161
+ submit () {
162
+ const val = document.getElementById('${id}').value ?? ''
163
+ const url = new URL(window.location.href)
164
+ const params = new URLSearchParams(url.search)
165
+ params.set('${qsKey.page}', 1)
166
+ params.set('${qsKey.query}', val)
167
+ window.location.href = '?' + params.toString()
168
+ }
169
+ }" @click="submit" @on-submit.window="submit()" />
170
+ </c:form-input-addon>
171
+ </c:form-input>
172
+ `)
173
+ }
174
+ }
175
+ }
176
+
177
+ export default query
@@ -0,0 +1,58 @@
1
+ import { getUrlOpts } from './pagination.js'
2
+ import wdbBase from '../wdb-base.js'
3
+
4
+ async function recsInfo () {
5
+ const WdbBase = await wdbBase.call(this)
6
+
7
+ return class WdbRecsInfo extends WdbBase {
8
+ async build () {
9
+ const { req } = this.component
10
+ const { attrToObject, groupAttrs, attrToArray } = this.plugin.app.waibuMpa
11
+ const { get, isEmpty, omit, merge } = this.plugin.app.bajo.lib._
12
+ const schema = get(this, 'component.locals.schema', {})
13
+ if (schema.view.disabled.includes('find')) {
14
+ this.params.html = ''
15
+ return
16
+ }
17
+ let { count, limit, page, pages } = attrToObject(this.params.attr.options)
18
+ count = count ?? get(this, 'component.locals.list.count', 0)
19
+ page = page ?? get(this, 'component.locals.list.page', 1)
20
+ limit = limit ?? get(this, 'component.locals.list.limit', 25)
21
+ pages = pages ?? get(this, 'component.locals.list.pages', 0)
22
+
23
+ this.params.tag = 'div'
24
+ this.params.attr.flex = 'justify-center:start align-items:center'
25
+ if (count === 0) {
26
+ this.params.html = req.t('No record found')
27
+ return
28
+ }
29
+ if (!this.params.attr.dropdown) this.params.attr.dropdown = true
30
+ const group = groupAttrs(this.params.attr, ['dropdown'])
31
+ const html = []
32
+ if (this.params.attr.count) html.push(req.t('%s record(s) found', req.format(count, 'integer')))
33
+ if (this.params.attr.pages) {
34
+ if (!isEmpty(html)) html[html.length - 1] += '.'
35
+ html.push(req.t('Page %s of %s pages', req.format(page, 'integer'), req.format(pages, 'integer')))
36
+ }
37
+ if (this.params.attr.recsPerPage) {
38
+ this.params.attr.recsPerPageValues = this.params.attr.recsPerPageValues ?? '10 25 50'
39
+ this.params.attr.recsPerPageValues = attrToArray(this.params.attr.recsPerPageValues)
40
+ if (!isEmpty(html)) html[html.length - 1] += ','
41
+ const items = []
42
+ for (const i of this.params.attr.recsPerPageValues) {
43
+ const attr = { href: this.component.buildUrl(merge(getUrlOpts.call(this), { params: { limit: i, page: 1 } })), disabled: i === limit }
44
+ items.push(await this.component.buildTag({ tag: 'dropdownItem', attr, html: i + '' }))
45
+ }
46
+ const attr = group.dropdown
47
+ attr.content = limit + ''
48
+ attr.color = attr.color ?? 'secondary-outline'
49
+ html.push(await this.component.buildTag({ tag: 'dropdown', attr, html: items.join('\n') }))
50
+ html.push(' ', req.t('recs per page'))
51
+ }
52
+ this.params.attr = omit(this.params.attr, ['count', 'pages', 'recsPerPage', 'dropdown', 'recsPerPageValues'])
53
+ this.params.html = html.map(h => `<div class="me-1">${h}</div>`).join('\n')
54
+ }
55
+ }
56
+ }
57
+
58
+ export default recsInfo
@@ -0,0 +1,168 @@
1
+ import wdbBase from '../wdb-base.js'
2
+
3
+ async function table () {
4
+ const WdbBase = await wdbBase.call(this)
5
+
6
+ return class WdbTable extends WdbBase {
7
+ isRightAligned (type) {
8
+ return ['smallint', 'integer', 'float', 'double'].includes(type)
9
+ }
10
+
11
+ async build () {
12
+ const { req } = this.component
13
+ const { escape } = this.plugin.app.waibu
14
+ const { attrToArray, groupAttrs } = this.plugin.app.waibuMpa
15
+ const { get, omit, set, find, isEmpty, without } = this.plugin.app.bajo.lib._
16
+ const group = groupAttrs(this.params.attr, ['body', 'head', 'foot'])
17
+ this.params.attr = group._
18
+
19
+ const data = get(this, 'component.locals.list.data', [])
20
+ const schema = get(this, 'component.locals.schema', {})
21
+ if (schema.view.disabled.includes('find')) {
22
+ this.params.html = ''
23
+ return
24
+ }
25
+ const qsKey = this.plugin.app.waibu.config.qsKey
26
+ let fields = without(get(this, `component.locals._meta.query.${qsKey.fields}`, '').split(','), '')
27
+ if (isEmpty(fields)) fields = schema.view.fields
28
+ const sort = this.params.attr.sort ? attrToArray(this.params.attr.sort) : get(this, `component.locals._meta.query.${qsKey.sort}`, '')
29
+
30
+ let [sortCol, sortDir] = sort.split(':')
31
+ if (!['-1', '1'].includes(sortDir)) sortDir = '1'
32
+
33
+ let selection
34
+ const canDelete = !schema.view.disabled.includes('remove')
35
+ const canEdit = !schema.view.disabled.includes('update')
36
+ if (canEdit) selection = 'single'
37
+ if (canDelete) selection = 'multi'
38
+ if (selection) this.params.attr.hover = true
39
+
40
+ this.params.noTag = true
41
+ const html = []
42
+ let items = []
43
+ // head
44
+ for (const f of schema.view.fields) {
45
+ if (!fields.includes(f)) continue
46
+ const prop = find(schema.properties, { name: f })
47
+ let head = req.t(`field.${f}`)
48
+ if (!this.params.attr.noSort && (schema.sortables ?? []).includes(f)) {
49
+ let sortItem = `${f}:-1`
50
+ let icon = this.params.attr.sortUpIcon ?? 'caretUp'
51
+ if (f === sortCol) {
52
+ sortItem = `${f}:${sortDir === '1' ? '-1' : '1'}`
53
+ icon = sortDir === '1' ? (this.params.attr.sortUpIcon ?? 'caretUp') : (this.params.attr.sortDownIcon ?? 'caretDown')
54
+ }
55
+ const item = set({ page: 1 }, qsKey.sort, sortItem)
56
+ const href = this.component.buildUrl({ params: item })
57
+ const attr = this.isRightAligned(prop.type) ? { text: 'align:end' } : {}
58
+ const content = [
59
+ await this.component.buildTag({ tag: 'div', attr, html: req.t(`field.${f}`) }),
60
+ await this.component.buildTag({ tag: 'a', attr: { icon, href }, prepend: '<div class="ms-1">', append: '</div>' })
61
+ ]
62
+ head = await this.component.buildTag({ tag: 'div', attr: { flex: 'justify-content:between align-items:end' }, html: content.join('\n') })
63
+ }
64
+ let text = this.params.attr.headerNowrap ? '' : 'nowrap'
65
+ if (this.isRightAligned(prop.type)) text += ' align:end'
66
+ const attr = { dataKey: f, dataType: prop.type, text }
67
+ items.push(await this.component.buildTag({ tag: 'th', attr, html: head }))
68
+ }
69
+ if (items.length > 0 && selection) {
70
+ let item = '<th></th>'
71
+ if (selection === 'multi') {
72
+ const attr = { 'x-model': 'toggleAll', name: '_rtm', noWrapper: true, noLabel: true }
73
+ item = await this.component.buildTag({ tag: 'formCheck', attr, prepend: '<th>', append: '</th>' })
74
+ } else {
75
+ const attr = { name: 'remove', '@click': 'selected = \'\'', style: { cursor: 'pointer' } }
76
+ item = await this.component.buildTag({ tag: 'icon', attr, prepend: '<th>', append: '</th>' })
77
+ }
78
+ items.unshift(item)
79
+ }
80
+ const header = await this.component.buildTag({ tag: 'tr', html: items.join('\n') })
81
+ html.push(await this.component.buildTag({ tag: 'thead', attr: group.head, html: header }))
82
+ // body
83
+ items = []
84
+ for (const d of data) {
85
+ const lines = []
86
+ if (selection) {
87
+ const tag = selection === 'single' ? 'formRadio' : 'formCheck'
88
+ const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
89
+ lines.push(await this.component.buildTag({ tag, attr, prepend: '<td>', append: '</td>' }))
90
+ }
91
+ for (const f of schema.view.fields) {
92
+ const prop = find(schema.properties, { name: f })
93
+ if (!fields.includes(f)) continue
94
+ const opts = {}
95
+ if (f === 'lng') opts.longitude = true
96
+ else if (f === 'lat') opts.latitude = true
97
+ let value = req.format(d[f], prop.type, opts)
98
+ if (prop.type === 'boolean') {
99
+ value = (await this.component.buildTag({ tag: 'icon', attr: { name: `circle${d[f] ? 'Check' : ''}` } })) +
100
+ ' ' + (req.t(d[f] ? 'Yes' : 'No'))
101
+ } else value = escape(value)
102
+ let dataValue = d[f] ?? ''
103
+ if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
104
+ if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
105
+ const attr = { dataValue }
106
+ if (!['object', 'array'].includes(prop.type)) {
107
+ if (this.isRightAligned(prop.type)) attr.text = 'align:end nowrap'
108
+ else attr.text = 'nowrap'
109
+ }
110
+ const line = await this.component.buildTag({ tag: 'td', attr, html: value })
111
+ lines.push(line)
112
+ }
113
+ const attr = {}
114
+ if (!schema.view.disabled.includes('update') || !schema.view.disabled.includes('remove')) attr['@click'] = `toggle('${d.id}')`
115
+ if (!schema.view.disabled.includes('get')) attr['@dblclick'] = `goDetails('${d.id}')`
116
+ items.push(await this.component.buildTag({ tag: 'tr', attr, html: lines.join('\n') }))
117
+ }
118
+ html.push(await this.component.buildTag({ tag: 'tbody', attr: group.body, html: items.join('\n') }))
119
+ this.params.attr = omit(this.params.attr, ['sortUpIcon', 'sortDownIcon', 'noSort', 'selection', 'headerNowrap'])
120
+ const goDetails = `
121
+ goDetails (id) {
122
+ window.location.href = '${this.component.buildUrl({ base: 'details' })}&id=' + id
123
+ }
124
+ `
125
+ if (selection === 'multi') {
126
+ this.params.attr['x-data'] = `{
127
+ toggleAll: false,
128
+ selected: [],
129
+ toggle (id) {
130
+ if (this.selected.includes(id)) {
131
+ const idx = this.selected.indexOf(id)
132
+ this.selected.splice(idx, 1)
133
+ } else this.selected.push(id)
134
+ },
135
+ ${goDetails}
136
+ }`
137
+ this.params.attr['x-init'] = `
138
+ $watch('toggleAll', val => {
139
+ if (val) {
140
+ const els = document.getElementsByName('_rt')
141
+ const items = Array.from(els)
142
+ selected = items.map(el => el.value)
143
+ } else selected = []
144
+ })
145
+ $watch('selected', val => $dispatch('on-selection', val))
146
+ `
147
+ } else if (selection === 'single') {
148
+ this.params.attr['x-data'] = `{
149
+ selected: '',
150
+ toggle (id) {
151
+ this.selected = id
152
+ },
153
+ ${goDetails}
154
+ }`
155
+ this.params.attr['x-init'] = `
156
+ $watch('selected', val => $dispatch('on-selection', [val]))
157
+ `
158
+ } else {
159
+ this.params.attr['x-data'] = `{
160
+ ${goDetails}
161
+ }`
162
+ }
163
+ this.params.html = await this.component.buildTag({ tag: 'table', attr: this.params.attr, html: html.join('\n') })
164
+ }
165
+ }
166
+ }
167
+
168
+ export default table
@@ -0,0 +1,6 @@
1
+ async function wdbBase () {
2
+ return class WdbBase extends this.baseFactory {
3
+ }
4
+ }
5
+
6
+ export default wdbBase
@@ -1,15 +0,0 @@
1
- async function btnAdd (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('create')) {
6
- params.html = ''
7
- return
8
- }
9
- if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Add')
10
- params.attr.color = params.attr.color ?? 'secondary-outline'
11
- if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'add' })
12
- params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
13
- }
14
-
15
- export default btnAdd
@@ -1,13 +0,0 @@
1
- async function btnBack (params = {}) {
2
- const { isEmpty } = this.plugin.app.bajo.lib._
3
- const { attrToArray } = this.plugin.app.waibuMpa
4
- params.noTag = true
5
- if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Back')
6
- if (isEmpty(params.attr.icon)) params.attr.icon = 'arrowStart'
7
- params.attr.color = params.attr.color ?? 'secondary-outline'
8
- params.attr.excludeQs = ['mode', 'id', ...attrToArray(params.attr.excludeQs ?? '')]
9
- if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'list', exclude: params.attr.excludeQs })
10
- params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
11
- }
12
-
13
- export default btnBack
@@ -1,30 +0,0 @@
1
- async function btnClone (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('create')) {
6
- params.html = ''
7
- return
8
- }
9
- if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Clone')
10
- params.attr.color = params.attr.color ?? 'secondary-outline'
11
- if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'add', exclude: ['id'] }) + '&mode=clone'
12
- if (params.attr.onList) {
13
- params.attr['x-ref'] = 'clone'
14
- params.attr.disabled = true
15
- params.attr['x-data'] = `{
16
- path: '${params.attr.href}'
17
- }`
18
- params.attr['@on-selection.window'] = `
19
- const recId = $event.detail[0] ?? ''
20
- if ($event.detail.length === 1) $refs.clone.classList.remove('disabled')
21
- else $refs.clone.classList.add('disabled')
22
- $refs.clone.href = path + '&id=' + recId
23
- `
24
- } else {
25
- params.attr.href += '&id=' + this.req.query.id
26
- }
27
- params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
28
- }
29
-
30
- export default btnClone
@@ -1,45 +0,0 @@
1
- async function btnColumns (params = {}) {
2
- const { get, isEmpty, without } = this.plugin.app.bajo.lib._
3
- const { jsonStringify } = this.plugin.app.waibuMpa
4
- const qsKey = this.plugin.app.waibu.config.qsKey
5
- const schema = get(this, 'locals.schema', {})
6
- if (schema.view.disabled.includes('find')) {
7
- params.html = ''
8
- return
9
- }
10
- let fields = without(get(this, `locals._meta.query.${qsKey.fields}`, '').split(','), '')
11
- if (isEmpty(fields)) fields = schema.view.fields
12
- const items = []
13
- params.attr.color = params.attr.color ?? 'secondary-outline'
14
- if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Columns')
15
- for (const f of schema.view.fields) {
16
- if (f === 'id') {
17
- items.push(await this.buildTag({ tag: 'formCheck', attr: { checked: true, label: this.req.t('ID'), value: f, disabled: true } }))
18
- continue
19
- }
20
- const attr = { 'x-model': 'selected', label: this.req.t(`field.${f}`), value: f }
21
- if (fields.includes(f)) attr.checked = true
22
- items.push(await this.buildTag({ tag: 'formCheck', attr }))
23
- }
24
- const href = this._buildUrl({ exclude: [qsKey.fields] })
25
- const html = ['<form class="mt-1 mb-2 mx-3" ']
26
- html.push(`x-data="{
27
- selected: ${jsonStringify(fields, true)},
28
- all: ${jsonStringify(schema.view.fields, true)}
29
- }"`)
30
- html.push(`x-init="
31
- $refs.apply.href = '${href}&${qsKey.fields}=' + selected.join(',')
32
- $watch('selected', v => {
33
- $refs.apply.href = '${href}&${qsKey.fields}=' + v.join(',')
34
- })
35
- ">`)
36
- html.push(...items)
37
- const attr = { size: 'sm', 'x-ref': 'apply', margin: 'top-2', color: params.attr.applyColor ?? 'primary', icon: params.attr.applyIcon ?? 'arrowsStartEnd', href }
38
- html.push(await this.buildTag({ tag: 'btn', attr, html: this.req.t('Apply') }))
39
- html.push('</form>')
40
- params.attr.autoClose = 'outside'
41
- params.html = await this.buildTag({ tag: 'dropdown', attr: params.attr, html: html.join('\n') })
42
- params.noTag = true
43
- }
44
-
45
- export default btnColumns
@@ -1,43 +0,0 @@
1
- async function btnDelete (params = {}) {
2
- const { generateId } = this.plugin.app.bajo
3
- const { isEmpty, get } = this.plugin.app.bajo.lib._
4
- params.noTag = true
5
- const schema = get(this, 'locals.schema', {})
6
- if (schema.view.disabled.includes('remove')) {
7
- params.html = ''
8
- return
9
- }
10
- if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Delete')
11
- params.attr.color = params.attr.color ?? 'danger-outline'
12
- params.attr.id = generateId('alpha')
13
- if (params.attr.onList) {
14
- params.attr.disabled = true
15
- params.attr['x-data'] = `{
16
- selected: [],
17
- remove (ids) {
18
- wmpa.postForm({ ids }, '${this._buildUrl({ base: 'delete' })}')
19
- }
20
- }`
21
- params.attr['@on-selection.window'] = `
22
- const el = document.getElementById('${params.attr.id}')
23
- selected = $event.detail
24
- if (selected.length > 0) el.classList.remove('disabled')
25
- else el.classList.add('disabled')
26
- `
27
- } else {
28
- params.attr['x-data'] = `{
29
- selected: ['${this.req.query.id}'],
30
- remove (modalId, ids) {
31
- wmpa.postForm({ ids }, '${this._buildUrl({ base: 'delete', exclude: ['id', 'page'] })}')
32
- }
33
- }`
34
- }
35
- const msg = 'You\'re about to remove one or more records. Are you really sure to do this?'
36
- params.attr['@click'] = `
37
- const opts = selected.join(',')
38
- const id = await wbs.confirmation(\`${this.req.t(msg)}\`, { ok: '${params.attr.id}:remove', close: 'y', opts })
39
- `
40
- params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
41
- }
42
-
43
- export default btnDelete
@@ -1,32 +0,0 @@
1
- async function btnDetails (params = {}) {
2
- const { generateId } = this.plugin.app.bajo
3
- const { isEmpty, get } = this.plugin.app.bajo.lib._
4
- params.noTag = true
5
- const schema = get(this, 'locals.schema', {})
6
- if (schema.view.disabled.includes('update')) {
7
- params.html = ''
8
- return
9
- }
10
- if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Details')
11
- params.attr.color = params.attr.color ?? 'secondary-outline'
12
- params.attr.id = generateId('alpha')
13
- if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'details', exclude: ['id'] })
14
- if (params.attr.onList) {
15
- params.attr.disabled = true
16
- params.attr['x-ref'] = 'details'
17
- params.attr['x-data'] = `{
18
- path: '${params.attr.href}'
19
- }`
20
- params.attr['@on-selection.window'] = `
21
- const recId = $event.detail[0] ?? ''
22
- if ($event.detail.length === 1) $refs.details.classList.remove('disabled')
23
- else $refs.details.classList.add('disabled')
24
- $refs.details.href = path + '&id=' + recId
25
- `
26
- } else {
27
- params.attr.href += '&id=' + this.req.query.id
28
- }
29
- params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
30
- }
31
-
32
- export default btnDetails
@@ -1,56 +0,0 @@
1
- async function btnEdit (params = {}) {
2
- const { generateId } = this.plugin.app.bajo
3
- const { isEmpty, get } = this.plugin.app.bajo.lib._
4
- params.noTag = true
5
- const schema = get(this, 'locals.schema', {})
6
- if (schema.view.disabled.includes('update')) {
7
- params.html = ''
8
- return
9
- }
10
- if (isEmpty(params.attr.content)) params.attr.content = this.req.t('Edit')
11
- params.attr.color = params.attr.color ?? 'secondary-outline'
12
- params.attr.id = generateId('alpha')
13
- if (!params.attr.href) params.attr.href = this._buildUrl({ base: 'edit', exclude: ['id'] })
14
- if (params.attr.onList) {
15
- params.attr.split = true
16
- params.attr.disabled = true
17
- params.attr['x-data'] = `{
18
- path: '${params.attr.href}'
19
- }`
20
- if (params.attr.noClone) {
21
- params.attr['@on-selection.window'] = `
22
- const recId = $event.detail[0] ?? ''
23
- const el = document.getElementById('${params.attr.id}')
24
- if ($event.detail.length === 1) el.classList.remove('disabled')
25
- else el.classList.add('disabled')
26
- el.href = path + '&id=' + recId
27
- `
28
- params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
29
- } else {
30
- params.attr['@on-selection.window'] = `
31
- const recId = $event.detail[0] ?? ''
32
- const elId = '${params.attr.id}'
33
- for (const id of [elId, elId + '-split']) {
34
- const el = document.getElementById(id)
35
- if ($event.detail.length === 1) el.classList.remove('disabled')
36
- else el.classList.add('disabled')
37
- const href = path + '&id=' + recId
38
- if (id.slice(-6) === '-split') {
39
- const selector = '#' + id.replace('-split', '-menu') + ' a.dropdown-item'
40
- const item = document.querySelector(selector)
41
- item.href = href.replace('/edit', '/add') + '&mode=clone'
42
- } else el.href = href
43
- }
44
- `
45
- const html = [
46
- await this.buildTag({ tag: 'dropdownItem', attr: { content: this.req.t('Add as New Clone') } })
47
- ]
48
- params.html = await this.buildTag({ tag: 'dropdown', attr: params.attr, html: html.join('\n') })
49
- }
50
- } else {
51
- params.attr.href += '&id=' + this.req.query.id
52
- params.html = await this.buildTag({ tag: 'btn', attr: params.attr, html: params.html })
53
- }
54
- }
55
-
56
- export default btnEdit