@koumoul/vjsf 2.22.1 → 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 -67
  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 -67
  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
package/package.json CHANGED
@@ -1,99 +1,63 @@
1
1
  {
2
2
  "name": "@koumoul/vjsf",
3
- "version": "2.22.1",
3
+ "version": "3.0.0-alpha.0",
4
4
  "description": "Generate forms for the vuetify UI library (vuejs) based on annotated JSON schemas.",
5
- "main": "dist/main.js",
6
5
  "scripts": {
7
- "lint": "eslint --ext js .",
8
- "lint-fix": "eslint --fix --ext js .",
9
- "build": "rm -rf dist && webpack && cp lib/VJsf.css dist/main.css",
10
- "prepare": "npm run lint && npm test && npm run build && npm run doc-build",
11
- "postpublish": "gh-pages-multi deploy -v -s doc/dist -t latest",
12
- "test": "jest",
13
- "test-watch": "jest --watch",
14
- "test-update": "jest --updateSnapshot",
15
- "doc-build": "(cd doc && TARGET=https://koumoul-dev.github.io/vuetify-jsonschema-form/latest/ nuxt generate)",
16
- "doc-master": "(cd doc && TARGET=https://koumoul-dev.github.io/vuetify-jsonschema-form/master/ nuxt generate) && gh-pages-multi deploy -v -s doc/dist -t master",
17
- "analyze": "webpack --profile --json > dist/stats.json && webpack-bundle-analyzer dist/stats.json"
18
- },
19
- "jest": {
20
- "moduleFileExtensions": [
21
- "js",
22
- "json",
23
- "vue"
24
- ],
25
- "transform": {
26
- ".*\\.(vue)$": "vue-jest",
27
- "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
28
- },
29
- "collectCoverage": true,
30
- "collectCoverageFrom": [
31
- "lib/**/*.{js,vue}"
32
- ],
33
- "snapshotSerializers": [
34
- "jest-serializer-vue"
35
- ],
36
- "testEnvironment": "jsdom"
37
- },
38
- "repository": {
39
- "type": "git",
40
- "url": "git+https://github.com/koumoul-dev/vuetify-jsonschema-form.git"
6
+ "test": "vitest",
7
+ "build": "vue-tsc",
8
+ "watch:build": "vue-tsc --watch"
41
9
  },
42
10
  "author": "Alban Mouton <alban.mouton@gmail.com>",
43
11
  "license": "MIT",
44
- "bugs": {
45
- "url": "https://github.com/koumoul-dev/vuetify-jsonschema-form/issues"
46
- },
47
- "homepage": "https://github.com/koumoul-dev/vuetify-jsonschema-form#readme",
48
- "dependencies": {
49
- "debounce": "^1.2.1",
50
- "debounce-promise": "^3.1.2",
51
- "debug": "^4.3.3",
52
- "fast-copy": "^2.1.1",
53
- "fast-equals": "^2.0.4",
54
- "match-all": "^1.2.6"
55
- },
56
- "optionalDependencies": {
57
- "@mdi/font": "^6.5.95",
58
- "@mdi/js": "^6.5.95",
59
- "ajv": "^8.11.0",
60
- "ajv-formats": "^2.1.1",
61
- "ajv-i18n": "^4.2.0",
62
- "expr-eval": "^2.0.2",
63
- "markdown-it": "^12.3.2",
64
- "property-expr": "^2.0.5",
65
- "vuedraggable": "^2.24.3"
12
+ "files": [
13
+ "types",
14
+ "src",
15
+ "LICENSE"
16
+ ],
17
+ "type": "module",
18
+ "exports": {
19
+ ".": {
20
+ "import": {
21
+ "default": "./src/index.js",
22
+ "types": "./types/index.d.ts"
23
+ }
24
+ },
25
+ "./components/*": {
26
+ "import": {
27
+ "default": "./src/components/*",
28
+ "types": "./types/components/*.d.ts"
29
+ }
30
+ },
31
+ "./compile": {
32
+ "import": {
33
+ "default": "./src/compile/index.js",
34
+ "types": "./types/compile/index.d.ts"
35
+ }
36
+ },
37
+ "./compat/v2": {
38
+ "import": {
39
+ "default": "./src/compat/v2.js",
40
+ "types": "./types/compat/v2.d.ts"
41
+ }
42
+ }
66
43
  },
67
44
  "peerDependencies": {
68
- "vuetify": "^2.0.0"
45
+ "vue": "^3.3.4",
46
+ "vuetify": "^3.3.20"
47
+ },
48
+ "dependencies": {
49
+ "@json-layout/core": "0.1.0",
50
+ "@json-layout/vocabulary": "0.1.0",
51
+ "@vueuse/core": "^10.5.0",
52
+ "debug": "^4.3.4",
53
+ "ejs": "^3.1.9",
54
+ "rfdc": "^1.3.0"
69
55
  },
70
56
  "devDependencies": {
71
- "@babel/core": "^7.16.12",
72
- "@babel/plugin-transform-runtime": "^7.17.0",
73
- "@babel/preset-env": "^7.16.11",
74
- "@koumoul/data-fair-search-widget": "^0.3.0",
75
- "@koumoul/gh-pages-multi": "^0.6.0",
76
- "@vue/test-utils": "^1.3.0",
77
- "babel-eslint": "^10.1.0",
78
- "babel-jest": "^27.5.1",
79
- "babel-loader": "^8.2.4",
80
- "eslint": "^6.1.0",
81
- "eslint-config-standard": "^13.0.1",
82
- "eslint-plugin-import": "^2.18.2",
83
- "eslint-plugin-jest": "^23.1.1",
84
- "eslint-plugin-node": "^9.1.0",
85
- "eslint-plugin-promise": "^4.2.1",
86
- "eslint-plugin-standard": "^4.0.0",
87
- "eslint-plugin-vue": "^7.20.0",
88
- "jest": "^27.5.1",
89
- "jest-serializer-vue": "^2.0.2",
90
- "random-words": "^1.1.2",
91
- "v-mask": "^2.3.0",
92
- "vue-axios": "^2.1.5",
93
- "vue-jest": "^3.0.7",
94
- "vue-loader": "^15.9.8",
95
- "webpack": "^5.72.0",
96
- "webpack-bundle-analyzer": "^4.5.0",
97
- "webpack-cli": "^4.9.2"
57
+ "@types/debug": "^4.1.8",
58
+ "@types/ejs": "^3.1.2",
59
+ "vitest": "^0.34.5",
60
+ "vue": "^3.3.4",
61
+ "vue-tsc": "^1.8.15"
98
62
  }
99
63
  }
@@ -0,0 +1,82 @@
1
+ import ajvModule from 'ajv'
2
+ import rfdc from 'rfdc'
3
+ import addFormats from 'ajv-formats'
4
+ import { resolveRefs } from '@json-layout/core'
5
+
6
+ // @ts-ignore
7
+ const Ajv = /** @type {typeof ajvModule.default} */ (ajvModule)
8
+
9
+ const clone = rfdc()
10
+
11
+ const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
12
+ if (!schema.layout) {
13
+ schema.layout = {}
14
+ if (schema['x-display']) {
15
+ schema.layout.comp = schema['x-display']
16
+ delete schema['x-display']
17
+ }
18
+
19
+ if (schema.format === 'hexcolor') {
20
+ schema.layout.comp = 'color-picker'
21
+ delete schema.format
22
+ }
23
+
24
+ if (schema['x-props']) {
25
+ schema.layout.props = schema['x-props']
26
+ delete schema['x-props']
27
+ }
28
+
29
+ if (Object.keys(schema.layout).length === 1 && 'comp' in schema.layout) {
30
+ schema.layout = schema.layout.comp
31
+ }
32
+ }
33
+
34
+ if (schema.type === 'object') {
35
+ if (schema.properties) {
36
+ for (const propertyKey of Object.keys(schema.properties)) {
37
+ processFragment(schema.properties[propertyKey])
38
+ }
39
+ }
40
+ if (schema.allOf) {
41
+ for (const item of schema.allOf) processFragment(item)
42
+ }
43
+ if (schema.oneOf) {
44
+ for (const item of schema.oneOf) processFragment(item)
45
+ }
46
+ if (schema.anyOf) {
47
+ for (const item of schema.anyOf) processFragment(item)
48
+ }
49
+ }
50
+
51
+ if (schema.type === 'array' && schema.items) {
52
+ if (Array.isArray(schema.items)) {
53
+ for (const item of schema.items) processFragment(item)
54
+ } else {
55
+ processFragment(schema.items)
56
+ }
57
+ }
58
+ }
59
+
60
+ /**
61
+ *
62
+ * @param {object} _schema
63
+ * @param {import("ajv").default} [_ajv]
64
+ * @param {string} lang
65
+ * @returns
66
+ */
67
+ export function v2compat (_schema, _ajv, lang = 'en') {
68
+ let ajv = _ajv
69
+ if (!ajv) {
70
+ /** @type {import('ajv').Options} */
71
+ const ajvOpts = { strict: false, allErrors: true }
72
+ const newAjv = new Ajv(ajvOpts)
73
+ addFormats.default(newAjv)
74
+ ajv = newAjv
75
+ }
76
+
77
+ const schema = /** @type {import("ajv").SchemaObject} */ (clone(_schema))
78
+ schema.$id = schema.$id ?? '_jl'
79
+ resolveRefs(schema, ajv, lang)
80
+ processFragment(schema)
81
+ return schema
82
+ }
@@ -0,0 +1,32 @@
1
+ import { readFileSync } from 'fs'
2
+ import { fileURLToPath } from 'url'
3
+ import path from 'path'
4
+ import ejs from 'ejs'
5
+ import { compile as compileLayout } from '@json-layout/core'
6
+ import { serialize as serializeCompiledLayout } from '@json-layout/core/src/compile/serialize'
7
+ import { isCompObject, isSwitchStruct } from '@json-layout/vocabulary'
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
10
+
11
+ const template = readFileSync(path.join(__dirname, 'v-jsf-compiled.vue.ejs'), 'utf8')
12
+
13
+ /**
14
+ * @param {object} schema
15
+ * @param {string} baseImport
16
+ * @returns {string}
17
+ */
18
+ export function compile (schema, baseImport = '@koumoul/vjsf/components') {
19
+ const compiledLayout = compileLayout(schema, { code: true })
20
+ const compiledLayoutCode = serializeCompiledLayout(compiledLayout)
21
+ /** @type Set<string> */
22
+ const comps = new Set([])
23
+ for (const layout of Object.values(compiledLayout.normalizedLayouts)) {
24
+ if (isCompObject(layout)) comps.add(layout.comp)
25
+ if (isSwitchStruct(layout)) {
26
+ for (const switchCase of layout.switch) comps.add(switchCase.comp)
27
+ }
28
+ }
29
+ comps.delete('none')
30
+ const code = ejs.render(template, { compiledLayoutCode, comps, baseImport })
31
+ return code
32
+ }
@@ -0,0 +1,83 @@
1
+ <script setup>
2
+ // @ts-nocheck
3
+
4
+ import { StatefulLayout } from '@json-layout/core'
5
+ import { ref, shallowRef, getCurrentInstance, useSlots } from 'vue'
6
+ import { useElementSize } from '@vueuse/core'
7
+
8
+ import { defaultOptions } from '<%- baseImport %>/options.js'
9
+ import Tree from '<%- baseImport %>/tree.vue'
10
+ <% comps.forEach(function(comp){ %>
11
+ import <%= comp.replace(/-/g, '') %>Node from '<%- baseImport %>/nodes/<%= comp %>.vue'
12
+ <% }); %>
13
+
14
+ <%- compiledLayoutCode %>
15
+
16
+ const vueInstance = getCurrentInstance()
17
+ <% comps.forEach(function(comp){ %>
18
+ if (!vueInstance?.appContext.app.component('vjsf-node-<%= comp %>')) {
19
+ vueInstance.appContext.app.component('vjsf-node-<%= comp %>', <%= comp.replace(/-/g, '') %>Node)
20
+ }
21
+ <% }); %>
22
+
23
+ const props = defineProps(['modelValue', 'options'])
24
+ const emit = defineEmits(['update:modelValue', 'update:state'])
25
+
26
+ const statefulLayout = shallowRef(null)
27
+ const stateTree = shallowRef(null)
28
+
29
+ const el = ref(null)
30
+ const { width } = useElementSize(el)
31
+
32
+ const slots = useSlots()
33
+
34
+ const fullOptions = computed(() => {
35
+ if (!width.value) return null
36
+ return {
37
+ ...defaultOptions,
38
+ ...props.options,
39
+ context: props.options.context ? JSON.parse(JSON.stringify(props.options.context)) : {},
40
+ width: Math.round(width.value),
41
+ vjsfSlots: { ...slots }
42
+ }
43
+ })
44
+
45
+ const initStatefulLayout = () => {
46
+ if (!fullOptions.value) return
47
+ const _statefulLayout = new StatefulLayout(compiledLayout, compiledLayout.skeletonTree, fullOptions.value, props.modelValue)
48
+ statefulLayout.value = _statefulLayout
49
+ stateTree.value = _statefulLayout.stateTree
50
+ _statefulLayout.events.on('update', () => {
51
+ stateTree.value = _statefulLayout.stateTree
52
+ emit('update:modelValue', _statefulLayout.data)
53
+ emit('update:state', _statefulLayout)
54
+ })
55
+ emit('update:state', _statefulLayout)
56
+ }
57
+
58
+ watch(fullOptions, (newOptions) => {
59
+ if (!newOptions) {
60
+ statefulLayout.value = null
61
+ } else if (statefulLayout.value) {
62
+ statefulLayout.value.options = newOptions
63
+ } else {
64
+ initStatefulLayout()
65
+ }
66
+ })
67
+
68
+ // case where data is updated from outside
69
+ watch(() => props.modelValue, (newData) => {
70
+ if (statefulLayout.value && statefulLayout.value.data !== newData) statefulLayout.value.data = newData
71
+ })
72
+
73
+ </script>
74
+
75
+ <template>
76
+ <div ref="el">
77
+ <tree
78
+ v-if="statefulLayout && stateTree"
79
+ :model-value="stateTree"
80
+ :stateful-layout="statefulLayout"
81
+ />
82
+ </div>
83
+ </template>
@@ -0,0 +1,49 @@
1
+ <script>
2
+ // @ts-nocheck
3
+
4
+ import { isTextSlot, isMarkdownSlot, isNameSlot } from '@json-layout/vocabulary'
5
+ import { h } from 'vue'
6
+
7
+ export default {
8
+ props: {
9
+ layoutSlot: {
10
+ /** @type import('vue').PropType<import('@json-layout/vocabulary').Slot> */
11
+ type: Object,
12
+ required: true
13
+ },
14
+ node: {
15
+ /** @type import('vue').PropType<import('../types.js').VjsfNode> */
16
+ type: Object,
17
+ required: true
18
+ },
19
+ statefulLayout: {
20
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
21
+ type: Object,
22
+ required: true
23
+ },
24
+ tag: {
25
+ /** @type import('vue').PropType<string> */
26
+ type: String,
27
+ default: null
28
+ }
29
+ },
30
+ render () {
31
+ const renderTag = this.tag ?? (isTextSlot(this.layoutSlot) ? 'p' : 'div')
32
+
33
+ if (isTextSlot(this.layoutSlot)) {
34
+ return h(renderTag, this.layoutSlot.text)
35
+ }
36
+ if (isMarkdownSlot(this.layoutSlot)) {
37
+ return h(renderTag, { innerHTML: this.layoutSlot.markdown })
38
+ }
39
+ if (isNameSlot(this.layoutSlot)) {
40
+ if (!this.statefulLayout.options.vjsfSlots[this.layoutSlot.name]) {
41
+ console.error(`layout references a code slot "${this.layoutSlot.name}" that was not provided.`)
42
+ } else {
43
+ return h(renderTag, this.statefulLayout.options.vjsfSlots[this.layoutSlot.name]({ node: this.node, statefulLayout: this.statefulLayout }))
44
+ }
45
+ }
46
+ return null
47
+ }
48
+ }
49
+ </script>
@@ -0,0 +1,52 @@
1
+ <script setup>
2
+ import { VAlert } from 'vuetify/components'
3
+ import { computed } from 'vue'
4
+
5
+ const props = defineProps({
6
+ node: {
7
+ /** @type import('vue').PropType<import('../types.js').VjsfNode> */
8
+ type: Object,
9
+ required: true
10
+ }
11
+ })
12
+
13
+ const titleDepthBase = computed(() => {
14
+ if (props.node.options.density === 'compact') return 6
15
+ if (props.node.options.density === 'comfortable') return 7
16
+ return 8
17
+ })
18
+
19
+ const classes = ['text-h1', 'text-h2', 'text-h3', 'text-h4', 'text-h5', 'text-h6', 'text-subtitle-1', 'text-subtitle-2']
20
+ const titleClass = computed(() => {
21
+ const index = props.node.options.titleDepth
22
+ if (props.node.options.density === 'compact') return classes[index + 2]
23
+ if (props.node.options.density === 'comfortable') return classes[index + 1]
24
+ return classes[index]
25
+ })
26
+ </script>
27
+
28
+ <template>
29
+ <div :class="`mb-${titleDepthBase - node.options.titleDepth} mt-${titleDepthBase - node.options.titleDepth}`">
30
+ <component
31
+ :is="`h${node.options.titleDepth}`"
32
+ v-if="node.layout.title"
33
+ :class="`${titleClass}`"
34
+ >
35
+ {{ node.layout.title }}
36
+ </component>
37
+ <p
38
+ v-if="node.layout.subtitle"
39
+ :class="`text-subtitle mt-${titleDepthBase - node.options.titleDepth}`"
40
+ >
41
+ {{ node.layout.subtitle }}
42
+ </p>
43
+ <v-alert
44
+ v-if="node.error && node.validated"
45
+ v-bind="node.options.errorAlertProps"
46
+ :class="`mt-${titleDepthBase - node.options.titleDepth}`"
47
+ :density="node.options.density"
48
+ >
49
+ {{ node.error }}
50
+ </v-alert>
51
+ </div>
52
+ </template>
@@ -0,0 +1,68 @@
1
+ <script setup>
2
+ import { VMenu, VTextField } from 'vuetify/components'
3
+ import { computed, ref } from 'vue'
4
+ import { getCompProps, getInputProps } from '../../utils/props.js'
5
+
6
+ const props = defineProps({
7
+ modelValue: {
8
+ /** @type import('vue').PropType<import('../types.js').VjsfNode> */
9
+ type: Object,
10
+ required: true
11
+ },
12
+ statefulLayout: {
13
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
14
+ type: Object,
15
+ required: true
16
+ },
17
+ formattedValue: {
18
+ /** @type import('vue').PropType<string | null> */
19
+ type: String,
20
+ default: null
21
+ }
22
+ })
23
+
24
+ const fieldProps = computed(() => {
25
+ const fieldProps = getInputProps(props.modelValue, props.statefulLayout, false)
26
+ fieldProps.readonly = true
27
+ return fieldProps
28
+ })
29
+
30
+ const menuProps = computed(() => {
31
+ const menuProps = getCompProps(props.modelValue, 'menu', false)
32
+ menuProps.closeOnContentClick = false
33
+ menuProps.disabled = true
34
+ return menuProps
35
+ })
36
+
37
+ const textField = ref(null)
38
+ const menuOpened = ref(false)
39
+
40
+ </script>
41
+
42
+ <template>
43
+ <v-text-field
44
+ ref="textField"
45
+ v-bind="fieldProps"
46
+ :model-value="formattedValue ?? modelValue.data"
47
+ @click:control="e => {menuOpened = !menuOpened; e.stopPropagation()}"
48
+ >
49
+ <template #prepend-inner>
50
+ <slot name="prepend-inner" />
51
+ </template>
52
+ </v-text-field>
53
+ <v-menu
54
+ v-if="textField"
55
+ v-bind="menuProps"
56
+ v-model="menuOpened"
57
+ class="vjsf-text-field-menu"
58
+ :activator="textField"
59
+ >
60
+ <slot :close="() => menuOpened = false" />
61
+ </v-menu>
62
+ </template>
63
+
64
+ <style>
65
+ .vjsf-text-field-menu .v-sheet.v-color-picker {
66
+ overflow-x: hidden;
67
+ }
68
+ </style>
@@ -0,0 +1,60 @@
1
+ <script setup>
2
+ import nodeSlot from './fragments/node-slot.vue'
3
+
4
+ defineProps({
5
+ modelValue: {
6
+ /** @type import('vue').PropType<import('./types.js').VjsfNode> */
7
+ type: Object,
8
+ required: true
9
+ },
10
+ statefulLayout: {
11
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
12
+ type: Object,
13
+ required: true
14
+ }
15
+ })
16
+
17
+ /** @type Record<import('./types.js').Density, string> */
18
+ const beforeAfterClasses = {
19
+ compact: 'my-1',
20
+ comfortable: 'my-2',
21
+ default: 'my-3'
22
+ }
23
+ </script>
24
+
25
+ <template>
26
+ <v-col
27
+ :cols="modelValue.cols"
28
+ >
29
+ <node-slot
30
+ v-if="modelValue.layout.slots?.before"
31
+ key="before"
32
+ :layout-slot="modelValue.layout.slots?.before"
33
+ :node="modelValue"
34
+ :stateful-layout="statefulLayout"
35
+ :class="beforeAfterClasses[/** @type import('./types.js').VjsfOptions */(modelValue.options).density]"
36
+ />
37
+
38
+ <node-slot
39
+ v-if="modelValue.layout.slots?.component"
40
+ key="component"
41
+ :layout-slot="modelValue.layout.slots?.component"
42
+ :node="modelValue"
43
+ :stateful-layout="statefulLayout"
44
+ />
45
+ <component
46
+ :is="`vjsf-node-${modelValue.layout.comp}`"
47
+ v-else-if="modelValue.layout.comp !== 'none'"
48
+ :model-value="modelValue"
49
+ :stateful-layout="statefulLayout"
50
+ />
51
+ <node-slot
52
+ v-if="modelValue.layout.slots?.after"
53
+ key="after"
54
+ :layout-slot="modelValue.layout.slots?.after"
55
+ :node="modelValue"
56
+ :stateful-layout="statefulLayout"
57
+ :class="beforeAfterClasses[/** @type import('./types.js').VjsfOptions */(modelValue.options).density]"
58
+ />
59
+ </v-col>
60
+ </template>
@@ -0,0 +1,27 @@
1
+ <script setup>
2
+ import { VCheckbox } from 'vuetify/components'
3
+ import { computed } from 'vue'
4
+ import { getInputProps } from '../../utils/props.js'
5
+
6
+ const props = defineProps({
7
+ modelValue: {
8
+ /** @type import('vue').PropType<import('../types.js').VjsfCheckboxNode> */
9
+ type: Object,
10
+ required: true
11
+ },
12
+ statefulLayout: {
13
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
14
+ type: Object,
15
+ required: true
16
+ }
17
+ })
18
+
19
+ const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout))
20
+ </script>
21
+
22
+ <template>
23
+ <v-checkbox
24
+ v-bind="fieldProps"
25
+ @update:model-value="value => statefulLayout.input(modelValue, value)"
26
+ />
27
+ </template>
@@ -0,0 +1,43 @@
1
+ <script setup>
2
+ import TextFieldMenu from '../fragments/text-field-menu.vue'
3
+ import { VColorPicker } from 'vuetify/components'
4
+ import { computed } from 'vue'
5
+ import { getCompProps } from '../../utils/props.js'
6
+
7
+ const props = defineProps({
8
+ modelValue: {
9
+ /** @type import('vue').PropType<import('../types.js').VjsfColorPickerNode> */
10
+ type: Object,
11
+ required: true
12
+ },
13
+ statefulLayout: {
14
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
15
+ type: Object,
16
+ required: true
17
+ }
18
+ })
19
+
20
+ const colorPickerProps = computed(() => {
21
+ const colorPickerProps = getCompProps(props.modelValue, 'colorPicker', true)
22
+ return colorPickerProps
23
+ })
24
+ </script>
25
+
26
+ <template>
27
+ <text-field-menu
28
+ :model-value="modelValue"
29
+ :stateful-layout="statefulLayout"
30
+ :formatted-value="modelValue.data"
31
+ >
32
+ <template
33
+ v-if="modelValue.data"
34
+ #prepend-inner
35
+ >
36
+ <div :style="`height:30px; width: 30px; border-radius: 40px; margin-right:6px; background: ${modelValue.data};`" />
37
+ </template>
38
+ <v-color-picker
39
+ v-bind="colorPickerProps"
40
+ @update:model-value="value => statefulLayout.input(modelValue, value)"
41
+ />
42
+ </text-field-menu>
43
+ </template>