@json-editor/json-editor 2.9.0-beta.0 → 2.9.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/README.md +16 -7
  3. package/dist/jsoneditor.js +2 -2
  4. package/dist/jsoneditor.js.LICENSE.txt +15 -0
  5. package/dist/jsoneditor.js.map +1 -0
  6. package/dist/nonmin/jsoneditor.js +989 -127
  7. package/dist/nonmin/jsoneditor.js.map +1 -1
  8. package/docs/index.html +22 -10
  9. package/docs/meta_schema.json +3 -2
  10. package/docs/starrating.html +2 -6
  11. package/package.json +4 -2
  12. package/src/defaults.js +9 -1
  13. package/src/editor.js +17 -3
  14. package/src/editors/array.js +7 -4
  15. package/src/editors/multiselect.js +1 -0
  16. package/src/editors/object.css +3 -3
  17. package/src/editors/object.css.js +1 -1
  18. package/src/editors/object.js +5 -2
  19. package/src/editors/table.js +11 -4
  20. package/src/editors/upload.js +3 -2
  21. package/src/themes/barebones.js +3 -1
  22. package/src/themes/bootstrap5.css +97 -0
  23. package/src/themes/bootstrap5.css.js +3 -0
  24. package/src/themes/bootstrap5.js +623 -0
  25. package/src/themes/index.js +2 -0
  26. package/src/themes/tailwind.js +2 -2
  27. package/tests/codeceptjs/core_test.js +107 -17
  28. package/tests/codeceptjs/editors/array_test.js +6 -5
  29. package/tests/codeceptjs/editors/button_test.js +7 -6
  30. package/tests/codeceptjs/editors/checkbox_test.js +3 -2
  31. package/tests/codeceptjs/editors/integer_test.js +3 -2
  32. package/tests/codeceptjs/editors/issues/issue-gh-1158_test.js +10 -0
  33. package/tests/codeceptjs/editors/issues/issue-gh-1257_test.js +13 -0
  34. package/tests/codeceptjs/editors/number_test.js +2 -1
  35. package/tests/codeceptjs/editors/object_test.js +39 -38
  36. package/tests/codeceptjs/editors/option-no_default_values_test.js +1 -1
  37. package/tests/codeceptjs/editors/programmatic-changes_test.js +3 -2
  38. package/tests/codeceptjs/editors/tabs_test.js +5 -3
  39. package/tests/codeceptjs/editors/validation_test.js +3 -1
  40. package/tests/codeceptjs/meta-schema_test.js +72 -4
  41. package/tests/codeceptjs/schemaloader_test.js +2 -1
  42. package/tests/codeceptjs/test-config.js +3 -0
  43. package/tests/codeceptjs/themes_test.js +14 -0
  44. package/tests/pages/anyof-2.html +90 -0
  45. package/tests/pages/anyof.html +1 -1
  46. package/tests/pages/container-attributes.html +50 -0
  47. package/tests/pages/issues/issue-gh-1158.html +50 -0
  48. package/tests/pages/issues/issue-gh-1233-failing.html +46 -0
  49. package/tests/pages/issues/issue-gh-1233-passing.html +49 -0
  50. package/tests/pages/issues/issue-gh-1257.html +77 -0
  51. package/tests/pages/meta-schema.html +747 -0
  52. package/tests/pages/meta_schema.json +3 -16
  53. package/tests/pages/oneof-2.html +90 -0
  54. package/tests/pages/per-editor-options.html +44 -0
  55. package/tests/pages/stepper.html +2 -2
  56. package/tests/pages/themes.html +2 -0
  57. /package/tests/pages/{table-move-events.html → array-events-table.html} +0 -0
  58. /package/tests/pages/{array-move-events.html → array-events.html} +0 -0
@@ -0,0 +1,623 @@
1
+ import { AbstractTheme } from '../theme.js'
2
+ import rules from './bootstrap5.css.js'
3
+ import { trigger } from '../utilities'
4
+
5
+ /* Theme config options that allows changing various aspects of the output */
6
+ const options = {
7
+ disable_theme_rules: false,
8
+ input_size: 'normal', /* Size of input and select elements. "small", "normal", "large" */
9
+ object_indent: true, /* Indent nested object elements (use nested .card layout) */
10
+ object_background: 'bg-light', /* Bootstrap 4 card background modifier class */
11
+ object_text: '', /* Bootstrap 5 card text color modifier class */
12
+ table_border: false, /* Add border to array "table" row and cells */
13
+ table_zebrastyle: false, /* Add "zebra style" to array "table" rows */
14
+ tooltip: 'bootstrap' /* how to display tooltips (infoText). Can be `browser` for native `title`, `css` for simple CSS Styling, or `bootstrap` for TWBS/Popper.js handling */
15
+ }
16
+
17
+ export class bootstrap5Theme extends AbstractTheme {
18
+ constructor (jsoneditor) {
19
+ super(jsoneditor, options)
20
+ }
21
+
22
+ getSelectInput (options, multiple) {
23
+ const el = super.getSelectInput(options)
24
+ el.classList.add('form-control')
25
+ el.classList.add('form-select')
26
+ if (this.options.input_size === 'small') el.classList.add('form-control-sm')
27
+ if (this.options.input_size === 'large') el.classList.add('form-control-lg')
28
+
29
+ return el
30
+ }
31
+
32
+ getContainer () {
33
+ const el = document.createElement('div')
34
+ if (!this.options.object_indent) el.classList.add('je-noindent')
35
+ return el
36
+ }
37
+
38
+ setGridColumnSize (el, size, offset) {
39
+ el.classList.add(`col-md-${size}`)
40
+
41
+ if (offset) {
42
+ el.classList.add(`offset-md-${offset}`)
43
+ }
44
+ }
45
+
46
+ afterInputReady (input) {
47
+ if (input.controlgroup) return
48
+
49
+ /* set id/for */
50
+ /* is not working for: [type=file], [type=checkbox] */
51
+ const id = input.name
52
+ input.id = id
53
+ /* 2x parentNode, b/c range input has an <div> wrapper */
54
+ const label = input.parentNode.parentNode.getElementsByTagName('label')[0]
55
+ if (label) {
56
+ label.classList.add('form-label')
57
+ label.htmlFor = id
58
+ }
59
+
60
+ input.controlgroup = this.closest(input, '.form-group')
61
+ }
62
+
63
+ getTextareaInput () {
64
+ const el = document.createElement('textarea')
65
+ el.classList.add('form-control')
66
+ if (this.options.input_size === 'small') el.classList.add('form-control-sm')
67
+ if (this.options.input_size === 'large') el.classList.add('form-control-lg')
68
+ return el
69
+ }
70
+
71
+ getRangeInput (min, max, step) {
72
+ const el = super.getRangeInput(min, max, step)
73
+ el.classList.remove('form-control')
74
+ el.classList.add('form-range')
75
+ return el
76
+ }
77
+
78
+ getStepperButtons (input) {
79
+ const inputGroup = document.createElement('div')
80
+
81
+ const minusBtn = document.createElement('button')
82
+ minusBtn.setAttribute('type', 'button')
83
+
84
+ const plusBtn = document.createElement('button')
85
+ plusBtn.setAttribute('type', 'button')
86
+
87
+ inputGroup.appendChild(minusBtn)
88
+ inputGroup.appendChild(input)
89
+ inputGroup.appendChild(plusBtn)
90
+
91
+ inputGroup.classList.add('input-group')
92
+ minusBtn.classList.add('btn')
93
+ minusBtn.classList.add('btn-secondary')
94
+ minusBtn.classList.add('stepper-down')
95
+ plusBtn.classList.add('btn')
96
+ plusBtn.classList.add('btn-secondary')
97
+ plusBtn.classList.add('stepper-up')
98
+
99
+ const readonly = input.getAttribute('readonly')
100
+
101
+ if (readonly) {
102
+ minusBtn.setAttribute('disabled', true)
103
+ plusBtn.setAttribute('disabled', true)
104
+ }
105
+
106
+ minusBtn.textContent = '-'
107
+ plusBtn.textContent = '+'
108
+
109
+ const initialize = (input, min) => {
110
+ if (min) {
111
+ input.value = Number(min)
112
+ } else {
113
+ input.value = Number(input.value)
114
+ }
115
+ input.setAttribute('initialized', '1')
116
+ }
117
+
118
+ const min = input.getAttribute('min')
119
+ const max = input.getAttribute('max')
120
+
121
+ input.addEventListener('change', () => {
122
+ if (!input.getAttribute('initialized')) {
123
+ input.setAttribute('initialized', '1')
124
+ }
125
+ })
126
+
127
+ minusBtn.addEventListener('click', () => {
128
+ if (!input.getAttribute('initialized')) {
129
+ initialize(input, min)
130
+ } else if (min) {
131
+ if (Number(input.value) > Number(min)) {
132
+ input.stepDown()
133
+ }
134
+ } else {
135
+ input.stepDown()
136
+ }
137
+ trigger(input, 'change')
138
+ })
139
+
140
+ plusBtn.addEventListener('click', () => {
141
+ if (!input.getAttribute('initialized')) {
142
+ initialize(input, min)
143
+ } else if (max) {
144
+ if (Number(input.value) < Number(max)) {
145
+ input.stepUp()
146
+ }
147
+ } else {
148
+ input.stepUp()
149
+ }
150
+ trigger(input, 'change')
151
+ })
152
+
153
+ return inputGroup
154
+ }
155
+
156
+ getFormInputField (type) {
157
+ const el = super.getFormInputField(type)
158
+ if (type !== 'checkbox' && type !== 'radio') {
159
+ el.classList.add('form-control')
160
+ if (this.options.input_size === 'small') el.classList.add('form-control-sm')
161
+ if (this.options.input_size === 'large') el.classList.add('form-control-lg')
162
+ }
163
+ return el
164
+ }
165
+
166
+ getFormControl (label, input, description, infoText) {
167
+ const group = document.createElement('div')
168
+ group.classList.add('form-group')
169
+
170
+ if (label && (input.type === 'checkbox' || input.type === 'radio')) {
171
+ const check = document.createElement('div')
172
+
173
+ check.classList.add('form-check')
174
+ input.classList.add('form-check-input')
175
+ label.classList.add('form-check-label')
176
+
177
+ const unique = (Date.now() * Math.random()).toFixed(0)
178
+ input.setAttribute('id', unique)
179
+ label.setAttribute('for', unique)
180
+
181
+ check.appendChild(input)
182
+ check.appendChild(label)
183
+ if (infoText) check.appendChild(infoText)
184
+
185
+ group.appendChild(check)
186
+ } else {
187
+ if (label) {
188
+ label.classList.add('form-label')
189
+ group.appendChild(label)
190
+
191
+ if (infoText) group.appendChild(infoText)
192
+ }
193
+
194
+ group.appendChild(input)
195
+ }
196
+
197
+ if (description) {
198
+ group.appendChild(description)
199
+ }
200
+
201
+ return group
202
+ }
203
+
204
+ getInfoButton (text) {
205
+ const button = document.createElement('button') /* shoud be a <button> but no fitting tbws style... */
206
+ button.type = 'button'
207
+ button.classList.add('ms-3', 'jsoneditor-twbs5-text-button')
208
+ button.setAttribute('data-toggle', 'tooltip')
209
+ button.setAttribute('data-placement', 'auto')
210
+ button.title = text
211
+
212
+ const icon = document.createTextNode('ⓘ')
213
+ button.appendChild(icon)
214
+
215
+ if (this.options.tooltip === 'bootstrap') {
216
+ if (window.jQuery && window.jQuery().tooltip) {
217
+ window.jQuery(button).tooltip()
218
+ } else {
219
+ // eslint-disable-next-line no-console
220
+ console.warn('Could not find popper jQuery plugin of Bootstrap.')
221
+ }
222
+ } else if (this.options.tooltip === 'css') {
223
+ button.classList.add('je-tooltip')
224
+ } /* else -> nothing todo for native [title] handling */
225
+
226
+ return button
227
+ }
228
+
229
+ /**
230
+ * Generates a checkbox...
231
+ *
232
+ * Overwriten from master theme to get rid of inline styles.
233
+ */
234
+ getCheckbox () {
235
+ const el = this.getFormInputField('checkbox')
236
+ return el
237
+ }
238
+
239
+ /**
240
+ * Multiple checkboxes in a row.
241
+ *
242
+ */
243
+ getMultiCheckboxHolder (controls, label, description, infoText) {
244
+ const el = document.createElement('div')
245
+ el.classList.add('form-group')
246
+
247
+ if (label) {
248
+ el.appendChild(label)
249
+
250
+ if (infoText) {
251
+ label.appendChild(infoText)
252
+ }
253
+ }
254
+
255
+ /* for inline view we need an container so it doesnt wrap in the "row" of the <label> */
256
+ const container = document.createElement('div')
257
+
258
+ Object.values(controls).forEach(c => {
259
+ /* controls are already parsed by getFormControl() so they have an .form-group */
260
+ /* wrapper we need to get rid of... */
261
+ const ctrl = c.firstChild
262
+
263
+ container.appendChild(ctrl)
264
+ })
265
+
266
+ el.appendChild(container)
267
+
268
+ if (description) el.appendChild(description)
269
+
270
+ return el
271
+ }
272
+
273
+ /**
274
+ * Single radio element
275
+ */
276
+ getFormRadio (attributes) {
277
+ const el = this.getFormInputField('radio')
278
+
279
+ for (const key in attributes) {
280
+ el.setAttribute(key, attributes[key])
281
+ }
282
+ el.classList.add('form-check-input')
283
+
284
+ return el
285
+ }
286
+
287
+ /**
288
+ * Add the <label> for the single radio from getFormRadio()
289
+ *
290
+ */
291
+ getFormRadioLabel (text, req) {
292
+ const el = document.createElement('label')
293
+
294
+ el.classList.add('form-check-label')
295
+ el.appendChild(document.createTextNode(text))
296
+ return el
297
+ }
298
+
299
+ /**
300
+ * Stack the radios from getFormRadio()/getFormRadioLabel()
301
+ *
302
+ */
303
+ getFormRadioControl (label, input, compact) {
304
+ const el = document.createElement('div')
305
+
306
+ el.classList.add('form-check')
307
+
308
+ el.appendChild(input)
309
+ el.appendChild(label)
310
+
311
+ if (compact) {
312
+ el.classList.add('form-check-inline')
313
+ }
314
+
315
+ return el
316
+ }
317
+
318
+ getIndentedPanel () {
319
+ const el = document.createElement('div')
320
+ el.classList.add('card', 'card-body', 'my-3')
321
+
322
+ if (this.options.object_background) {
323
+ el.classList.add(this.options.object_background)
324
+ }
325
+
326
+ if (this.options.object_text) {
327
+ el.classList.add(this.options.object_text)
328
+ }
329
+
330
+ /* for better twbs card styling we should be able to return a nested div */
331
+
332
+ return el
333
+ }
334
+
335
+ getFormInputDescription (text) {
336
+ const el = document.createElement('small')
337
+ el.classList.add('form-text')
338
+ el.classList.add('d-block')
339
+
340
+ if (window.DOMPurify) {
341
+ el.innerHTML = window.DOMPurify.sanitize(text)
342
+ } else {
343
+ el.textContent = this.cleanText(text)
344
+ }
345
+
346
+ return el
347
+ }
348
+
349
+ getHeader (text, pathDepth) {
350
+ const el = document.createElement('h3')
351
+ el.classList.add('card-title')
352
+ el.classList.add('level-' + pathDepth)
353
+
354
+ if (typeof text === 'string') {
355
+ el.textContent = text
356
+ } else {
357
+ el.appendChild(text)
358
+ }
359
+
360
+ el.style.display = 'inline-block'
361
+
362
+ return el
363
+ }
364
+
365
+ getHeaderButtonHolder () {
366
+ const el = this.getButtonHolder()
367
+
368
+ return el
369
+ }
370
+
371
+ getButtonHolder () {
372
+ const el = document.createElement('span')
373
+ el.classList.add('btn-group')
374
+ return el
375
+ }
376
+
377
+ getFormButtonHolder (buttonAlign) {
378
+ const el = this.getButtonHolder()
379
+ el.classList.add('d-block')
380
+
381
+ if (buttonAlign === 'center') el.classList.add('text-center')
382
+ else if (buttonAlign === 'right') el.classList.add('text-end')
383
+
384
+ return el
385
+ }
386
+
387
+ getButton (text, icon, title) {
388
+ const el = super.getButton(text, icon, title)
389
+ el.classList.add('btn', 'btn-secondary', 'btn-sm')
390
+ return el
391
+ }
392
+
393
+ getTable () {
394
+ const el = document.createElement('table')
395
+ el.classList.add('table', 'table-sm')
396
+
397
+ if (this.options.table_border) {
398
+ el.classList.add('table-bordered')
399
+ }
400
+
401
+ if (this.options.table_zebrastyle) {
402
+ el.classList.add('table-striped')
403
+ }
404
+
405
+ return el
406
+ }
407
+
408
+ getErrorMessage (text) {
409
+ const el = document.createElement('div')
410
+ el.classList.add('alert', 'alert-danger')
411
+ el.setAttribute('role', 'alert')
412
+ el.appendChild(document.createTextNode(text))
413
+ return el
414
+ }
415
+
416
+ /**
417
+ * input validation on <input>
418
+ */
419
+ addInputError (input, text) {
420
+ if (!input.controlgroup) return
421
+
422
+ input.controlgroup.classList.add('is-invalid')
423
+
424
+ if (!input.errmsg) {
425
+ input.errmsg = document.createElement('p')
426
+ input.errmsg.classList.add('invalid-feedback')
427
+ input.controlgroup.appendChild(input.errmsg)
428
+ input.errmsg.style.display = 'block'
429
+ }
430
+
431
+ input.errmsg.style.display = 'block'
432
+ input.errmsg.textContent = text
433
+ }
434
+
435
+ removeInputError (input) {
436
+ if (!input.errmsg) return
437
+ input.errmsg.style.display = 'none'
438
+ input.controlgroup.classList.remove('is-invalid')
439
+ }
440
+
441
+ getTabHolder (propertyName) {
442
+ const el = document.createElement('div')
443
+ const pName = (typeof propertyName === 'undefined') ? '' : propertyName
444
+ el.innerHTML = `<div class='col-md-2' id='${pName}'><ul class='nav flex-column nav-pills'></ul></div><div class='col-md-10'><div class='tab-content' id='${pName}'></div></div>`
445
+ el.classList.add('row')
446
+ return el
447
+ }
448
+
449
+ addTab (holder, tab) {
450
+ holder.children[0].children[0].appendChild(tab)
451
+ }
452
+
453
+ getTabContentHolder (tabHolder) {
454
+ return tabHolder.children[1].children[0]
455
+ }
456
+
457
+ getTopTabHolder (propertyName) {
458
+ const pName = (typeof propertyName === 'undefined') ? '' : propertyName
459
+
460
+ const el = document.createElement('div')
461
+ el.classList.add('card')
462
+
463
+ el.innerHTML = `<div class='card-header'><ul class='nav nav-tabs card-header-tabs' id='${pName}'></ul></div><div class='card-body'><div class='tab-content' id='${pName}'></div></div>`
464
+
465
+ return el
466
+ }
467
+
468
+ getTab (text, tabId) {
469
+ const liel = document.createElement('li')
470
+ liel.classList.add('nav-item')
471
+
472
+ const ael = document.createElement('a')
473
+ ael.classList.add('nav-link')
474
+ ael.setAttribute('href', `#${tabId}`)
475
+ ael.setAttribute('data-toggle', 'tab')
476
+ ael.appendChild(text)
477
+
478
+ liel.appendChild(ael)
479
+
480
+ return liel
481
+ }
482
+
483
+ getTopTab (text, tabId) {
484
+ const el = document.createElement('li')
485
+ el.classList.add('nav-item')
486
+
487
+ const a = document.createElement('a')
488
+ a.classList.add('nav-link')
489
+ a.setAttribute('href', `#${tabId}`)
490
+ a.setAttribute('data-toggle', 'tab')
491
+ a.appendChild(text)
492
+
493
+ el.appendChild(a)
494
+
495
+ return el
496
+ }
497
+
498
+ getTabContent () {
499
+ const el = document.createElement('div')
500
+ el.classList.add('tab-pane')
501
+ el.setAttribute('role', 'tabpanel')
502
+ return el
503
+ }
504
+
505
+ getTopTabContent () {
506
+ const el = document.createElement('div')
507
+ el.classList.add('tab-pane')
508
+ el.setAttribute('role', 'tabpanel')
509
+ return el
510
+ }
511
+
512
+ markTabActive (row) {
513
+ row.tab.firstChild.classList.add('active')
514
+
515
+ if (typeof row.rowPane !== 'undefined') {
516
+ row.rowPane.classList.add('active')
517
+ } else {
518
+ row.container.classList.add('active')
519
+ }
520
+ }
521
+
522
+ markTabInactive (row) {
523
+ row.tab.firstChild.classList.remove('active')
524
+
525
+ if (typeof row.rowPane !== 'undefined') {
526
+ row.rowPane.classList.remove('active')
527
+ } else {
528
+ row.container.classList.remove('active')
529
+ }
530
+ }
531
+
532
+ insertBasicTopTab (tab, newTabsHolder) {
533
+ newTabsHolder.children[0].children[0].insertBefore(tab, newTabsHolder.children[0].children[0].firstChild)
534
+ }
535
+
536
+ addTopTab (holder, tab) {
537
+ holder.children[0].children[0].appendChild(tab)
538
+ }
539
+
540
+ getTopTabContentHolder (tabHolder) {
541
+ return tabHolder.children[1].children[0]
542
+ }
543
+
544
+ getFirstTab (holder) {
545
+ return holder.firstChild.firstChild.firstChild
546
+ }
547
+
548
+ getProgressBar () {
549
+ const min = 0
550
+ const max = 100
551
+ const start = 0
552
+
553
+ const container = document.createElement('div')
554
+ container.classList.add('progress')
555
+
556
+ const bar = document.createElement('div')
557
+ bar.classList.add('progress-bar')
558
+ bar.setAttribute('role', 'progressbar')
559
+ bar.setAttribute('aria-valuenow', start)
560
+ bar.setAttribute('aria-valuemin', min)
561
+ bar.setAttribute('aria-valuenax', max)
562
+ bar.innerHTML = `${start}%`
563
+ container.appendChild(bar)
564
+
565
+ return container
566
+ }
567
+
568
+ updateProgressBar (progressBar, progress) {
569
+ if (!progressBar) return
570
+
571
+ const bar = progressBar.firstChild
572
+ const percentage = `${progress}%`
573
+ bar.setAttribute('aria-valuenow', progress)
574
+ bar.style.width = percentage
575
+ bar.innerHTML = percentage
576
+ }
577
+
578
+ updateProgressBarUnknown (progressBar) {
579
+ if (!progressBar) return
580
+
581
+ const bar = progressBar.firstChild
582
+ progressBar.classList.add('progress', 'progress-striped', 'active')
583
+ bar.removeAttribute('aria-valuenow')
584
+ bar.style.width = '100%'
585
+ bar.innerHTML = ''
586
+ }
587
+
588
+ getBlockLink () {
589
+ const link = document.createElement('a')
590
+ link.classList.add('mb-3', 'd-inline-block')
591
+ return link
592
+ }
593
+
594
+ /**
595
+ * Link after successfull upload
596
+ */
597
+ getLinksHolder () {
598
+ const el = document.createElement('div')
599
+ return el
600
+ }
601
+
602
+ getInputGroup (input, buttons) {
603
+ if (!input) return
604
+
605
+ const inputGroupContainer = document.createElement('div')
606
+ inputGroupContainer.classList.add('input-group')
607
+
608
+ inputGroupContainer.appendChild(input)
609
+
610
+ for (let i = 0; i < buttons.length; i++) {
611
+ /* this uses the getButton() wrapper, so we have to remove the panel/ctrl spacing for this case */
612
+ buttons[i].classList.remove('me-2', 'btn-secondary')
613
+ buttons[i].classList.add('btn-outline-secondary')
614
+
615
+ inputGroupContainer.appendChild(buttons[i])
616
+ }
617
+
618
+ return inputGroupContainer
619
+ }
620
+ }
621
+
622
+ /* Custom stylesheet rules. format: "selector" : "CSS rules" */
623
+ bootstrap5Theme.rules = rules
@@ -3,6 +3,7 @@ import { htmlTheme } from './html.js'
3
3
  // import { bootstrap2Theme } from './bootstrap2'
4
4
  import { bootstrap3Theme } from './bootstrap3.js'
5
5
  import { bootstrap4Theme } from './bootstrap4.js'
6
+ import { bootstrap5Theme } from './bootstrap5.js'
6
7
  // import { foundationTheme, foundation3Theme, foundation4Theme, foundation5Theme, foundation6Theme } from './foundation.js'
7
8
  import { jqueryuiTheme } from './jqueryui.js'
8
9
  import { barebonesTheme } from './barebones.js'
@@ -15,6 +16,7 @@ export const themes = {
15
16
  // bootstrap2: bootstrap2Theme,
16
17
  bootstrap3: bootstrap3Theme,
17
18
  bootstrap4: bootstrap4Theme,
19
+ bootstrap5: bootstrap5Theme,
18
20
  // foundation: foundationTheme,
19
21
  // foundation3: foundation3Theme,
20
22
  // foundation4: foundation4Theme,
@@ -277,7 +277,7 @@ export class tailwindTheme extends AbstractTheme {
277
277
  addInputError (input, text) {
278
278
  if (!input.controlgroup) return
279
279
  input.controlgroup.classList.add('has-error')
280
- input.classList.add('bg-red-600')
280
+ input.controlgroup.classList.add('text-red-600')
281
281
  if (!input.errmsg) {
282
282
  input.errmsg = document.createElement('p')
283
283
  input.errmsg.classList.add('block', 'mt-1', 'text-xs', 'text-red')
@@ -291,7 +291,7 @@ export class tailwindTheme extends AbstractTheme {
291
291
  removeInputError (input) {
292
292
  if (!input.errmsg) return
293
293
  input.errmsg.style.display = 'none'
294
- input.classList.remove('bg-red-600')
294
+ input.controlgroup.classList.remove('text-red-600')
295
295
  input.controlgroup.classList.remove('has-error')
296
296
  }
297
297