
Security News
/Research
Wallet-Draining npm Package Impersonates Nodemailer to Hijack Crypto Transactions
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
@oarepo/data-editor
Advanced tools
A library for editing of rendered JSON data.
@oarepo/data-editor
requires @oarepo/data-renderer
to be installed as well.
yarn add @oarepo/data-editor
yarn add @oarepo/data-renderer
To register/configure the library, add a new boot file to quasar (or main.js for vue-cli projects):
import DataEditor from '@oarepo/data-editor'
import DataRenderer from '@oarepo/data-renderer'
export default async ({ Vue, store, router }) => {
Vue.use(DataEditor, {})
Vue.use(DataRenderer, {})
}
To use data editor, add data-editor
to template. data-editor
acceptsrecord
, options
, layout
and pathLayouts
and renders interface to edit received data with buttons for editing, addition and removal based on data type.
Dialog components and default values can be passed to data-editor
.
record
contains json data to be passed to data-editor.
record: { object: { a: 1 } }
record: { array: [1, 2] }
record: { array: [{ a: 1 }}, { b: 2 }] }
record: {}
options
may consist of schema, extraProps.
schema
of rendered data (inline, block, table, flex), defaults to table
options: { schema: table }
options: { schema: inline }
extraProps
(submit, cancel method for data editor)
options: { extraProps: { submit: { submit: this.submit, cancel: this.cancel } } }
layout
can be used to define the layout of an empty record, to associate dialog components or default values with props, to define children.
layout: [{ 'path': 'a', 'label': 'a' }]
layout: [{ 'path': 'a', 'label': 'a', dialogComponent: DialogComponent, dynamic: true }]
layout: [{ 'path': 'a', 'label': 'a', defaultValue: () => 1, dynamic: true }]
layout: [{ 'path': 'a', 'label': 'a', defaultValue: defaultValue, dynamic: true }]
pathLayouts
can be used to associate dialog components or default values with objects or arrays regardless of their position within the layout.
* simple default value options: { pathLayouts: { simpleArray: defaultValue: () => 1 } }
* complex default value options: { pathLayouts: { complexArray: defaultValue: () => { a: 1 } } }
If there is a complex value in layout
and not in data
, then this complex value must be created by clicking displayed button before it and its content can be edited.
layout
and pathLayouts
are described in more detail here: https://github.com/oarepo/data-renderer/
Specifies dialog component to be used with data editor, e.g. dialog to add a new object with default property to array, dialog to enter property and value to be added to object etc.
Examples are located at /src/components:
Example of a simple object. Src at /src/components/SimpleEdit.vue:
<template lang="pug">
data-editor(:record="record" :options="options" :layout="layout")
</template>
<script>
import Vue from 'vue'
export default {
name: 'simple-edit',
data: function () {
return {
record: {
Contact: {
Phone: '+420123123123',
Email: 'mary.black@gmail.com'
}
},
options: {
schema: 'table',
extraProps: {
submit: this.submit,
cancel: this.cancel
}
},
layout: {
showEmpty: true
}
}
},
methods: {
submit ({ context, prop, value, op }) {
isNaN(value)
if (op === 'add') {
Vue.set(context, prop, value)
}
if (op === 'replace') {
if (context[prop] === undefined) {
Vue.set(context, prop, value)
} else {
context[prop] = value
}
}
},
cancel (props) {
console.log('cancelling')
}
}
}
</script>
Example of a simple array. Src at /src/components/SimpleArray.vue:
<template lang="pug">
data-editor(:record="record" :options="options" :layout="layout")
</template>
<script>
import Vue from 'vue'
export default {
name: 'array-edit',
data: function () {
return {
record: {
keywords: ['first keyword', 'second keyword']
},
options: {
schema: 'table',
extraProps: {
submit: this.submit,
cancel: this.cancel
}
},
layout: {
children: [
{
prop: 'keywords',
label: {
label: 'keywordArray'
},
item: {
label: {
label: 'keyword'
}
}
}
]
}
}
},
methods: {
submit ({ path, context, prop, value, op, pathValues }) {
if (op === 'add') {
if (Array.isArray(context)) {
context.push(value)
} else {
context[prop] = value
}
}
if (op === 'replace') {
Vue.set(context, prop, value)
}
if (op === 'remove') {
if (Array.isArray(context)) {
context.splice(prop, 1)
} else {
delete context[prop]
}
}
},
cancel ({ props }) {
console.log('cancelling')
}
}
}
</script>
Example of a complex array with default value for newly added items. Src at /src/components/SimpleArray.vue:
<template lang="pug">
data-editor(:record="record" :options="options" :layout="layout")
</template>
<script>
export default {
name: 'default-value-complex-array-edit',
data: function () {
return {
record: {
contact: [{ email: 1 }, { phone: '+420123123123' }]
},
options: {
schema: 'table',
extraProps: {
submit: this.submit,
cancel: this.cancel
}
},
layout: {
children: [{
prop: 'contact',
additionalProps: { defaultValue: () => ({ phone: '+420123123124' }) }
}]
}
}
},
methods: {
submit ({ path, context, prop, value, op, pathValues }) {
if (op === 'add') {
if (Array.isArray(context)) {
context.push(value)
} else {
context[prop] = value
}
}
if (op === 'replace') {
context[prop] = value
}
if (op === 'remove') {
if (Array.isArray(context)) {
context.splice(prop, 1)
} else {
delete context[prop]
}
}
},
cancel ({ props }) {
console.log('cancelling')
}
}
}
</script>
Example of a complex array with dialog for addition of new items. Src at /src/components/ComplexArrayDialogEdit.vue:
<template lang="pug">
data-editor(:record="record" :options="options" :layout="layout")
</template>
<script>
import Vue from 'vue'
import DialogComponent from './DialogComponent'
export default {
name: 'complex-array-dialog-edit',
data: function () {
return {
record: {
contact: [{ phone: '+420123123124' }, { email: 'mary.black@gmail.com' }]
},
options: {
schema: 'table',
extraProps: {
submit: this.submit,
cancel: this.cancel,
dialogComponent: DialogComponent
}
},
layout: {
children: [
{
prop: 'contact'
}
]
}
}
},
methods: {
submit ({ path, context, prop, value, op, pathValues }) {
if (op === 'add') {
if (Array.isArray(context)) {
context.push(value)
} else {
Vue.set(context, prop, value)
}
}
if (op === 'replace') {
context[prop] = value
}
if (op === 'remove') {
if (Array.isArray(context)) {
context.splice(prop, 1)
} else {
delete context[prop]
}
}
},
cancel ({ props }) {
console.log('cancelling')
}
}
}
</script>
Example of an empty record with layout and dialog. Src at /src/components/NonExistingObjectDialogEdit.vue:
<template lang="pug">
data-editor(:record="record" :options="options" :layout="layout")
</template>
<script>
import Vue from 'vue'
import DialogWithPropertyComponent from './DialogWithPropertyComponent'
export default {
name: 'non-existing-object-dialog-edit',
data: function () {
return {
record: {},
options: {
schema: 'table',
extraProps: {
submit: this.submit,
cancel: this.cancel
}
},
layout: {
showEmpty: true,
children: [
{
prop: 'object',
additionalProps: { dialogComponent: DialogWithPropertyComponent },
children: []
}
]
}
}
},
methods: {
submit ({ path, context, prop, value, op, pathValues }) {
if (op === 'add') {
if (Array.isArray(context)) {
context.push(value)
} else {
Vue.set(context, prop, value)
}
}
if (op === 'replace') {
context[prop] = value
}
},
cancel ({ props }) {
console.log('cancelling')
}
}
}
</script>
Example of a dialog component. Src at /src/components/DialogComponent.vue:
<template lang="pug">
q-dialog(ref="dialog" @hide="onDialogHide")
q-card
q-card-section
q-form(ref="form")
q-input(label="value" v-model="value")
q-card-actions(align="right")
q-btn(color="primary" type="submit" label="OK" @click="onOKClick")
q-btn(color="primary" label="Cancel" @click="onCancelClick")
</template>
<script>
export default {
name: 'dialog-component',
data: function () {
return {
value: null
}
},
props: {
initialValue: Object
},
mounted () {
if (this.initialValue) {
this.value = this.initialValue
}
},
methods: {
show () {
this.$refs.dialog.show()
},
hide () {
this.$refs.dialog.hide()
},
onDialogHide () {
this.$emit('hide')
},
async onOKClick () {
if (await this.$refs.form.validate()) {
this.$emit('ok', { phone: this.value })
this.hide()
}
},
onCancelClick () {
this.hide()
}
}
}
</script>
Example of object with default value as a function and dialog component. Src at /src/components/AdditionalPropsEdit.vue:
<template lang="pug">
data-editor(:record="record" :options="options" :layout="layout")
</template>
<script>
import DialogWithPropertyComponent from './DialogWithPropertyComponent'
import Vue from 'vue'
function defaultValue ({ context, layout }) {
for (const prop of 'abcdefghijklmnopqrstuvwxyz'.split('')) {
if (context[layout.prop][prop] === undefined) {
return { prop: prop, value: 'keyword' }
}
}
}
export default {
name: 'additional-props-edit',
data: function () {
return {
record: {
creator: { name: 'Mary Black' },
contact: { phone: '+420123123124' },
keywords: {}
},
layout: {
children: [
{ prop: 'creator', additionalProps: { dialogComponent: DialogWithPropertyComponent } },
{ prop: 'contact', additionalProps: { dialogComponent: DialogWithPropertyComponent } },
{ prop: 'keywords', additionalProps: { defaultValue: defaultValue } }]
},
options: {
schema: 'table',
extraProps: {
submit: this.submit,
cancel: this.cancel
}
}
}
},
methods: {
submit ({ path, context, prop, value, op, pathValues }) {
if (op === 'add') {
if (Array.isArray(context)) {
context.push(value)
} else {
Vue.set(context, prop, value)
}
}
if (op === 'replace') {
context[prop] = value
}
if (op === 'remove') {
if (Array.isArray(context)) {
context.splice(prop, 1)
} else {
delete context[prop]
}
}
},
cancel ({ props }) {
console.log('cancelling')
}
}
}
</script>
Example of a record as tree with a complex default value for addition of new items. Src at /src/components/TreeEdit.vue:
<template lang="pug">
data-editor(:record="record" :options="options" :layout="layout" :path-layouts="pathLayouts")
</template>
<script>
import Vue from 'vue'
export default {
name: 'tree-edit',
data: function () {
return {
record: {
object: [
{ creator: 'Mary Black' },
{
contact: [
{ phone: '+420123123123' },
{ email: ['mary.black@gmail.com'] }]
}]
},
options: {
schema: 'table',
extraProps: {
submit: this.submit,
cancel: this.cancel
}
},
layout: {
children: [
{
prop: 'object',
additionalProps: { defaultValue: () => ({ keywords: ['first keyword', 'second keyword'] }) }
}
]
}
}
},
methods: {
submit ({ path, context, prop, value, op, pathValues }) {
if (op === 'add') {
if (Array.isArray(context)) {
context.push(value)
} else {
Vue.set(context, prop, value)
}
}
if (op === 'replace') {
context[prop] = value
}
if (op === 'remove') {
if (Array.isArray(context)) {
context.splice(prop, 1)
} else {
delete context[prop]
}
}
},
cancel ({ props }) {
console.log('cancelling')
}
}
}
</script>
FAQs
A library for editing of rendered JSON data.
We found that @oarepo/data-editor demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
/Research
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
Security News
/Research
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.