Commit 5b86fb1d authored by Petar Hristov's avatar Petar Hristov 💬
Browse files

Merge branch 'Issue/2035-rcv' into 'uiv2'

Update: Adapt Form-Generator for UIv2 (coscine/issues#2035)

See merge request !80
parents 7f1d93cf 5ac312ec
......@@ -3,19 +3,28 @@ module.exports = {
env: {
node: true,
},
ignorePatterns: ["node_modules", "build", "coverage"],
plugins: ["eslint-comments", "functional"],
extends: [
'plugin:vue/essential',
'eslint:recommended',
'@vue/typescript/recommended',
'@vue/prettier',
'@vue/prettier/@typescript-eslint',
"plugin:vue/essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint",
],
parserOptions: {
ecmaVersion: 2020,
project: "./tsconfig.json"
parser: "@typescript-eslint/parser",
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"eslint-comments/disable-enable-pair": [
"error",
{ "allowWholeFile": true }
],
"@typescript-eslint/no-empty-interface": 1, // empty Interfaces will be only warnings for now.
"vue/multi-word-component-names": "off"
},
};
}
packageExtensions:
"@vue/cli-service@*":
peerDependencies:
"@vue/cli-plugin-eslint": "*"
"@vue/cli-plugin-typescript": "*"
"fork-ts-checker-webpack-plugin@*":
dependencies:
"vue-template-compiler": "*"
......@@ -19,3 +15,6 @@ packageExtensions:
dependencies:
"vue": "^2.6.14"
"jquery": "*"
"vue-eslint-parser@*":
dependencies:
"@typescript-eslint/parser" : "*"
......@@ -2,17 +2,16 @@
"name": "@coscine/form-generator",
"version": "1.17.0",
"main": "dist/index.umd.js",
"module": "dist/index.common.js",
"browser": "dist/index.umd.min.js",
"style": "dist/index.css",
"module": "dist/index.es.js",
"browser": "dist/index.umd.js",
"style": "dist/style.css",
"directories": {
"doc": "docs"
},
"scripts": {
"build": "vue-cli-service build --target lib --name index ./src/index.ts",
"dev": "vue-cli-service build --target lib --name index ./src/index.ts --mode development",
"lint": "vue-cli-service lint",
"test:unit": "vue-cli-service test:unit"
"build": "vite build",
"lint": "eslint './src/**/*.{js,ts,tsx,vue,md}'",
"lint:fix": "eslint './src/**/*.{js,ts,tsx,vue,md}' --fix"
},
"files": [
"dist/*"
......@@ -22,12 +21,12 @@
"singleQuote": true
},
"dependencies": {
"@coscine/api-client": "^1.3.0",
"@coscine/app-util": "^1.9.0",
"@coscine/api-client": "^1.5.0",
"bootstrap-vue": "^2.20.1",
"rdf-ext": "^1.3.4",
"rdf-parse": "^1.5.0",
"rdf-parse": "1.8.0",
"rdf-validate-shacl": "^0.2.5",
"stream-browserify": "^3.0.0",
"uuid": "^8.3.2",
"vue": "^2.6.14",
"vue-i18n": "^8.22.2",
......@@ -48,21 +47,28 @@
"@types/rdf-validate-shacl": "^0.2.4",
"@types/uuid": "^8.3.1",
"@types/vuelidate": "^0.7.13",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-eslint": "^4.5.7",
"@vue/cli-plugin-typescript": "^4.5.7",
"@vue/cli-service": "^4.5.7",
"@typescript-eslint/eslint-plugin": "^5.15.0",
"@typescript-eslint/parser": "^5.15.0",
"@vue/cli-plugin-eslint": "^4.5.15",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"@vue/eslint-config-typescript": "^8.0.0",
"conventional-changelog-eslint": "3.0.9",
"core-js": "^3.8.2",
"eslint": "^7.17.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^6.2.2",
"prettier": "^2.2.1",
"eslint": "^8.11.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^4.2.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.5.0",
"prettier": "^2.5.1",
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-polyfills": "^0.2.1",
"rollup-plugin-polyfill-node": "^0.8.0",
"semantic-release": "^17.3.1",
"typescript": "~4.4.3",
"vite": "^2.8.6",
"vite-plugin-vue2": "^1.9.3",
"vue-template-compiler": "^2.6.14"
},
"repository": {
......
......@@ -11,6 +11,8 @@
:languageLocale="languageLocale"
:v="$v"
:errorMessages="errorMessages"
:classReceiver="classReceiver"
:userReceiver="userReceiver"
@input="input"
@triggerValidation="
validateMetadata(formData, quads, applicationProfileId)
......@@ -21,26 +23,29 @@
</template>
<script lang="ts">
import Vue from 'vue';
import Vue, { PropType } from 'vue';
import locale from '@/locale';
import VueI18n from 'vue-i18n';
import Vuelidate from 'vuelidate';
import { MetadataApi, UserApi } from '@coscine/api-client';
import { Quad } from 'rdf-js';
import { LanguageUtil } from '@coscine/app-util';
import FieldReader from './util/FieldReader';
import WrapperInput from './components/WrapperInput.vue';
import LinkedDataHandler from './base/LinkedDataHandler';
import type { UserObject } from '@coscine/api-client/dist/types/Coscine.Api.User';
import type { BilingualLabels } from '@coscine/api-client/dist/types/Coscine.Api.Metadata';
Vue.use(VueI18n);
Vue.use(Vuelidate);
const i18n = new VueI18n({
locale: 'en',
messages:
window.coscine && window.coscine.i18n
? window.coscine.i18n['form-generator']
: undefined,
messages: locale,
silentFallbackWarn: true,
});
......@@ -80,9 +85,25 @@ export default LinkedDataHandler.extend({
type: String,
},
languageLocale: {
default: () => LanguageUtil.getLanguage(),
default: 'en',
type: String,
},
classReceiver: {
default: async (classUrl: string) => {
const response = await MetadataApi.metadataGetClassInstances(classUrl);
return response.data;
},
type: Function as PropType<
(classUrl: string) => Promise<BilingualLabels>
>,
},
userReceiver: {
default: async () => {
const response = await UserApi.userGetUser();
return response.data;
},
type: Function as PropType<() => Promise<UserObject>>,
},
},
data() {
return {
......@@ -284,51 +305,7 @@ export default LinkedDataHandler.extend({
</script>
<style>
.multiselect {
height: auto;
}
.multiselect__input {
border: 0px !important;
height: auto;
padding: 0px;
}
.multiselect__tags {
border-radius: 0px;
height: calc(1.4em + 0.75rem + 2px);
min-height: calc(1.4em + 0.75rem + 2px);
padding-top: 2px;
padding-bottom: 2px;
padding-left: 5px;
}
.multiselect__tag {
background-color: #00549f !important;
}
.multiselect__select {
height: calc(1.4em + 0.75rem + 2px);
min-height: calc(1.4em + 0.75rem + 2px);
}
.multiselect,
.multiselect__input,
.multiselect__single {
font-size: 14px;
}
.multiselect__input,
.multiselect__single {
vertical-align: -webkit-baseline-middle;
margin-bottom: unset;
}
.multiselect__option--highlight {
background-color: #00549f !important;
background: #00549f !important;
}
.multiselect__option--selected.multiselect__option--highlight {
background-color: #a70619 !important;
}
.multiselect__tag-icon:focus,
.multiselect__tag-icon:hover {
background-color: #a70619 !important;
}
.invalid-tooltip {
#FormGenerator .invalid-tooltip {
position: absolute;
top: 100%;
z-index: 5;
......@@ -349,11 +326,8 @@ export default LinkedDataHandler.extend({
content: ' *';
color: #a70619;
}
#FormGenerator .col-form-label {
font-weight: bold;
}
#FormGenerator .innerButton {
height: calc(1.4em + 0.75rem + 2px);
height: calc(1.5em + 0.75rem + 2px);
margin: 0px !important;
min-width: 60px !important;
width: 60px !important;
......
......@@ -73,7 +73,7 @@ export default Vue.extend({
const data = await this.loadDataset(
JSON.stringify(combinedDataObject),
'application/ld+json',
null
undefined
);
const validator = new SHACLValidator(quads as any);
const report = validator.validate(data);
......@@ -83,7 +83,7 @@ export default Vue.extend({
async getQuads(
data: string,
mimeType: string,
baseUri: string | null
baseUri: string | undefined
): Promise<Array<Quad>> {
const input = new Readable({
read: () => {
......@@ -101,7 +101,11 @@ export default Vue.extend({
.on('end', () => resolve(quads));
});
},
async loadDataset(data: string, mimeType: string, baseUri: string | null) {
async loadDataset(
data: string,
mimeType: string,
baseUri: string | undefined
) {
const quads = await this.getQuads(data, mimeType, baseUri);
const dataSet = factory.dataset();
for (const quad of quads) {
......
......@@ -19,6 +19,8 @@
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import locale from '@/locale';
import FieldReader from '@/util/FieldReader';
import Multiselect from 'vue-multiselect';
import 'vue-multiselect/dist/vue-multiselect.min.css';
......@@ -28,10 +30,7 @@ Vue.use(VueI18n);
const i18n = new VueI18n({
locale: 'en',
messages:
window.coscine && window.coscine.i18n
? window.coscine.i18n['form-generator']
: {},
messages: locale,
silentFallbackWarn: true,
});
......
......@@ -16,25 +16,27 @@
</template>
<script lang="ts">
import Vue from 'vue';
import Vue, { PropType } from 'vue';
import VueI18n from 'vue-i18n';
import locale from '@/locale';
import Multiselect from 'vue-multiselect';
import 'vue-multiselect/dist/vue-multiselect.min.css';
Vue.component('multiselect', Multiselect);
import { MetadataApi } from '@coscine/api-client';
import FieldReader from '@/util/FieldReader';
import { Label } from '@coscine/api-client/dist/types/Coscine.Api.Metadata';
import type {
BilingualLabels,
Label,
} from '@coscine/api-client/dist/types/Coscine.Api.Metadata';
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: 'en',
messages:
window.coscine && window.coscine.i18n
? window.coscine.i18n['form-generator']
: {},
messages: locale,
silentFallbackWarn: true,
});
......@@ -116,17 +118,15 @@ export default Vue.extend({
},
async retrieveLabels() {
try {
const response = await MetadataApi.metadataGetClassInstances(
this.class
);
if (this.languageCode === 'en' && response.data.en) {
this.selectableOptions = response.data.en;
} else if (this.languageCode === 'de' && response.data.de) {
this.selectableOptions = response.data.de;
} else if (response.data.en && response.data.en.length) {
this.selectableOptions = response.data.en;
} else if (response.data.de && response.data.de.length) {
this.selectableOptions = response.data.de;
const bilingualLabels = await this.classReceiver(this.class);
if (this.languageCode === 'en' && bilingualLabels.en) {
this.selectableOptions = bilingualLabels.en;
} else if (this.languageCode === 'de' && bilingualLabels.de) {
this.selectableOptions = bilingualLabels.de;
} else if (bilingualLabels.en && bilingualLabels.en.length) {
this.selectableOptions = bilingualLabels.en;
} else if (bilingualLabels.de && bilingualLabels.de.length) {
this.selectableOptions = bilingualLabels.de;
} else {
this.selectableOptions = [];
}
......@@ -151,6 +151,11 @@ export default Vue.extend({
languageCode: String,
v: Object,
entryKey: Number,
classReceiver: {
type: Function as PropType<
(classUrl: string) => Promise<BilingualLabels>
>,
},
},
components: {},
});
......
......@@ -2,7 +2,6 @@
<b-form-datepicker
:id="cssId"
class="formgeneratordatepicker"
size="sm"
v-model="selectedDate"
:locale="languageCode"
:disabled="disabledMode || locked"
......
......@@ -10,13 +10,14 @@
</template>
<script lang="ts">
import Vue from 'vue';
import Vue, { PropType } from 'vue';
import { BFormTextarea } from 'bootstrap-vue';
import { UserApi } from '@coscine/api-client';
import FieldReader from '@/util/FieldReader';
import type { UserObject } from '@coscine/api-client/dist/types/Coscine.Api.User';
export default Vue.extend({
name: 'InputTextArea',
components: {
......@@ -65,6 +66,9 @@ export default Vue.extend({
type: [Boolean, Object],
default: null,
},
userReceiver: {
type: Function as PropType<() => Promise<UserObject>>,
},
},
methods: {
input() {
......@@ -92,11 +96,11 @@ export default Vue.extend({
},
async retrieveUser() {
try {
const response = await UserApi.userGetUser();
const user = await this.userReceiver();
this.$set(
this.formData[this.nodeName][this.entryKey],
'value',
response.data.displayName
user.displayName
);
this.textValue = this.formData[this.nodeName][this.entryKey]['value'];
this.$forceUpdate();
......
......@@ -11,13 +11,14 @@
</template>
<script lang="ts">
import Vue from 'vue';
import Vue, { PropType } from 'vue';
import { BFormInput } from 'bootstrap-vue';
import { UserApi } from '@coscine/api-client';
import FieldReader from '@/util/FieldReader';
import type { UserObject } from '@coscine/api-client/dist/types/Coscine.Api.User';
export default Vue.extend({
name: 'InputTextField',
components: {
......@@ -88,6 +89,9 @@ export default Vue.extend({
type: [Boolean, Object],
default: null,
},
userReceiver: {
type: Function as PropType<() => Promise<UserObject>>,
},
},
methods: {
input() {
......@@ -118,11 +122,11 @@ export default Vue.extend({
},
async retrieveUser() {
try {
const response = await UserApi.userGetUser();
const user = await this.userReceiver();
this.$set(
this.formData[this.nodeName][this.entryKey],
'value',
response.data.displayName
user.displayName
);
this.textValue = this.formData[this.nodeName][this.entryKey]['value'];
this.$forceUpdate();
......
<template>
<div
:class="componentDefinition"
v-if="componentDefinition !== null"
v-if="componentDefinition !== null && formData[nodeName]"
:style="cssProps"
>
<b-form-group
......@@ -39,6 +39,8 @@
:v="v"
:entryKey="entryIndex"
:state="state"
:classReceiver="classReceiver"
:userReceiver="userReceiver"
@triggerValidation="triggerValidation"
/>
<b-button-group class="wrapper-input-button">
......@@ -93,9 +95,11 @@
</template>
<script lang="ts">
import Vue from 'vue';
import Vue, { PropType } from 'vue';
import VueI18n from 'vue-i18n';
import locale from '@/locale';
import LockIcon from 'vue-material-design-icons/Lock.vue';
import LockOpenIcon from 'vue-material-design-icons/LockOpen.vue';
import MinusIcon from 'vue-material-design-icons/Minus.vue';
......@@ -123,14 +127,14 @@ Vue.use(VueI18n);
const i18n = new VueI18n({
locale: 'en',
messages:
window.coscine && window.coscine.i18n
? window.coscine.i18n['form-generator']
: {},
messages: locale,
silentFallbackWarn: true,
});
import { v4 as uuid_v4 } from 'uuid';
import type { BilingualLabels } from '@coscine/api-client/dist/types/Coscine.Api.Metadata';
import type { UserObject } from '@coscine/api-client/dist/types/Coscine.Api.User';
export default Vue.extend({
i18n,
name: 'WrapperInput',
......@@ -313,6 +317,14 @@ export default Vue.extend({
languageLocale: String,
errorMessages: Object,
v: Object,
classReceiver: {
type: Function as PropType<
(classUrl: string) => Promise<BilingualLabels>
>,
},
userReceiver: {
type: Function as PropType<() => Promise<UserObject>>,
},
},
methods: {
changeLockable(entryKey: number) {
......
import type { VueConstructor } from 'vue';
import FormGenerator from './FormGenerator.vue';
export default {
install(Vue: any, options: any) {
install(Vue: VueConstructor<Vue>, options: any): void {
Vue.component('FormGenerator', FormGenerator);
},
};
import type VueI18n from 'vue-i18n';
export default {
en: {
selectPlaceholder: 'Select...',
invisibilityInfo:
'Fields can be hidden to remove non-applicable fields from the metadata view. Mandatory fields can only be hidden if they have a default value that is marked as unchangeable.',
},
de: {
selectPlaceholder: 'Auswählen...',
invisibilityInfo:
'Felder können ausgeblendet werden, um nicht zutreffende Felder aus der Metadatenansicht zu entfernen. Pflichtfelder können nur dann ausgeblendet werden, wenn sie einen Standardwert haben, der als nicht änderbar gekennzeichnet ist.',
},
} as VueI18n.LocaleMessages;
......@@ -3,9 +3,9 @@ import Vue, { VNode } from 'vue';
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
type Element = VNode;
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
type ElementClass = Vue;
interface IntrinsicElements {
[elem: string]: any;
}
......
......@@ -4,5 +4,4 @@ declare module '*.vue' {
}
declare module 'rdf-ext';
declare module 'rdf-parse';
declare module 'uuid';
import path from 'path';
import { defineConfig } from 'vite';
import { createVuePlugin } from "vite-plugin-vue2";
import nodePolyfills from 'rollup-plugin-polyfill-node';
import globalPolyfills from 'rollup-plugin-node-globals';
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {