@koumoul/vjsf 2.22.0 → 3.0.0-alpha.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 (138) hide show
  1. package/package.json +50 -86
  2. package/src/compat/v2.js +82 -0
  3. package/src/compile/index.js +32 -0
  4. package/src/compile/v-jsf-compiled.vue.ejs +83 -0
  5. package/src/components/fragments/node-slot.vue +49 -0
  6. package/src/components/fragments/section-header.vue +52 -0
  7. package/src/components/fragments/text-field-menu.vue +68 -0
  8. package/src/components/node.vue +60 -0
  9. package/src/components/nodes/checkbox.vue +27 -0
  10. package/src/components/nodes/color-picker.vue +43 -0
  11. package/src/components/nodes/date-picker.vue +45 -0
  12. package/src/components/nodes/date-time-picker.vue +20 -0
  13. package/src/components/nodes/expansion-panels.vue +52 -0
  14. package/src/components/nodes/list.vue +112 -0
  15. package/src/components/nodes/number-field.vue +35 -0
  16. package/src/components/nodes/one-of-select.vue +56 -0
  17. package/src/components/nodes/section.vue +30 -0
  18. package/src/components/nodes/select.vue +59 -0
  19. package/src/components/nodes/slider.vue +34 -0
  20. package/src/components/nodes/switch.vue +29 -0
  21. package/src/components/nodes/tabs.vue +63 -0
  22. package/src/components/nodes/text-field.vue +29 -0
  23. package/src/components/nodes/textarea.vue +29 -0
  24. package/src/components/nodes/time-picker.vue +7 -0
  25. package/src/components/nodes/vertical-tabs.vue +70 -0
  26. package/src/components/options.js +25 -0
  27. package/src/components/tree.vue +25 -0
  28. package/src/components/types.ts +59 -0
  29. package/src/components/vjsf.vue +168 -0
  30. package/src/index.js +2 -0
  31. package/src/utils/clone.js +3 -0
  32. package/src/utils/dates.js +52 -0
  33. package/src/utils/props.js +79 -0
  34. package/src/utils/slots.js +19 -0
  35. package/types/compat/v2.d.ts +10 -0
  36. package/types/compat/v2.d.ts.map +1 -0
  37. package/types/compile/index.d.ts +7 -0
  38. package/types/compile/index.d.ts.map +1 -0
  39. package/types/components/fragments/node-slot.vue.d.ts +47 -0
  40. package/types/components/fragments/node-slot.vue.d.ts.map +1 -0
  41. package/types/components/fragments/section-header.vue.d.ts +8 -0
  42. package/types/components/fragments/section-header.vue.d.ts.map +1 -0
  43. package/types/components/fragments/text-field-menu.vue.d.ts +20 -0
  44. package/types/components/fragments/text-field-menu.vue.d.ts.map +1 -0
  45. package/types/components/node.vue.d.ts +10 -0
  46. package/types/components/node.vue.d.ts.map +1 -0
  47. package/types/components/nodes/checkbox.vue.d.ts +10 -0
  48. package/types/components/nodes/checkbox.vue.d.ts.map +1 -0
  49. package/types/components/nodes/color-picker.vue.d.ts +10 -0
  50. package/types/components/nodes/color-picker.vue.d.ts.map +1 -0
  51. package/types/components/nodes/date-picker.vue.d.ts +10 -0
  52. package/types/components/nodes/date-picker.vue.d.ts.map +1 -0
  53. package/types/components/nodes/date-time-picker.vue.d.ts +10 -0
  54. package/types/components/nodes/date-time-picker.vue.d.ts.map +1 -0
  55. package/types/components/nodes/expansion-panels.vue.d.ts +10 -0
  56. package/types/components/nodes/expansion-panels.vue.d.ts.map +1 -0
  57. package/types/components/nodes/list.vue.d.ts +10 -0
  58. package/types/components/nodes/list.vue.d.ts.map +1 -0
  59. package/types/components/nodes/number-field.vue.d.ts +27 -0
  60. package/types/components/nodes/number-field.vue.d.ts.map +1 -0
  61. package/types/components/nodes/one-of-select.vue.d.ts +10 -0
  62. package/types/components/nodes/one-of-select.vue.d.ts.map +1 -0
  63. package/types/components/nodes/section.vue.d.ts +10 -0
  64. package/types/components/nodes/section.vue.d.ts.map +1 -0
  65. package/types/components/nodes/select.vue.d.ts +10 -0
  66. package/types/components/nodes/select.vue.d.ts.map +1 -0
  67. package/types/components/nodes/slider.vue.d.ts +10 -0
  68. package/types/components/nodes/slider.vue.d.ts.map +1 -0
  69. package/types/components/nodes/switch.vue.d.ts +10 -0
  70. package/types/components/nodes/switch.vue.d.ts.map +1 -0
  71. package/types/components/nodes/tabs.vue.d.ts +10 -0
  72. package/types/components/nodes/tabs.vue.d.ts.map +1 -0
  73. package/types/components/nodes/text-field copy.vue.d.ts +10 -0
  74. package/types/components/nodes/text-field copy.vue.d.ts.map +1 -0
  75. package/types/components/nodes/text-field.vue.d.ts +27 -0
  76. package/types/components/nodes/text-field.vue.d.ts.map +1 -0
  77. package/types/components/nodes/textarea.vue.d.ts +27 -0
  78. package/types/components/nodes/textarea.vue.d.ts.map +1 -0
  79. package/types/components/nodes/time-picker.vue.d.ts +3 -0
  80. package/types/components/nodes/time-picker.vue.d.ts.map +1 -0
  81. package/types/components/nodes/vertical-tabs.vue.d.ts +10 -0
  82. package/types/components/nodes/vertical-tabs.vue.d.ts.map +1 -0
  83. package/types/components/options.d.ts +3 -0
  84. package/types/components/options.d.ts.map +1 -0
  85. package/types/components/tree.vue.d.ts +10 -0
  86. package/types/components/tree.vue.d.ts.map +1 -0
  87. package/types/components/types.d.ts +71 -0
  88. package/types/components/types.d.ts.map +1 -0
  89. package/types/components/v-jsf.vue.d.ts +13 -0
  90. package/types/components/v-jsf.vue.d.ts.map +1 -0
  91. package/types/components/vjsf.vue.d.ts +13 -0
  92. package/types/components/vjsf.vue.d.ts.map +1 -0
  93. package/types/index.d.ts +3 -0
  94. package/types/index.d.ts.map +1 -0
  95. package/types/utils/clone.d.ts +3 -0
  96. package/types/utils/clone.d.ts.map +1 -0
  97. package/types/utils/dates.d.ts +7 -0
  98. package/types/utils/dates.d.ts.map +1 -0
  99. package/types/utils/props.d.ts +20 -0
  100. package/types/utils/props.d.ts.map +1 -0
  101. package/types/utils/slots.d.ts +7 -0
  102. package/types/utils/slots.d.ts.map +1 -0
  103. package/.eslintignore +0 -9
  104. package/.eslintrc.js +0 -38
  105. package/.github/workflows/scrape-doc.yml +0 -14
  106. package/.nvmrc +0 -1
  107. package/CONTRIBUTE.md +0 -61
  108. package/FUNDING.yml +0 -1
  109. package/README.md +0 -19
  110. package/babel.config.js +0 -4
  111. package/dist/main.css +0 -63
  112. package/dist/main.js +0 -2
  113. package/dist/main.js.LICENSE.txt +0 -10
  114. package/dist/third-party.js +0 -8
  115. package/dist/third-party.js.LICENSE.txt +0 -8
  116. package/lib/VJsf.css +0 -63
  117. package/lib/VJsf.js +0 -117
  118. package/lib/VJsfNoDeps.js +0 -517
  119. package/lib/deps/third-party.js +0 -16
  120. package/lib/mixins/ColorProperty.js +0 -45
  121. package/lib/mixins/DateProperty.js +0 -170
  122. package/lib/mixins/Dependent.js +0 -69
  123. package/lib/mixins/EditableArray.js +0 -418
  124. package/lib/mixins/FileProperty.js +0 -81
  125. package/lib/mixins/MarkdownEditor.js +0 -183
  126. package/lib/mixins/ObjectContainer.js +0 -351
  127. package/lib/mixins/SelectProperty.js +0 -400
  128. package/lib/mixins/SimpleProperty.js +0 -165
  129. package/lib/mixins/Tooltip.js +0 -42
  130. package/lib/mixins/Validatable.js +0 -119
  131. package/lib/utils/expr-eval-parser.js +0 -21
  132. package/lib/utils/is-cyclic.js +0 -34
  133. package/lib/utils/json-refs.js +0 -209
  134. package/lib/utils/options.js +0 -328
  135. package/lib/utils/rules.js +0 -81
  136. package/lib/utils/schema.js +0 -100
  137. package/lib/utils/select.js +0 -141
  138. package/webpack.config.js +0 -46
@@ -1,400 +0,0 @@
1
- import { deepEqual } from 'fast-equals'
2
- import selectUtils from '../utils/select'
3
- import matchAll from 'match-all'
4
- import debounce from 'debounce-promise'
5
-
6
- export default {
7
- data() {
8
- return {
9
- rawSelectItems: null,
10
- selectItems: [],
11
- q: '',
12
- fromUrlParams: {}
13
- }
14
- },
15
- computed: {
16
- isSelectProp() {
17
- if (!this.fullSchema) return
18
- if (this.display === 'list') return false
19
- if (this.fullSchema.enum) return true
20
- if (this.fullSchema.type === 'array' && this.fullSchema.items && this.fullSchema.items.enum) return true
21
- if (this.oneOfSelect) return true
22
- if (this.examplesSelect) return true
23
- // WARNING: it is important not to use this.fromUrl here
24
- // because it is empty at first when fromUrlParams are not ready yet and it creates initialization problems
25
- if (this.fullSchema['x-fromUrl']) return true
26
- if (this.fromData) return true
27
- return false
28
- },
29
- oneOfSelect() {
30
- return selectUtils.isOneOfSelect(this.fullSchema)
31
- },
32
- examplesSelect() {
33
- if (!this.fullSchema) return
34
- if (this.fullSchema.type === 'array' && this.fullSchema.items && ['string', 'integer', 'number'].includes(this.fullSchema.items.type) && this.fullSchema.items.examples) return true
35
- if (['string', 'integer', 'number'].includes(this.fullSchema.type) && this.fullSchema.examples) return true
36
- return false
37
- },
38
- fromUrlWithQuery() {
39
- if (!this.fullSchema) return
40
- return !!(this.fullSchema['x-fromUrl'] && this.fullSchema['x-fromUrl'].indexOf('{q}') !== -1)
41
- },
42
- fromUrlKeys() {
43
- if (!this.fullSchema) return
44
- // Look for variable parts in the URL used to fetch data
45
- if (!this.fullSchema['x-fromUrl']) return null
46
- return matchAll(this.fullSchema['x-fromUrl'], /\{(.*?)\}/g).toArray().filter(key => key !== 'q')
47
- },
48
- fromUrl() {
49
- if (!this.fullSchema) return
50
- let url = this.fullSchema['x-fromUrl']
51
- if (!url) return
52
- for (const key of this.fromUrlKeys) {
53
- // URL parameters are incomplete
54
- if (this.fromUrlParams[key] === undefined) return
55
- else url = url.replace(`{${key}}`, this.fromUrlParams[key])
56
- }
57
- return url
58
- },
59
- fromData() {
60
- if (!this.fullSchema) return
61
- return this.fullSchema['x-fromData']
62
- },
63
- itemKey() {
64
- if (!this.fullSchema) return
65
- return this.fullSchema['x-itemKey'] || 'key'
66
- },
67
- itemTitle() {
68
- if (!this.fullSchema) return
69
- return this.fullSchema['x-itemTitle'] || 'title'
70
- },
71
- itemIcon() {
72
- if (!this.fullSchema) return
73
- return this.fullSchema['x-itemIcon'] || (this.display === 'icon' ? this.itemKey : null)
74
- },
75
- returnObject() {
76
- if (!this.fullSchema) return
77
- return this.fullSchema.type === 'object' || (this.fullSchema.items && this.fullSchema.items.type === 'object')
78
- }
79
- },
80
- watch: {
81
- q() {
82
- // This line prevents reloading the list just after selecting an item in an auto-complete
83
- if (this.value && this.value[this.itemTitle] === this.q) return
84
- this.fetchSelectItems()
85
- },
86
- rawSelectItems: {
87
- handler() {
88
- this.updateSelectItems()
89
- }
90
- }
91
- },
92
- methods: {
93
- initSelectProp(model) {
94
- // Case of an auto-complete field already defined
95
- if (this.fromUrlWithQuery && model && model[this.itemTitle] !== undefined) {
96
- this.q = model[this.itemTitle]
97
- }
98
- // Case of a select based on ajax query
99
- if (this.fromUrl) {
100
- this.fetchSelectItems()
101
- }
102
- if (this.fullSchema['x-fromUrl']) {
103
- // do not use this.fromUrl to determine this.openEndedSelect as it might be null if missing parameters
104
- this.openEndedSelect = this.customTag === 'v-combobox' || this.fullSchema['x-display'] === 'combobox'
105
- }
106
- // Case of select based on an enum
107
- if ((this.fullSchema.type === 'array' && this.fullSchema.items && this.fullSchema.items.enum) || this.fullSchema.enum) {
108
- this.rawSelectItems = this.fullSchema.type === 'array' ? this.fullSchema.items.enum : this.fullSchema.enum
109
- }
110
- // Case of select based on a oneof on simple types
111
- if (this.oneOfSelect) {
112
- const schema = (this.fullSchema.type === 'array' ? this.fullSchema.items : this.fullSchema)
113
- const of = schema.anyOf || schema.oneOf
114
- this.openEndedSelect = schema.anyOf && !!schema.anyOf.find(item => !item.const && !item.enum)
115
- this.rawSelectItems = of
116
- .filter(item => !item['x-if'] || !!this.getFromExpr(item['x-if']))
117
- .filter(item => ('const' in item) || !!item.enum)
118
- .map(item => ({ ...item, [this.itemKey]: ('const' in item) ? item.const : (item.enum && item.enum[0]), [this.itemTitle]: item.title }))
119
- }
120
-
121
- // Case of combobox based on examples
122
- if (this.examplesSelect) {
123
- const examples = (this.fullSchema.type === 'array' ? (this.fullSchema.items.examples || this.fullSchema.examples) : this.fullSchema.examples)
124
- this.openEndedSelect = true
125
- this.rawSelectItems = examples.map(example => ({ [this.itemKey]: example, [this.itemTitle]: example }))
126
- }
127
-
128
- // Case of a select based on an array somewhere in the data
129
- if (this.fullSchema['x-fromData']) {
130
- this.openEndedSelect = this.customTag === 'v-combobox' || this.fullSchema['x-display'] === 'combobox'
131
- this.$watch(() => this.getFromExpr(this.fullSchema['x-fromData']), (val) => {
132
- this.rawSelectItems = val
133
- }, { immediate: true })
134
- }
135
- // Watch the dynamic parts of the URL used to fill the select field
136
- if (this.fromUrlKeys) {
137
- this.fromUrlKeys.forEach(key => {
138
- this.$watch(() => this.getFromExpr(key), (val) => {
139
- this.$set(this.fromUrlParams, key, val)
140
- this.fetchSelectItems()
141
- }, { immediate: true })
142
- })
143
- }
144
- },
145
- fetchSelectItems() {
146
- if (!this.fromUrl) return
147
- if (!this.fullOptions.httpLib) {
148
- console.error('No http lib found to perform ajax request')
149
- return this.$emit('error', 'No http lib found to perform ajax request')
150
- }
151
- this.debouncedFetch = this.debouncedFetch || debounce(async () => {
152
- this.loading = true
153
- try {
154
- this.rawSelectItems = await selectUtils.fetchRawItems(this.fullOptions, this.fullSchema, this.fromUrl, this.q)
155
- } catch (err) {
156
- console.error(err)
157
- this.$emit('error', err.message)
158
- }
159
- this.loading = false
160
- }, 250)
161
- const promise = this.debouncedFetch()
162
- // store all current promises in sharedData.asyncOperations so that we can delay change events
163
- // until after a user interaction has finished having async consequencies
164
- this.sharedData.asyncOperations = this.sharedData.asyncOperations || {}
165
- this.sharedData.asyncOperations[this.fullKey] = promise
166
- promise.finally(() => {
167
- if (this.sharedData.asyncOperations[this.fullKey] === promise) delete this.sharedData.asyncOperations[this.fullKey]
168
- })
169
- },
170
- async updateSelectItems() {
171
- const selectItems = selectUtils.getSelectItems(this.rawSelectItems, this.fullSchema, this.itemKey, this.itemIcon)
172
- if (this.display === 'list' && this.rawSelectItems) {
173
- this.input(selectUtils.fillList(this.fullSchema, this.value, selectItems, this.itemKey))
174
- }
175
-
176
- this.loading = true
177
- await selectUtils.fillSelectItems(
178
- this.fullOptions,
179
- this.fullSchema,
180
- this.separator && typeof this.value === 'string' ? this.value.split(this.separator) : this.value,
181
- selectItems,
182
- this.itemKey,
183
- this.itemTitle,
184
- this.fromUrlWithQuery && this.fromUrl,
185
- this.returnObject
186
- )
187
- this.loading = false
188
-
189
- // we check for actual differences in order to prevent infinite loops
190
- if (!deepEqual(selectItems, this.selectItems)) {
191
- this.selectItems = selectItems
192
- }
193
- },
194
- renderSelectIcon(h, item) {
195
- if (!this.itemIcon) return
196
- let itemIcon = item[this.itemIcon]
197
- if (!itemIcon) return
198
- let iconChild
199
- if (itemIcon.startsWith('http://') || itemIcon.startsWith('https://')) {
200
- iconChild = h('img', { domProps: { src: itemIcon }, style: 'height:100%;width:100%;' })
201
- } else if (itemIcon.startsWith('<?xml') || itemIcon.startsWith('<svg')) {
202
- iconChild = h('div', { domProps: { innerHTML: itemIcon } })
203
- } else {
204
- const prefix = this.fullOptions.iconfont + '-'
205
- if (this.fullOptions.iconfont && !itemIcon.startsWith(prefix)) itemIcon = prefix + itemIcon
206
- iconChild = h('v-icon', null, itemIcon)
207
- }
208
- return h('v-avatar', { props: { tile: true, size: 20 }, class: 'mr-2' }, [iconChild])
209
- },
210
- renderSelectCheckbox(h, value) {
211
- if (this.fullSchema.type !== 'array' || this.itemIcon) return
212
- return h('v-list-item-action', [h('v-checkbox', { props: { inputValue: value } })])
213
- },
214
- renderSelectItem(h, item) {
215
- const text = item[this.itemTitle] || item[this.itemKey]
216
- return h('v-list-item-content', [h('v-list-item-title', text)])
217
- },
218
- renderSelectionControlItem(h, item) {
219
- const label = item[this.itemTitle] || item[this.itemKey]
220
- const value = item[this.itemKey]
221
- const on = {
222
- change: (inputValue) => {
223
- this.input(inputValue)
224
- this.change()
225
- }
226
- }
227
-
228
- const props = {
229
- ...this.fullOptions.radioItemProps,
230
- label,
231
- value,
232
- inputValue: this.separator && typeof this.value === 'string' ? this.value.split(this.separator) : this.value,
233
- multiple: this.fullSchema.type === 'array' || !!this.separator,
234
- hideDetails: true
235
- }
236
-
237
- return h(`v-${this.display}`, { props, on, class: 'pb-1' })
238
- },
239
- renderSelectionControlGroup(h) {
240
- const on = {
241
- change: value => {
242
- this.input(value)
243
- this.change()
244
- }
245
- }
246
- const props = {
247
- ...this.commonFieldProps,
248
- multiple: this.fullSchema.type === 'array' || !!this.separator,
249
- label: null
250
- }
251
- // imitate a radio-group, but with checkboxes and switches
252
- const legend = h('legend', { class: `v-label theme--${this.theme.isDark ? 'dark' : 'light'} ${this.hasError ? 'error--text' : ''}` }, this.commonFieldProps.label)
253
- const itemsElements = this.selectItems.map(item => this.renderSelectionControlItem(h, item))
254
- return [
255
- h('v-input', { props, on, class: 'v-input--selection-controls v-input--radio-group v-input--radio-group--column' }, [
256
- h('div', { class: 'v-input--radio-group__input' }, [legend, ...itemsElements]),
257
- this.renderTooltip(h, 'append')
258
- ])
259
-
260
- ]
261
- },
262
- renderRadioItem(h, item) {
263
- const label = item[this.itemTitle] || item[this.itemKey]
264
- const value = item[this.itemKey]
265
- return h('v-radio', { props: { ...this.fullOptions.radioItemProps, label, value } })
266
- },
267
- renderRadioGroup(h) {
268
- const props = { ...this.commonFieldProps, ...this.fullOptions.radioGroupProps }
269
- const on = {
270
- change: value => {
271
- this.input(value)
272
- this.change()
273
- }
274
- }
275
- return [h('v-radio-group', { props, on }, [
276
- ...this.selectItems.map(item => this.renderRadioItem(h, item)), this.renderTooltip(h, 'append')
277
- ])]
278
- },
279
- renderSelectProp(h) {
280
- if (!this.isSelectProp) return
281
-
282
- // radio cannot be applied on an array
283
- if (this.display === 'radio') {
284
- if (this.fullSchema.type === 'array' || this.separator) {
285
- console.error('radio display is not available for arrays, use checkbox or switch')
286
- } else {
287
- return this.renderRadioGroup(h)
288
- }
289
- }
290
-
291
- if (['checkbox', 'switch'].includes(this.display)) {
292
- return this.renderSelectionControlGroup(h)
293
- }
294
-
295
- const on = {
296
- input: value => this.input(value),
297
- change: value => this.change()
298
- }
299
- const scopedSlots = {
300
- selection: (data) => {
301
- let item
302
- // combobox only gives us the value in selection results, not the full select item
303
- if (this.openEndedSelect) {
304
- item = (this.selectItems || []).find(item => item[this.itemKey] === data.item)
305
- if (!item) item = { [this.itemKey]: data.item, [this.itemTitle]: data.item }
306
- } else {
307
- item = data.item
308
- }
309
- let text = item[this.itemTitle] || item[this.itemKey]
310
- if (this.fullSchema.type === 'array' && data.index !== this.value.length - 1) text += ',&nbsp;'
311
- if (this.separator && this.commonFieldProps.value && data.index !== this.commonFieldProps.value.length - 1) text += ',&nbsp;'
312
- return h('div', {
313
- class: { 'v-select__selection': true, 'v-select__selection--comma': true, 'v-select__selection--disabled': this.disabled }
314
- }, [
315
- this.renderSelectIcon(h, item),
316
- h('span', { domProps: { innerHTML: text }, class: 'mt-1' })
317
- ])
318
- },
319
- item: (data) => {
320
- return [this.renderSelectIcon(h, data.item), this.renderSelectCheckbox(h, data.attrs.inputValue), this.renderSelectItem(h, data.item)]
321
- }
322
- }
323
-
324
- // checkbox can only be applied on an array
325
- /* if (this.display === 'checkbox' && this.fullSchema.type === 'array') {
326
- return [h('v-col', { props, scopedSlots }, [
327
- ...this.selectItems.map(item => this.renderCheckboxItem(h, item, on))
328
- ])]
329
- } */
330
- let tag = 'v-select'
331
- if (this.customTag) tag = this.customTag
332
- else if (this.display) {
333
- if (this.display === 'autocomplete') tag = 'v-autocomplete'
334
- if (this.display === 'select') tag = 'v-select'
335
- } else {
336
- if (this.fromUrlWithQuery || (this.rawSelectItems && this.rawSelectItems.length > 20)) tag = 'v-autocomplete'
337
- }
338
-
339
- const props = {
340
- ...this.commonFieldProps,
341
- ...this.fullOptions.selectProps,
342
- validateOnBlur: true, // without this we sometimes get a weird infinite render loop
343
- clearable: !this.required,
344
- multiple: this.fullSchema.type === 'array' || !!this.separator,
345
- itemValue: this.itemKey,
346
- itemText: this.itemTitle,
347
- items: this.selectItems || [],
348
- returnObject: !!this.returnObject,
349
- loading: this.loading
350
- }
351
- if (tag === 'v-autocomplete') {
352
- tag = 'v-autocomplete'
353
- props.noDataText = this.fullOptions.messages.noData
354
- props.placeholder = this.fullOptions.messages.search
355
- if (this.fromUrlWithQuery) {
356
- props.filter = () => true
357
- props.searchInput = this.q
358
- on['update:search-input'] = (searchUpdate) => { this.q = searchUpdate }
359
- } else {
360
- props.filter = (item, q) => (item[this.itemTitle] || item[this.itemKey]).toLowerCase().includes(q.toLowerCase())
361
- }
362
- }
363
-
364
- if (this.openEndedSelect) {
365
- tag = 'v-combobox'
366
- if (!props.multiple) {
367
- props.hideSelected = true
368
- on.input = value => this.input(value || '')
369
- }
370
- }
371
-
372
- const children = [...this.renderPropSlots(h)]
373
-
374
- // "select all" action on top of the select
375
- if (tag === 'v-select' && props.multiple && this.fullOptions.selectAll) {
376
- const allIsSelected = props.items.length === this.value.length
377
- children.push(h('template', { slot: 'prepend-item' }, [
378
- h('v-list-item', [
379
- h('v-list-item-action', [h('v-checkbox', {
380
- props: { value: allIsSelected },
381
- on: { click: () => {
382
- if (allIsSelected) this.input([])
383
- else this.input(props.items.map(item => this.returnObject ? item : item[this.itemKey]))
384
- this.change()
385
- } }
386
- })]),
387
- h('v-list-item-content', [h('v-list-item-title', this.fullOptions.messages.selectAll)])
388
- ]),
389
- h('v-divider')
390
- ]))
391
- }
392
-
393
- if (this.htmlDescription) {
394
- children.push(this.renderTooltip(h, 'append-outer'))
395
- }
396
-
397
- return [h(tag, { props, on, scopedSlots }, children)]
398
- }
399
- }
400
- }
@@ -1,165 +0,0 @@
1
- import { getRules } from '../utils/rules'
2
- import schemaUtils from '../utils/schema'
3
-
4
- export default {
5
- computed: {
6
- isSimpleProp() {
7
- return this.fullSchema.type === 'string' ||
8
- ['number', 'integer'].includes(this.fullSchema.type) ||
9
- this.fullSchema.type === 'boolean' ||
10
- (this.fullSchema.type === 'array' && this.fullSchema.items && ['string', 'number', 'integer'].includes(this.fullSchema.items.type))
11
- }
12
- },
13
- methods: {
14
- parseNumber(type, str) {
15
- str = str + ''
16
- if (!str) return null
17
- if (str === '-') return null
18
- return this.fullSchema.type === 'integer' ? parseInt(str, 10) : parseFloat(str)
19
- },
20
- renderSimpleProp(h) {
21
- if (!this.isSimpleProp) return
22
- let tag
23
- const props = { ...this.commonFieldProps }
24
-
25
- const domProps = {}
26
- const attrs = {}
27
- const children = [...this.renderPropSlots(h)]
28
- const on = {
29
- input: value => this.input(value),
30
- change: value => this.change()
31
- }
32
- const scopedSlots = {}
33
- let tooltipSlot = 'append-outer'
34
-
35
- // simple string
36
- if (this.fullSchema.type === 'string' && !this.separator) {
37
- if (this.display === 'textarea' || (this.fullSchema.maxLength && this.fullSchema.maxLength > 1000 && this.display !== 'single-line')) {
38
- tag = 'v-textarea'
39
- Object.assign(props, this.fullOptions.textareaProps)
40
- domProps.class = 'v-text-field--box v-text-field--enclosed'
41
- } else {
42
- tag = 'v-text-field'
43
- Object.assign(props, this.fullOptions.textFieldProps)
44
- if (this.display === 'password') {
45
- props.type = 'password'
46
- attrs.autocomplete = 'new-password'
47
- }
48
- }
49
- if (this.fullOptions.maxLengthCounter && this.fullSchema.maxLength) props.counter = this.fullSchema.maxLength
50
- }
51
-
52
- // multivalued string with separator
53
- if (this.fullSchema.type === 'string' && this.separator) {
54
- tag = 'v-combobox'
55
- Object.assign(props, this.fullOptions.comboboxProps)
56
- props.chips = true
57
- props.multiple = true
58
- props.appendIcon = ''
59
- props.type = 'string'
60
- props.validateOnBlur = true
61
-
62
- scopedSlots.selection = slotProps => {
63
- const onClose = () => {
64
- const value = this.value ? this.value.split(this.separator) : []
65
- value.splice(slotProps.index, 1)
66
- this.input(value)
67
- this.change()
68
- }
69
- return h('v-chip', {
70
- props: { close: true },
71
- on: { 'click:close': onClose }
72
- }, slotProps.item)
73
- }
74
- }
75
-
76
- // simple boolean
77
- if (['number', 'integer'].includes(this.fullSchema.type)) {
78
- if (this.display === 'slider') {
79
- tag = 'v-slider'
80
- Object.assign(props, this.fullOptions.sliderProps)
81
- } else {
82
- tag = 'v-text-field'
83
- Object.assign(props, this.fullOptions.textFieldProps)
84
- Object.assign(props, this.fullOptions.numberProps)
85
- }
86
- props.type = 'number'
87
- if (this.fullSchema.minimum !== undefined) props.min = this.fullSchema.minimum
88
- if (this.fullSchema.maximum !== undefined) props.max = this.fullSchema.maximum
89
- props.step = this.fullSchema['x-step'] || (this.fullSchema.type === 'integer' ? 1 : 0.01)
90
-
91
- on.input = value => this.input(this.parseNumber(this.fullSchema.type, value))
92
- }
93
-
94
- if (this.fullSchema.type === 'boolean') {
95
- tooltipSlot = 'append'
96
- if (this.display === 'switch') {
97
- tag = 'v-switch'
98
- Object.assign(props, this.fullOptions.switchProps)
99
- } else {
100
- tag = 'v-checkbox'
101
- Object.assign(props, this.fullOptions.checkboxProps)
102
- }
103
- on.change = value => {
104
- this.input(value || false)
105
- this.change()
106
- }
107
- }
108
-
109
- if (this.fullSchema.type === 'array' && ['string', 'number', 'integer'].includes(this.fullSchema.items.type)) {
110
- tag = 'v-combobox'
111
- Object.assign(props, this.fullOptions.comboboxProps)
112
- props.chips = true
113
- props.multiple = true
114
- props.appendIcon = ''
115
- props.type = 'string'
116
- props.validateOnBlur = true
117
- const itemRules = getRules(this.fullSchema.items, schemaUtils.prepareFullSchema(this.fullSchema.items, null, this.fullOptions), this.fullOptions)
118
- props.rules = props.rules.concat([(values) => {
119
- const valuesMessages = values.map(value => {
120
- const brokenRule = itemRules.find(rule => {
121
- return typeof rule(value) === 'string'
122
- })
123
- return brokenRule && brokenRule(value)
124
- })
125
- const firstMessage = valuesMessages.find(m => !!m)
126
- return firstMessage || true
127
- }])
128
-
129
- if (this.fullSchema.items.type !== 'string') {
130
- props.type = 'number'
131
- on.input = value => {
132
- const vals = value
133
- .map(val => this.parseNumber(this.fullSchema.items.type, val))
134
- .filter(val => !isNaN(val))
135
- this.input(vals)
136
- }
137
- }
138
-
139
- scopedSlots.selection = slotProps => {
140
- const onClose = () => {
141
- const value = [...this.value]
142
- value.splice(slotProps.index, 1)
143
- this.input(value)
144
- this.change()
145
- }
146
- const brokenRule = itemRules.find(rule => {
147
- return typeof rule(slotProps.item) === 'string'
148
- })
149
- return h('v-chip', {
150
- props: { close: true, color: brokenRule ? 'error' : 'default' },
151
- on: { 'click:close': onClose }
152
- }, slotProps.item)
153
- }
154
- }
155
-
156
- if (this.htmlDescription) {
157
- children.push(this.renderTooltip(h, tooltipSlot))
158
- }
159
-
160
- tag = this.customTag ? this.customTag : tag
161
-
162
- return tag ? [h(tag, { props, domProps, attrs, on, scopedSlots, directives: this.directives }, children)] : null
163
- }
164
- }
165
- }
@@ -1,42 +0,0 @@
1
- export default {
2
- data() {
3
- return {
4
- tooltip: {
5
- maxWidth: 200
6
- }
7
- }
8
- },
9
- mounted() {
10
- if (!this.htmlDescription) return
11
- if (this.$el && this.$el.getBoundingClientRect) this.tooltip.maxWidth = this.$el.getBoundingClientRect().left - 80
12
- },
13
- methods: {
14
- renderTooltip(h, slot) {
15
- if (this.fullOptions.hideTooltips) return
16
- if (this.fullOptions.hideReadOnlyTooltips && (this.fullSchema.readOnly || this.fullOptions.readOnlyArrayItem)) return
17
- if (!this.htmlDescription) return
18
-
19
- return h('v-tooltip', {
20
- slot,
21
- props: { contentClass: 'vjsf-tooltip', ...this.fullOptions.tooltipProps },
22
- scopedSlots: {
23
- activator: ({ on, attrs }) => {
24
- // openOnClick doesn't work well
25
- // cf https://stackoverflow.com/questions/60322211/vuetify-vtooltip-trigger-only-on-activator-click/60333126#60333126
26
- if (this.fullOptions.tooltipProps.openOnClick) {
27
- delete on.focus
28
- }
29
- return h('v-btn', {
30
- on,
31
- attrs,
32
- props: { icon: true, retainFocusOnClick: true },
33
- style: 'pointer-events: auto' // necessary or the tooltip is disabled on readOnly props
34
- }, [h('v-icon', { }, this.fullOptions.icons.info)])
35
- }
36
- }
37
- }, [
38
- h('div', { style: `max-width: ${this.tooltip.maxWidth}px`, domProps: { innerHTML: this.htmlDescription } })
39
- ])
40
- }
41
- }
42
- }