waibu-bootstrap 2.4.0 → 2.6.0

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.
@@ -2,9 +2,10 @@ import { sizes } from '../method/after-build-tag/_lib.js'
2
2
 
3
3
  const trueValues = ['true', 'on', 'yes', '1', 1, true]
4
4
 
5
- function getInputAttr (group, formControl = true, ro) {
6
- const { omit, get, set, isPlainObject, isArray, isString, has, forOwn } = this.app.lib._
5
+ async function getInputAttr (group, formControl = true, ro) {
6
+ const { omit, get, set, isPlainObject, isArray, isString, has, forOwn, find, camelCase, isEmpty } = this.app.lib._
7
7
  const { escape } = this.app.waibu
8
+ const { req } = this.component
8
9
  if (formControl) group._.class.push('form-control')
9
10
  const attr = omit(group._, ['hint', 'label', 'wrapper'])
10
11
  if (attr.href) {
@@ -13,7 +14,10 @@ function getInputAttr (group, formControl = true, ro) {
13
14
  })
14
15
  }
15
16
  if (has(attr, 'name') && !has(attr, 'value') && this.component.locals.form) {
16
- attr.dataType = attr.dataType ?? 'auto'
17
+ let prop = {}
18
+ const schema = get(this, 'component.locals.schema')
19
+ if (schema) prop = find(schema.properties, { name: attr.name }) ?? {}
20
+ attr.dataType = attr.dataType ?? prop.type ?? 'auto'
17
21
  let val = get(this, `component.locals.form.${attr.name}`)
18
22
  if (attr.dataType === 'boolean') {
19
23
  val = trueValues.includes(val)
@@ -26,10 +30,16 @@ function getInputAttr (group, formControl = true, ro) {
26
30
  if (attr.ref) {
27
31
  const [ref, fieldName = 'id'] = attr.ref.split(':')
28
32
  attr.value = get(this, `component.locals.form._ref.${ref}.${fieldName}`, val)
29
- } else if (attr.dataType === 'boolean') attr.value = this.component.req.t(val ? 'true' : 'false')
30
- else if (has(attr, 'name') === 'lat') attr.value = escape(this.component.req.format(val, attr.dataType, { latitude: true }))
31
- else if (has(attr, 'name') === 'lng') attr.value = escape(this.component.req.format(val, attr.dataType, { longitude: true }))
32
- else attr.value = escape(this.component.req.format(val, attr.dataType))
33
+ } else if (attr.dataType === 'boolean') attr.value = req.t(val ? 'true' : 'false')
34
+ else if (has(attr, 'name') === 'lat') attr.value = escape(req.format(val, attr.dataType, { latitude: true }))
35
+ else if (has(attr, 'name') === 'lng') attr.value = escape(req.format(val, attr.dataType, { longitude: true }))
36
+ else if (prop.values) {
37
+ let items = prop.values
38
+ if (typeof prop.values === 'string') items = await this.app.bajo.callHandler(prop.values)
39
+ const item = find(items, { value: val }) ?? {}
40
+ const ttext = camelCase(`${prop.name} ${item.text}`)
41
+ attr.value = escape(req.format(!isEmpty(item) ? (req.te(ttext) ? req.t(ttext) : item.text) : val, attr.dataType))
42
+ } else attr.value = escape(req.format(val, attr.dataType))
33
43
  } else attr.value = attr.dataValue
34
44
  if (isArray(val)) attr.value = val.join(' ')
35
45
  }
@@ -52,13 +62,13 @@ export async function buildFormLabel (group, tag, cls) {
52
62
  }
53
63
 
54
64
  export async function buildFormInput (group, params) {
55
- const attr = getInputAttr.call(this, group)
65
+ const attr = await getInputAttr.call(this, group)
56
66
  return await this.component.buildTag({ tag: 'input', attr, selfClosing: true })
57
67
  }
58
68
 
59
69
  export async function buildFormCheck (group, params) {
60
70
  const { has, get } = this.app.lib._
61
- const attr = getInputAttr.call(this, group, false)
71
+ const attr = await getInputAttr.call(this, group, false)
62
72
  attr.type = 'checkbox'
63
73
  attr.class.push('form-check-input')
64
74
  if (has(attr, 'name') && !has(attr, 'value')) attr.value = 'true'
@@ -68,7 +78,7 @@ export async function buildFormCheck (group, params) {
68
78
 
69
79
  export async function buildFormSwitch (group, params) {
70
80
  const { has } = this.app.lib._
71
- const attr = getInputAttr.call(this, group, false)
81
+ const attr = await getInputAttr.call(this, group, false)
72
82
  attr.type = 'checkbox'
73
83
  attr.class.push('form-check-input')
74
84
  attr.role = 'switch'
@@ -78,14 +88,14 @@ export async function buildFormSwitch (group, params) {
78
88
  }
79
89
 
80
90
  export async function buildFormRadio (group, params) {
81
- const attr = getInputAttr.call(this, group, false)
91
+ const attr = await getInputAttr.call(this, group, false)
82
92
  attr.type = 'radio'
83
93
  attr.class.push('form-check-input')
84
94
  return await this.component.buildTag({ tag: 'input', attr, selfClosing: true })
85
95
  }
86
96
 
87
97
  export async function buildFormCheckToggle (group, params) {
88
- const attr = getInputAttr.call(this, group, false)
98
+ const attr = await getInputAttr.call(this, group, false)
89
99
  attr.type = 'checkbox'
90
100
  attr.autocomplete = 'off'
91
101
  attr.class.push('btn-check')
@@ -93,7 +103,7 @@ export async function buildFormCheckToggle (group, params) {
93
103
  }
94
104
 
95
105
  export async function buildFormRadioToggle (group, params) {
96
- const attr = getInputAttr.call(this, group, false)
106
+ const attr = await getInputAttr.call(this, group, false)
97
107
  attr.type = 'radio'
98
108
  attr.autocomplete = 'off'
99
109
  attr.class.push('btn-check')
@@ -101,7 +111,7 @@ export async function buildFormRadioToggle (group, params) {
101
111
  }
102
112
 
103
113
  export async function buildFormPlaintext (group, params) {
104
- const attr = getInputAttr.call(this, group, false, true)
114
+ const attr = await getInputAttr.call(this, group, false, true)
105
115
  delete attr.dataValue
106
116
  attr.class.push('form-control-plaintext')
107
117
  attr.readonly = ''
@@ -110,7 +120,7 @@ export async function buildFormPlaintext (group, params) {
110
120
  return await this.component.buildTag({ tag: 'textarea', attr, html: attr.value })
111
121
  }
112
122
  if (attr.href) {
113
- const content = attr.value ? this.component.req.t(attr.value) : attr.href
123
+ const content = attr.value ? attr.value : attr.href
114
124
  const html = await this.component.buildTag({ tag: 'a', attr: { href: attr.href, content } })
115
125
  return await this.component.buildTag({ tag: 'div', attr, html })
116
126
  }
@@ -118,7 +128,7 @@ export async function buildFormPlaintext (group, params) {
118
128
  }
119
129
 
120
130
  export async function buildFormColor (group, params) {
121
- const attr = getInputAttr.call(this, group)
131
+ const attr = await getInputAttr.call(this, group)
122
132
  attr.class.push('form-control-color')
123
133
  attr.type = 'color'
124
134
  if (!attr.dim) attr.dim = 'width:100'
@@ -126,13 +136,13 @@ export async function buildFormColor (group, params) {
126
136
  }
127
137
 
128
138
  export async function buildFormFile (group, params) {
129
- const attr = getInputAttr.call(this, group)
139
+ const attr = await getInputAttr.call(this, group)
130
140
  attr.type = 'file'
131
141
  return await this.component.buildTag({ tag: 'input', attr, selfClosing: true })
132
142
  }
133
143
 
134
144
  export async function buildFormTextarea (group, params) {
135
- const attr = getInputAttr.call(this, group)
145
+ const attr = await getInputAttr.call(this, group)
136
146
  params.html = attr.value
137
147
  attr.style.minHeight = '100px'
138
148
  delete attr.value
@@ -142,12 +152,12 @@ export async function buildFormTextarea (group, params) {
142
152
  export async function buildFormSelect (group, params) {
143
153
  const { omit, trim } = this.app.lib._
144
154
  const { $ } = this.component
145
- let attr = getInputAttr.call(this, group, false)
155
+ let attr = await getInputAttr.call(this, group, false)
146
156
  attr.value = attr.value + ''
147
157
  attr.class.push('form-select')
148
158
  let html = params.html
149
159
  if (sizes.includes(attr.size)) attr.class.push(`form-select-${attr.size}`)
150
- if (attr.options) html = this.component.buildOptions({ attr })
160
+ if (attr.options) html = await this.component.buildOptions({ attr })
151
161
  else {
152
162
  const items = []
153
163
  $(`<div>${trim(html ?? '')}</div>`).find('option').each(function () {
@@ -160,7 +170,7 @@ export async function buildFormSelect (group, params) {
160
170
  }
161
171
 
162
172
  export async function buildFormRange (group, params) {
163
- const attr = getInputAttr.call(this, group, false)
173
+ const attr = await getInputAttr.call(this, group, false)
164
174
  attr.type = 'range'
165
175
  attr.class.push('form-range')
166
176
  return await this.component.buildTag({ tag: 'input', attr, selfClosing: true })
@@ -9,8 +9,7 @@ async function appLauncher () {
9
9
  }
10
10
 
11
11
  build = async () => {
12
- const { locals } = this.component
13
- const { routePath, attrToArray } = this.app.waibu
12
+ const { attrToArray } = this.app.waibu
14
13
  const { groupAttrs } = this.app.waibuMpa
15
14
  const menu = this.params.attr.menu ?? 'pages'
16
15
  const group = groupAttrs(this.params.attr, ['trigger'])
@@ -21,10 +20,11 @@ async function appLauncher () {
21
20
  if (menu === 'pages') toolbar.unshift('home')
22
21
  launcher += `<c:div padding="x-3 ${menu === 'home' ? 'bottom-3' : ''}">`
23
22
  launcher += '<c:navbar padding="y-0"><c:nav tag="ul">\n'
24
- if (locals._meta.isAdmin) launcher += '<c:nav-item text="color:danger" href="' + routePath('waibuAdmin:/') + '" icon="lock" padding="start-0" />\n'
25
- launcher += '</c:nav>\n<c:nav tag="ul">\n'
26
23
  for (const t of toolbar) {
27
24
  if (t === 'home') launcher += '<c:nav-item href="/" icon="house" padding="end-2" />\n'
25
+ }
26
+ launcher += '</c:nav>\n<c:nav tag="ul">\n'
27
+ for (const t of toolbar) {
28
28
  if (t === 'user' && this.app.sumba) launcher += '<c:sumba-nav-dropdown-user padding="end-2" />\n'
29
29
  if (t === '-') launcher += '<c:nav-divider />\n'
30
30
  if (t === 'fullscreen') launcher += '<c:nav-toggle-fullscreen padding="end-2" />\n'
@@ -12,7 +12,8 @@ async function drawer () {
12
12
  build = async () => {
13
13
  const { isString, omit, trim } = this.app.lib._
14
14
  const { groupAttrs } = this.app.waibuMpa
15
- const { $ } = this.component
15
+ const { $, req, locals } = this.component
16
+ const { routePath } = this.app.waibu
16
17
  const group = groupAttrs(this.params.attr, ['trigger'])
17
18
  this.params.attr.responsive = this.params.attr.responsive ?? true
18
19
  this.params.attr.class.push(parseVariant.call(this, { cls, value: this.params.attr.responsive, values: breakpoints }))
@@ -22,6 +23,7 @@ async function drawer () {
22
23
  if (this.params.attr.scroll) this.params.attr.dataBsScroll = 'true'
23
24
  if (this.params.attr.noDismiss) this.params.attr.dataBsBackdrop = 'static'
24
25
  const buttons = []
26
+ if (locals._meta.isAdmin) buttons.push(await this.component.buildTag({ tag: 'btn', attr: { href: routePath('waibuAdmin:/'), icon: 'lock', text: 'color:danger', tooltip: req.t('adminArea') } }))
25
27
  const html = []
26
28
  $(`<div>${this.params.html}</div>`).children().each(function () {
27
29
  if (this.name === 'drawer-toolbar') buttons.push(trim($(this).prop('innerHTML')))
@@ -36,9 +36,7 @@ async function formSelectExt () {
36
36
  try {
37
37
  options = base64JsonDecode(this.params.attr.options)
38
38
  } catch (err) {
39
- options = this.params.attr.options.split(' ').map(item => {
40
- return { value: item, text: item }
41
- })
39
+ options = this.params.attr.options
42
40
  }
43
41
  }
44
42
  let opts = { plugins }
@@ -132,7 +130,7 @@ async function formSelectExt () {
132
130
  this.params.attr['x-data'] = `{ ${xData.join(',\n')} }`
133
131
  // this.params.attr['@load.window'] = 'onLoad()'
134
132
  this.params.attr['x-init'] = 'onLoad()'
135
- this.params.attr.options = options
133
+ if (options.length > 0) this.params.attr.options = options
136
134
  this.params.attr = omit(this.params.attr, ['noDropdownInput', 'removeBtn', 'clearBtn', 'c-opts', 'remoteUrl', 'remoteSearchField', 'remoteLabelField', 'remoteValueField'])
137
135
  await build.call(this, buildFormSelect, this.params)
138
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waibu-bootstrap",
3
- "version": "2.4.0",
3
+ "version": "2.6.0",
4
4
  "description": "Bootstrap suport for Waibu Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,10 +1,19 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-02-20
4
+
5
+ - [2.6.0] Change ```getInputAttr()``` to async function
6
+ - [2.6.0] Update to support a function handler as ```prop.values```
7
+ - [2.6.0] Add button to admin area in ```Drawer``` if user is an admin
8
+
3
9
  ## 2026-02-18
4
10
 
5
11
  - [2.4.0] Update attribute functions from ```waibu```
6
12
  - [2.4.0] Bug fix on ```FormSelectExt``` 's static css
7
13
  - [2.4.0] Bug fix on ```FormSelectCountry``` 's static css
14
+ - [2.5.0] Remove ```FormSelectCountry``` alltogther
15
+ - [2.5.0] Bug fix on ```FormSelectExt```'s options
16
+ - [2.5.0] In read only mode, auto detect value from model's values
8
17
 
9
18
  ## 2026-02-15
10
19
 
@@ -1,26 +0,0 @@
1
- import { css, scripts } from './form-select-ext.js'
2
-
3
- async function formSelectCountry () {
4
- return class FormSelectCountry extends this.app.baseClass.MpaWidget {
5
- static css = [...super.css, ...css]
6
- static scripts = [...super.scripts, scripts]
7
-
8
- constructor (options) {
9
- super(options)
10
- this.params.noTag = true
11
- }
12
-
13
- build = async () => {
14
- const { readConfig } = this.app.bajo
15
- const { map } = this.app.lib._
16
- const { base64JsonEncode } = this.app.waibu
17
- const countries = await readConfig('bajoCommonDb:/extend/dobo/fixture/country.json', { ignoreError: true, defValue: [] })
18
- this.params.attr.options = base64JsonEncode(map(countries, c => {
19
- return { value: c.id, text: c.name.replaceAll('\'', '') }
20
- }))
21
- this.params.html = await this.component.buildTag({ tag: 'formSelectExt', attr: this.params.attr, html: '' })
22
- }
23
- }
24
- }
25
-
26
- export default formSelectCountry