Apply prettier to whole project

This commit is contained in:
Ramon Wenger 2023-01-12 15:58:59 +01:00
parent 647e684469
commit 9a91aaf47c
443 changed files with 19003 additions and 17334 deletions

View File

@ -1,14 +1,15 @@
{ {
"presets": [ "presets": [
"@babel/preset-typescript", "@babel/preset-typescript",
["@babel/preset-env", { [
"@babel/preset-env",
{
"useBuiltIns": false, "useBuiltIns": false,
"targets": { "targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"] "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
} }
}] }
], ]
"plugins": [ ],
"@babel/plugin-transform-runtime" "plugins": ["@babel/plugin-transform-runtime"]
]
} }

View File

@ -10,7 +10,7 @@ module.exports = {
browser: true, browser: true,
}, },
globals: { globals: {
process: "readonly" process: 'readonly',
}, },
extends: [ extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
@ -21,32 +21,33 @@ module.exports = {
'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/eslint-recommended',
], ],
// required to lint *.vue files // required to lint *.vue files
plugins: [ plugins: ['vue', '@typescript-eslint'],
'vue', overrides: [
'@typescript-eslint' {
],
overrides: [{
files: ['*.ts', '*.tsx'], files: ['*.ts', '*.tsx'],
rules: { rules: {
'no-unused-vars': 'off' 'no-unused-vars': 'off',
} },
}], },
],
// add your custom rules here // add your custom rules here
rules: { rules: {
// allow async-await // allow async-await
'generator-star-spacing': 'off', 'generator-star-spacing': 'off',
// allow debugger during development // allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'indent': 'off', indent: 'off',
'semi': ['error', 'always'], semi: ['error', 'always'],
'space-before-function-paren': 'off', 'space-before-function-paren': 'off',
'comma-dangle': 'off', 'comma-dangle': 'off',
// vue rules // vue rules
'vue/require-prop-types': 'off', //todo: should we do this? 'vue/require-prop-types': 'off', //todo: should we do this?
'vue/require-default-prop': 'off', //todo: should we do this? 'vue/require-default-prop': 'off', //todo: should we do this?
'vue/attributes-order': ['error', { 'vue/attributes-order': [
'order': [ 'error',
{
order: [
'OTHER_ATTR', 'OTHER_ATTR',
'DEFINITION', 'DEFINITION',
'LIST_RENDERING', 'LIST_RENDERING',
@ -57,14 +58,20 @@ module.exports = {
'TWO_WAY_BINDING', 'TWO_WAY_BINDING',
'OTHER_DIRECTIVES', 'OTHER_DIRECTIVES',
'EVENTS', 'EVENTS',
'CONTENT' 'CONTENT',
] ],
}], },
"vue/multi-word-component-names": ["off", { ],
"ignores": [] 'vue/multi-word-component-names': [
}], 'off',
'vue/order-in-components': ['error', { {
'order': [ ignores: [],
},
],
'vue/order-in-components': [
'error',
{
order: [
'el', 'el',
'name', 'name',
'parent', 'parent',
@ -82,8 +89,9 @@ module.exports = {
'LIFECYCLE_HOOKS', 'LIFECYCLE_HOOKS',
'methods', 'methods',
['template', 'render'], ['template', 'render'],
'renderError' 'renderError',
] ],
}] },
} ],
},
}; };

View File

@ -1,10 +1,10 @@
// https://github.com/michael-ciniawsky/postcss-load-config // https://github.com/michael-ciniawsky/postcss-load-config
module.exports = { module.exports = {
"plugins": { plugins: {
"postcss-import": {}, 'postcss-import': {},
"postcss-url": {}, 'postcss-url': {},
// to edit target browsers: use "browserslist" field in package.json // to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {} autoprefixer: {},
} },
} };

View File

@ -1,41 +1,45 @@
'use strict' 'use strict';
require('./check-versions')() require('./check-versions')();
process.env.NODE_ENV = 'production' process.env.NODE_ENV = 'production';
const ora = require('ora') const ora = require('ora');
const rm = require('rimraf') const rm = require('rimraf');
const path = require('path') const path = require('path');
const chalk = require('chalk') const chalk = require('chalk');
const webpack = require('webpack') const webpack = require('webpack');
const config = require('../config') const config = require('../config');
const webpackConfig = require('./webpack.prod.conf') const webpackConfig = require('./webpack.prod.conf');
const spinner = ora('building for production...') const spinner = ora('building for production...');
spinner.start() spinner.start();
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), (err) => {
if (err) throw err if (err) throw err;
webpack(webpackConfig, (err, stats) => { webpack(webpackConfig, (err, stats) => {
spinner.succeed() spinner.succeed();
if (err) throw err if (err) throw err;
process.stdout.write(stats.toString({ process.stdout.write(
stats.toString({
colors: true, colors: true,
modules: false, modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false, chunks: false,
chunkModules: false chunkModules: false,
}) + '\n\n') }) + '\n\n'
);
if (stats.hasErrors()) { if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n')) console.log(chalk.red(' Build failed with errors.\n'));
process.exit(1) process.exit(1);
} }
console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.cyan(' Build complete.\n'));
console.log(chalk.yellow( console.log(
chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' + ' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n' " Opening index.html over file:// won't work.\n"
)) )
}) );
}) });
});

View File

@ -1,54 +1,53 @@
'use strict' 'use strict';
const chalk = require('chalk') const chalk = require('chalk');
const semver = require('semver') const semver = require('semver');
const packageConfig = require('../package.json') const packageConfig = require('../package.json');
const shell = require('shelljs') const shell = require('shelljs');
function exec(cmd) { function exec(cmd) {
return require('child_process').execSync(cmd).toString().trim() return require('child_process').execSync(cmd).toString().trim();
} }
const versionRequirements = [ const versionRequirements = [
{ {
name: 'node', name: 'node',
currentVersion: semver.clean(process.version), currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node versionRequirement: packageConfig.engines.node,
} },
] ];
if (shell.which('npm')) { if (shell.which('npm')) {
versionRequirements.push({ versionRequirements.push({
name: 'npm', name: 'npm',
currentVersion: exec('npm --version'), currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm versionRequirement: packageConfig.engines.npm,
}) });
} }
module.exports = function () { module.exports = function () {
const warnings = [] const warnings = [];
for (let i = 0; i < versionRequirements.length; i++) { for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i] const mod = versionRequirements[i];
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' + warnings.push(
chalk.red(mod.currentVersion) + ' should be ' + mod.name + ': ' + chalk.red(mod.currentVersion) + ' should be ' + chalk.green(mod.versionRequirement)
chalk.green(mod.versionRequirement) );
)
} }
} }
if (warnings.length) { if (warnings.length) {
console.log('') console.log('');
console.log(chalk.yellow('To use this template, you must update following to modules:')) console.log(chalk.yellow('To use this template, you must update following to modules:'));
console.log() console.log();
for (let i = 0; i < warnings.length; i++) { for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i] const warning = warnings[i];
console.log(' ' + warning) console.log(' ' + warning);
} }
console.log() console.log();
process.exit(1) process.exit(1);
}
} }
};

View File

@ -1,20 +1,17 @@
'use strict' 'use strict';
const path = require('path') const path = require('path');
const config = require('../config') const config = require('../config');
const packageConfig = require('../package.json') const packageConfig = require('../package.json');
const isDev = process.env.NODE_ENV !== 'production'; const isDev = process.env.NODE_ENV !== 'production';
const assetsPath = (_path) => { const assetsPath = (_path) => {
const assetsSubDirectory = isDev const assetsSubDirectory = isDev ? config.dev.assetsSubDirectory : config.build.assetsSubDirectory;
? config.dev.assetsSubDirectory
: config.build.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
return path.posix.join(assetsSubDirectory, _path);
};
module.exports = { module.exports = {
isDev, isDev,
assetsPath assetsPath,
} };

View File

@ -15,7 +15,7 @@ function resolve(dir) {
const eslintOptions = { const eslintOptions = {
formatter: require('eslint-formatter-friendly'), formatter: require('eslint-formatter-friendly'),
emitWarning: !config.dev.showEslintErrorsInOverlay, emitWarning: !config.dev.showEslintErrorsInOverlay,
extensions: ['js', 'ts', 'vue'] extensions: ['js', 'ts', 'vue'],
}; };
//todo: mini-css-extract-plugin? upgrade to webpack 4, then use this //todo: mini-css-extract-plugin? upgrade to webpack 4, then use this
@ -29,9 +29,7 @@ module.exports = {
output: { output: {
path: config.build.assetsRoot, path: config.build.assetsRoot,
filename: '[name].js', filename: '[name].js',
publicPath: isDev publicPath: isDev ? config.dev.assetsPublicPath : config.build.assetsPublicPath,
? config.dev.assetsPublicPath
: config.build.assetsPublicPath,
}, },
optimization: { optimization: {
splitChunks: { splitChunks: {
@ -131,10 +129,7 @@ module.exports = {
// styleRule(true), // sass rule // styleRule(true), // sass rule
], ],
}, },
plugins: [ plugins: [new VueLoaderPlugin(), new ESLintPlugin(eslintOptions)],
new VueLoaderPlugin(),
new ESLintPlugin(eslintOptions),
],
// node: { // node: {
// // prevent webpack from injecting useless setImmediate polyfill because Vue // // prevent webpack from injecting useless setImmediate polyfill because Vue

View File

@ -23,13 +23,10 @@ const devWebpackConfig = merge(baseWebpackConfig, {
logging: 'warn', logging: 'warn',
overlay: config.dev.errorOverlay ? { errors: true, warnings: false } : false, overlay: config.dev.errorOverlay ? { errors: true, warnings: false } : false,
progress: true, progress: true,
}, },
historyApiFallback: { historyApiFallback: {
rewrites: [ rewrites: [{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }],
{from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html')},
],
}, },
hot: true, hot: true,
compress: true, compress: true,
@ -66,8 +63,8 @@ const devWebpackConfig = merge(baseWebpackConfig, {
], ],
}), }),
new BundleAnalyzerPlugin({ new BundleAnalyzerPlugin({
analyzerMode: 'disabled' // do nothing by default, but be able to generate stats with --profile analyzerMode: 'disabled', // do nothing by default, but be able to generate stats with --profile
}) }),
], ],
}); });

View File

@ -7,7 +7,7 @@ const {merge} = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf'); const baseWebpackConfig = require('./webpack.base.conf');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const env = require('../config/prod.env'); const env = require('../config/prod.env');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
@ -21,9 +21,7 @@ const webpackConfig = merge(baseWebpackConfig, {
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'),
}, },
optimization: { optimization: {
minimizer: [ minimizer: [new CssMinimizerPlugin()],
new CssMinimizerPlugin()
]
}, },
plugins: [ plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html // http://vuejs.github.io/vue-loader/en/workflow/production.html
@ -44,7 +42,8 @@ const webpackConfig = merge(baseWebpackConfig, {
filename: config.build.index, filename: config.build.index,
template: 'index.html', template: 'index.html',
...require('../config/prod.env'), ...require('../config/prod.env'),
minify: { // defaults from https://github.com/jantimon/html-webpack-plugin#minification minify: {
// defaults from https://github.com/jantimon/html-webpack-plugin#minification
collapseWhitespace: true, collapseWhitespace: true,
keepClosingSlash: true, keepClosingSlash: true,
removeComments: true, removeComments: true,
@ -112,14 +111,10 @@ if (config.build.productionGzip) {
new CompressionWebpackPlugin({ new CompressionWebpackPlugin({
asset: '[path].gz[query]', asset: '[path].gz[query]',
algorithm: 'gzip', algorithm: 'gzip',
test: new RegExp( test: new RegExp('\\.(' + config.build.productionGzipExtensions.join('|') + ')$'),
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$',
),
threshold: 10240, threshold: 10240,
minRatio: 0.8, minRatio: 0.8,
}), })
); );
} }

View File

@ -1,8 +1,8 @@
'use strict' 'use strict';
const {merge} = require('webpack-merge') const { merge } = require('webpack-merge');
const prodEnv = require('./prod.env') const prodEnv = require('./prod.env');
module.exports = merge(prodEnv, { module.exports = merge(prodEnv, {
NODE_ENV: '"development"', NODE_ENV: '"development"',
VUE_APP_ENABLE_SPELLCHECK: 'true' VUE_APP_ENABLE_SPELLCHECK: 'true',
}); });

View File

@ -1,12 +1,11 @@
'use strict' 'use strict';
// Template version: 1.3.1 // Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation. // see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path') const path = require('path');
module.exports = { module.exports = {
dev: { dev: {
// Paths // Paths
assetsSubDirectory: 'static', assetsSubDirectory: 'static',
assetsPublicPath: '/', assetsPublicPath: '/',
@ -41,7 +40,7 @@ module.exports = {
// https://vue-loader.vuejs.org/en/options.html#cachebusting // https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true, cacheBusting: true,
cssSourceMap: true cssSourceMap: true,
}, },
build: { build: {
@ -71,6 +70,6 @@ module.exports = {
// View the bundle analyzer report after build finishes: // View the bundle analyzer report after build finishes:
// `npm run build --report` // `npm run build --report`
// Set to `true` or `false` to always turn it on or off // Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report bundleAnalyzerReport: process.env.npm_config_report,
} },
} };

View File

@ -1,4 +1,4 @@
'use strict' 'use strict';
module.exports = { module.exports = {
/* /*
* ENV variables used in JS code need to be stringyfied, as they will be replaced in the code, and JS needs quotes * ENV variables used in JS code need to be stringyfied, as they will be replaced in the code, and JS needs quotes
@ -12,6 +12,6 @@ module.exports = {
// vvvv HTML PROPERTIES FROM HERE, NOT STRINGIFIED vvvv // vvvv HTML PROPERTIES FROM HERE, NOT STRINGIFIED vvvv
VUE_APP_FAVICON_32: 'https://skillbox-my-detailhandel-dha-prod.s3.eu-central-1.amazonaws.com/myDHA-favicon.png', VUE_APP_FAVICON_32: 'https://skillbox-my-detailhandel-dha-prod.s3.eu-central-1.amazonaws.com/myDHA-favicon.png',
VUE_APP_FAVICON_16: 'https://skillbox-my-detailhandel-dha-prod.s3.eu-central-1.amazonaws.com/myDHA-favicon.png', VUE_APP_FAVICON_16: 'https://skillbox-my-detailhandel-dha-prod.s3.eu-central-1.amazonaws.com/myDHA-favicon.png',
VUE_APP_TITLE: 'myDHA' VUE_APP_TITLE: 'myDHA',
// ^^^^ HTML PROPERTIES TO HERE, NOT STRINGIFIED ^^^^ // ^^^^ HTML PROPERTIES TO HERE, NOT STRINGIFIED ^^^^
} };

View File

@ -1,4 +1,4 @@
'use strict' 'use strict';
module.exports = { module.exports = {
/* /*
* ENV variables used in JS code need to be stringyfied, as they will be replaced in the code, and JS needs quotes * ENV variables used in JS code need to be stringyfied, as they will be replaced in the code, and JS needs quotes
@ -12,6 +12,6 @@ module.exports = {
// vvvv HTML PROPERTIES FROM HERE, NOT STRINGIFIED vvvv // vvvv HTML PROPERTIES FROM HERE, NOT STRINGIFIED vvvv
VUE_APP_FAVICON_32: 'https://skillbox-my-detailhandel-dhf-prod.s3.eu-central-1.amazonaws.com/myDHF-favicon.png', VUE_APP_FAVICON_32: 'https://skillbox-my-detailhandel-dhf-prod.s3.eu-central-1.amazonaws.com/myDHF-favicon.png',
VUE_APP_FAVICON_16: 'https://skillbox-my-detailhandel-dhf-prod.s3.eu-central-1.amazonaws.com/myDHF-favicon.png', VUE_APP_FAVICON_16: 'https://skillbox-my-detailhandel-dhf-prod.s3.eu-central-1.amazonaws.com/myDHF-favicon.png',
VUE_APP_TITLE: 'myDHF' VUE_APP_TITLE: 'myDHF',
// ^^^^ HTML PROPERTIES TO HERE, NOT STRINGIFIED ^^^^ // ^^^^ HTML PROPERTIES TO HERE, NOT STRINGIFIED ^^^^
} };

View File

@ -1,4 +1,4 @@
'use strict' 'use strict';
module.exports = { module.exports = {
/* /*
* ENV variables used in JS code need to be stringyfied, as they will be replaced in the code, and JS needs quotes * ENV variables used in JS code need to be stringyfied, as they will be replaced in the code, and JS needs quotes
@ -12,6 +12,6 @@ module.exports = {
// vvvv HTML PROPERTIES FROM HERE, NOT STRINGIFIED vvvv // vvvv HTML PROPERTIES FROM HERE, NOT STRINGIFIED vvvv
VUE_APP_FAVICON_32: 'https://skillbox-my-kv-prod.s3-eu-west-1.amazonaws.com/mykv-favicon.png', VUE_APP_FAVICON_32: 'https://skillbox-my-kv-prod.s3-eu-west-1.amazonaws.com/mykv-favicon.png',
VUE_APP_FAVICON_16: 'https://skillbox-my-kv-prod.s3-eu-west-1.amazonaws.com/mykv-favicon.png', VUE_APP_FAVICON_16: 'https://skillbox-my-kv-prod.s3-eu-west-1.amazonaws.com/mykv-favicon.png',
VUE_APP_TITLE: 'myKV' VUE_APP_TITLE: 'myKV',
// ^^^^ HTML PROPERTIES TO HERE, NOT STRINGIFIED ^^^^ // ^^^^ HTML PROPERTIES TO HERE, NOT STRINGIFIED ^^^^
} };

View File

@ -1,5 +1,5 @@
'use strict' 'use strict';
const { merge } = require('webpack-merge') const { merge } = require('webpack-merge');
const values = { const values = {
NODE_ENV: '"production"', NODE_ENV: '"production"',
@ -21,9 +21,9 @@ const values = {
// vvvv HTML PROPERTIES FROM HERE, NOT STRINGIFIED vvvv // vvvv HTML PROPERTIES FROM HERE, NOT STRINGIFIED vvvv
VUE_APP_FAVICON_32: '/static/favicon-32x32.png', VUE_APP_FAVICON_32: '/static/favicon-32x32.png',
VUE_APP_FAVICON_16: '/static/favicon-16x16.png', VUE_APP_FAVICON_16: '/static/favicon-16x16.png',
VUE_APP_TITLE: 'mySkillbox' VUE_APP_TITLE: 'mySkillbox',
// ^^^^ HTML PROPERTIES TO HERE, NOT STRINGIFIED ^^^^ // ^^^^ HTML PROPERTIES TO HERE, NOT STRINGIFIED ^^^^
} };
switch (process.env.APP_FLAVOR) { switch (process.env.APP_FLAVOR) {
case 'my-kv': case 'my-kv':
@ -39,4 +39,3 @@ switch (process.env.APP_FLAVOR) {
// we are on the skillbox APP_FLAVOR // we are on the skillbox APP_FLAVOR
module.exports = values; module.exports = values;
} }

View File

@ -1,27 +1,24 @@
import { defineConfig } from 'cypress'; import { defineConfig } from 'cypress';
import {readFileSync} from "fs"; import { readFileSync } from 'fs';
import {resolve} from "path"; import { resolve } from 'path';
export default defineConfig({ export default defineConfig({
e2e: { e2e: {
"baseUrl": "http://localhost:8000", baseUrl: 'http://localhost:8000',
specPattern: 'cypress/e2e/e2e/**/*.{spec,cy}.{js,ts}', specPattern: 'cypress/e2e/e2e/**/*.{spec,cy}.{js,ts}',
supportFile: 'cypress/support/e2e.ts', supportFile: 'cypress/support/e2e.ts',
setupNodeEvents(on, config) { setupNodeEvents(on, config) {
on('task', { on('task', {
getSchema() { getSchema() {
return readFileSync( return readFileSync(resolve(__dirname, '../server/schema.graphql'), 'utf8');
resolve(__dirname, '../server/schema.graphql'), },
'utf8'
);
}
}); });
}, },
}, },
"videoUploadOnPasses": false, videoUploadOnPasses: false,
"reporter": "junit", reporter: 'junit',
"reporterOptions": { reporterOptions: {
"mochaFile": "cypress/test-reports/e2e/cypress-results-[hash].xml", mochaFile: 'cypress/test-reports/e2e/cypress-results-[hash].xml',
"toConsole": true toConsole: true,
}, },
"projectId": "msk-ee", projectId: 'msk-ee',
}); });

View File

@ -1,34 +1,29 @@
import { defineConfig } from 'cypress'; import { defineConfig } from 'cypress';
import {readFileSync} from "fs"; import { readFileSync } from 'fs';
import {resolve} from "path"; import { resolve } from 'path';
export default defineConfig({ export default defineConfig({
chromeWebSecurity: false, chromeWebSecurity: false,
e2e: { e2e: {
baseUrl: "http://localhost:8080", baseUrl: 'http://localhost:8080',
specPattern: 'cypress/e2e/frontend/**/*.{cy,spec}.{js,ts}', specPattern: 'cypress/e2e/frontend/**/*.{cy,spec}.{js,ts}',
supportFile: 'cypress/support/e2e.ts', supportFile: 'cypress/support/e2e.ts',
setupNodeEvents(on, config) { setupNodeEvents(on, config) {
on('task', { on('task', {
getSchema() { getSchema() {
return readFileSync( return readFileSync(resolve(__dirname, '../server/schema.graphql'), 'utf8');
resolve(__dirname, '../server/schema.graphql'), },
'utf8'
);
}
}); });
}, },
}, },
videoUploadOnPasses: false, videoUploadOnPasses: false,
reporter: "junit", reporter: 'junit',
reporterOptions: { reporterOptions: {
mochaFile: "cypress/test-reports/frontend/cypress-results-[hash].xml", mochaFile: 'cypress/test-reports/frontend/cypress-results-[hash].xml',
toConsole: true toConsole: true,
}, },
"projectId": "msk-fe", projectId: 'msk-fe',
retries: { retries: {
runMode: 3 runMode: 3,
} },
}); });

View File

@ -1,21 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
<title><%= htmlWebpackPlugin.options.VUE_APP_TITLE %></title> <title><%= htmlWebpackPlugin.options.VUE_APP_TITLE %></title>
<link href='https://fonts.googleapis.com/css?family=Material+Icons' rel="stylesheet" type="text/css"> <link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,600,800" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Montserrat:400,600,800" rel="stylesheet" />
<link href="https://use.typekit.net/tck7ptw.css" rel="stylesheet"> <link href="https://use.typekit.net/tck7ptw.css" rel="stylesheet" />
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="<%- htmlWebpackPlugin.options.VUE_APP_FAVICON_32 %>"> <link rel="icon" type="image/png" sizes="32x32" href="<%- htmlWebpackPlugin.options.VUE_APP_FAVICON_32 %>" />
<link rel="icon" type="image/png" sizes="16x16" href="<%- htmlWebpackPlugin.options.VUE_APP_FAVICON_16 %>"> <link rel="icon" type="image/png" sizes="16x16" href="<%- htmlWebpackPlugin.options.VUE_APP_FAVICON_16 %>" />
<link rel="manifest" href="/static/site.webmanifest"> <link rel="manifest" href="/static/site.webmanifest" />
<link rel="mask-icon" href="/static/safari-pinned-tab.svg" color="#5bbad5"> <link rel="mask-icon" href="/static/safari-pinned-tab.svg" color="#5bbad5" />
<meta name="msapplication-TileColor" content="#da532c"> <meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff" />
<script> <script>
window.UPLOADCARE_PUBLIC_KEY = '78212ff39934a59775ac'; window.UPLOADCARE_PUBLIC_KEY = '78212ff39934a59775ac';
@ -26,17 +26,15 @@
file: { file: {
drag: 'Ziehen Sie ein Bild hier hinein', drag: 'Ziehen Sie ein Bild hier hinein',
button: 'Wählen Sie ein lokales Bild', button: 'Wählen Sie ein lokales Bild',
} },
} },
} },
}; };
</script> </script>
</head> </head>
<body> <body>
<div id="app"> <div id="app">
<div class="center"> <div class="center"></div>
</div>
</div> </div>
<!-- built files will be auto injected --> <!-- built files will be auto injected -->
</body> </body>

View File

@ -1,11 +1,5 @@
module.exports = { module.exports = {
moduleFileExtensions: [ moduleFileExtensions: ['js', 'jsx', 'ts', 'json', 'vue'],
'js',
'jsx',
'ts',
'json',
'vue',
],
transform: { transform: {
'\\.(gql|graphql)$': 'jest-transform-graphql', '\\.(gql|graphql)$': 'jest-transform-graphql',
'^.+\\.js$': 'babel-jest', '^.+\\.js$': 'babel-jest',
@ -13,27 +7,15 @@ module.exports = {
'^.+\\.vue$': '@vue/vue2-jest', '^.+\\.vue$': '@vue/vue2-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
}, },
modulePaths: [ modulePaths: ['<rootDir>/src', '<rootDir>/node_modules'],
'<rootDir>/src', transformIgnorePatterns: ['/node_modules/'],
'<rootDir>/node_modules',
],
transformIgnorePatterns: [
'/node_modules/',
],
moduleNameMapper: { moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', '^@/(.*)$': '<rootDir>/src/$1',
'^gql/(.*)$': '<rootDir>/src/graphql/gql/$1', '^gql/(.*)$': '<rootDir>/src/graphql/gql/$1',
}, },
snapshotSerializers: [ snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
'<rootDir>/node_modules/jest-serializer-vue',
],
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
testMatch: [ testMatch: ['**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'],
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)',
],
testURL: 'http://localhost/', testURL: 'http://localhost/',
watchPlugins: [ watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
}; };

View File

@ -4,16 +4,13 @@ const { addMocksToSchema} = require('@graphql-tools/mock');
const { makeExecutableSchema } = require('@graphql-tools/schema'); const { makeExecutableSchema } = require('@graphql-tools/schema');
const { graphql } = require('graphql'); const { graphql } = require('graphql');
const schemaString = readFileSync( const schemaString = readFileSync(resolve(__dirname, '../server/schema.graphql'), 'utf8');
resolve(__dirname,'../server/schema.graphql'),
'utf8'
);
// Make a GraphQL schema with no resolvers // Make a GraphQL schema with no resolvers
const schema = makeExecutableSchema({ typeDefs: schemaString }) const schema = makeExecutableSchema({ typeDefs: schemaString });
// Create a new schema with mocks // Create a new schema with mocks
const schemaWithMocks = addMocksToSchema({ schema }) const schemaWithMocks = addMocksToSchema({ schema });
const query = /* GraphQL */ ` const query = /* GraphQL */ `
query MeQuery { query MeQuery {
@ -21,9 +18,9 @@ const query = /* GraphQL */ `
firstName firstName
} }
} }
` `;
graphql({ graphql({
schema: schemaWithMocks, schema: schemaWithMocks,
source: query, source: query,
}).then(result => console.log('Got result', result)) }).then((result) => console.log('Got result', result));

View File

@ -1,12 +1,12 @@
declare module '*.graphql' { declare module '*.graphql' {
import {DocumentNode} from "graphql"; import { DocumentNode } from 'graphql';
const Schema: DocumentNode; const Schema: DocumentNode;
export = Schema; export = Schema;
} }
declare module '*.gql' { declare module '*.gql' {
import {DocumentNode} from "graphql"; import { DocumentNode } from 'graphql';
const content: DocumentNode; const content: DocumentNode;
export default content; export default content;
} }

View File

@ -11,7 +11,7 @@ export interface ContentBlock {
} }
export interface ActionOptions { export interface ActionOptions {
up?: boolean, up?: boolean;
down?: boolean, down?: boolean;
extended?: boolean extended?: boolean;
} }

View File

@ -1,19 +1,9 @@
<template> <template>
<div <div :class="{ 'no-scroll': showModal || showMobileNavigation }" class="app" id="app">
:class="{'no-scroll': showModal || showMobileNavigation}"
class="app"
id="app"
>
<read-only-banner /> <read-only-banner />
<scroll-up /> <scroll-up />
<component <component :is="showModalDeprecated" v-if="showModalDeprecated" />
:is="showModalDeprecated" <component :is="showModal" v-if="showModal" />
v-if="showModalDeprecated"
/>
<component
:is="showModal"
v-if="showModal"
/>
<component :is="layout" /> <component :is="layout" />
</div> </div>
</template> </template>
@ -25,17 +15,26 @@
import modals from '@/components/modals'; import modals from '@/components/modals';
const NewContentBlockWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/content-block-form/NewContentBlockWizard'); const NewContentBlockWizard = () =>
const EditContentBlockWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/content-block-form/EditContentBlockWizard'); import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/NewContentBlockWizard');
const EditRoomEntryWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/rooms/room-entries/EditRoomEntryWizard'); const EditContentBlockWizard = () =>
const NewProjectEntryWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/portfolio/NewProjectEntryWizard'); import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/EditContentBlockWizard');
const EditProjectEntryWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/portfolio/EditProjectEntryWizard'); const EditRoomEntryWizard = () =>
const NewObjectiveWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/objective-groups/NewObjectiveWizard'); import(/* webpackChunkName: "content-forms" */ '@/components/rooms/room-entries/EditRoomEntryWizard');
const NewProjectEntryWizard = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/NewProjectEntryWizard');
const EditProjectEntryWizard = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/EditProjectEntryWizard');
const NewObjectiveWizard = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/objective-groups/NewObjectiveWizard');
const NewNoteWizard = () => import(/* webpackChunkName: "content-forms" */ '@/components/notes/NewNoteWizard'); const NewNoteWizard = () => import(/* webpackChunkName: "content-forms" */ '@/components/notes/NewNoteWizard');
const EditNoteWizard = () => import(/* webpackChunkName: "content-forms" */ '@/components/notes/EditNoteWizard'); const EditNoteWizard = () => import(/* webpackChunkName: "content-forms" */ '@/components/notes/EditNoteWizard');
const EditClassNameWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/school-class/EditClassNameWizard'); const EditClassNameWizard = () =>
const EditTeamNameWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/profile/EditTeamNameWizard'); import(/* webpackChunkName: "content-forms" */ '@/components/school-class/EditClassNameWizard');
const EditSnapshotTitleWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/snapshots/EditSnapshotTitleWizard'); const EditTeamNameWizard = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/profile/EditTeamNameWizard');
const EditSnapshotTitleWizard = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/snapshots/EditSnapshotTitleWizard');
const DefaultLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/DefaultLayout'); const DefaultLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/DefaultLayout');
const SimpleLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/SimpleLayout'); const SimpleLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/SimpleLayout');
const FullScreenLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/FullScreenLayout'); const FullScreenLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/FullScreenLayout');
@ -66,7 +65,7 @@
EditClassNameWizard, EditClassNameWizard,
EditTeamNameWizard, EditTeamNameWizard,
EditSnapshotTitleWizard, EditSnapshotTitleWizard,
...modals ...modals,
}, },
computed: { computed: {
@ -85,8 +84,8 @@
</script> </script>
<style lang="scss"> <style lang="scss">
@import "~styles/main.scss"; @import '~styles/main.scss';
@import "~styles/helpers"; @import '~styles/helpers';
body { body {
overflow-y: auto; overflow-y: auto;
@ -105,5 +104,4 @@
.no-scroll { .no-scroll {
overflow-y: hidden; overflow-y: hidden;
} }
</style> </style>

View File

@ -1,19 +1,13 @@
<template> <template>
<div class="add-content"> <div class="add-content">
<a <a class="add-content__button" @click="addContent">
class="add-content__button"
@click="addContent"
>
<add-pointer class="add-content__icon" /> <add-pointer class="add-content__icon" />
</a> </a>
</div> </div>
</template> </template>
<script> <script>
import { import { CREATE_CONTENT_BLOCK_AFTER_PAGE, CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE } from '@/router/module.names';
CREATE_CONTENT_BLOCK_AFTER_PAGE,
CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE,
} from '@/router/module.names';
const AddPointer = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddPointer'); const AddPointer = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddPointer');
@ -22,14 +16,15 @@
where: { where: {
type: Object, type: Object,
validator(prop) { validator(prop) {
return Object.prototype.hasOwnProperty.call(prop, 'after' ) return (
|| Object.prototype.hasOwnProperty.call(prop, 'parent'); Object.prototype.hasOwnProperty.call(prop, 'after') || Object.prototype.hasOwnProperty.call(prop, 'parent')
} );
},
}, },
}, },
components: { components: {
AddPointer AddPointer,
}, },
computed: { computed: {
@ -44,9 +39,8 @@
}, },
slug() { slug() {
return this.$route.params.slug; return this.$route.params.slug;
}
}, },
},
methods: { methods: {
addContent() { addContent() {
@ -59,26 +53,26 @@
name: CREATE_CONTENT_BLOCK_AFTER_PAGE, name: CREATE_CONTENT_BLOCK_AFTER_PAGE,
params: { params: {
after: this.after.id, after: this.after.id,
slug: this.slug slug: this.slug,
} },
}; };
} else { } else {
route = { route = {
name: CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE, name: CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE,
params: { params: {
parent: this.parent.id parent: this.parent.id,
} },
}; };
} }
this.$router.push(route); this.$router.push(route);
} }
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.add-content { .add-content {
display: none; display: none;

View File

@ -1,8 +1,5 @@
<template> <template>
<div <div class="add-content-element" @click="$emit('add-element', index)">
class="add-content-element"
@click="$emit('add-element', index)"
>
<add-icon class="add-content-element__icon" /> <add-icon class="add-content-element__icon" />
</div> </div>
</template> </template>
@ -14,13 +11,13 @@
props: ['index'], props: ['index'],
components: { components: {
AddIcon AddIcon,
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
.add-content-element { .add-content-element {
display: flex; display: flex;

View File

@ -17,20 +17,21 @@
props: { props: {
route: { route: {
type: String, type: String,
default: null default: null,
}, },
reverse: { // use reverse colors reverse: {
// use reverse colors
type: Boolean, type: Boolean,
default: false default: false,
}, },
click: { click: {
type: Function, type: Function,
default: null default: null,
} },
}, },
components: { components: {
AddIcon AddIcon,
}, },
computed: { computed: {
@ -39,17 +40,19 @@
return this.route ? 'router-link' : 'a'; return this.route ? 'router-link' : 'a';
}, },
properties() { properties() {
return this.route ? { return this.route
? {
to: this.route, to: this.route,
tag: 'div' tag: 'div',
} : {};
} }
: {};
},
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.add-widget { .add-widget {
display: none; display: none;

View File

@ -1,53 +1,31 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div class="assignment-with-submissions"> <div class="assignment-with-submissions">
<p <p class="assignment-with-submissions__text" data-cy="assignment-main-text" v-html="assignment.assignment" />
class="assignment-with-submissions__text"
data-cy="assignment-main-text"
v-html="assignment.assignment"
/>
<div> <div>
<a <a class="button button--primary submissions-page__back" @click="$emit('back')"
class="button button--primary submissions-page__back" >Aufgabe im {{ $flavor.textModule }} anzeigen</a
@click="$emit('back')" >
>Aufgabe im {{ $flavor.textModule }} anzeigen</a>
</div> </div>
<div <div class="assignment-with-submissions__solution" v-if="assignment.solution">
class="assignment-with-submissions__solution" <h4 class="assignment-with-submissions__heading">Lösung</h4>
v-if="assignment.solution"
>
<h4 class="assignment-with-submissions__heading">
Lösung
</h4>
<p <p
class="assignment-with-submissions__solution-text" class="assignment-with-submissions__solution-text"
data-cy="assignment-solution" data-cy="assignment-solution"
v-html="assignment.solution" v-html="assignment.solution"
/> />
</div> </div>
<p <p class="assignment-with-submissions__no-submissions" v-if="!assignment.submissions.length">
class="assignment-with-submissions__no-submissions"
v-if="!assignment.submissions.length"
>
Zu diesem Auftrag sind noch keine Ergebnisse vorhanden. Zu diesem Auftrag sind noch keine Ergebnisse vorhanden.
</p> </p>
<div <div class="assignment-with-submissions__submissions submissions" v-if="assignment.submissions.length">
class="assignment-with-submissions__submissions submissions"
v-if="assignment.submissions.length"
>
<div class="submissions__header student-submission-row submission-header"> <div class="submissions__header student-submission-row submission-header">
<p class="submission-header__title"> <p class="submission-header__title">Lernende</p>
Lernende <p class="submission-header__title">Ergebnisse</p>
</p> <p class="submission-header__title">Feedback</p>
<p class="submission-header__title">
Ergebnisse
</p>
<p class="submission-header__title">
Feedback
</p>
</div> </div>
<router-link <router-link
:to="submissionLink(submission)" :to="submissionLink(submission)"
@ -55,10 +33,7 @@
v-for="submission in submissions" v-for="submission in submissions"
:key="submission.id" :key="submission.id"
> >
<student-submission <student-submission :submission="submission" class="assignment-with-submissions__submission" />
:submission="submission"
class="assignment-with-submissions__submission"
/>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -73,18 +48,18 @@
props: ['assignment'], props: ['assignment'],
components: { components: {
StudentSubmission StudentSubmission,
}, },
data() { data() {
return { return {
me: {} me: {},
}; };
}, },
computed: { computed: {
submissions() { submissions() {
return this.assignment.submissions.filter(submission => { return this.assignment.submissions.filter((submission) => {
return this.belongsToSchool(submission); return this.belongsToSchool(submission);
}); });
}, },
@ -101,19 +76,18 @@
if (this.currentFilter.id === '') { if (this.currentFilter.id === '') {
return true; return true;
} }
return submission.student.schoolClasses.some(schoolClass => schoolClass .id === this.currentFilter.id); return submission.student.schoolClasses.some((schoolClass) => schoolClass.id === this.currentFilter.id);
} },
}, },
apollo: { apollo: {
me: meQuery me: meQuery,
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.assignment-with-submissions { .assignment-with-submissions {
&__title { &__title {
@ -153,7 +127,6 @@
:deep(li) { :deep(li) {
@include list-child; @include list-child;
} }
} }
.submissions { .submissions {
@ -166,5 +139,4 @@
font-family: $sans-serif-font-family; font-family: $sans-serif-font-family;
} }
} }
</style> </style>

View File

@ -1,9 +1,5 @@
<template> <template>
<router-link <router-link :to="to" data-cy="back-link" class="sub-navigation-item back-link">
:to="to"
data-cy="back-link"
class="sub-navigation-item back-link"
>
<chevron-left class="back-link__icon sub-navigation-item__icon" /> <chevron-left class="back-link__icon sub-navigation-item__icon" />
{{ fullTitle }} {{ fullTitle }}
</router-link> </router-link>

View File

@ -1,26 +1,12 @@
<template> <template>
<div <div :data-scrollto="chapter.id" class="chapter" data-cy="chapter">
:data-scrollto="chapter.id" <div :class="{ 'hideable-element--greyed-out': titleGreyedOut }" class="hideable-element" v-if="!titleHidden">
class="chapter" <h3 :id="'chapter-' + index">
data-cy="chapter"
>
<div
:class="{'hideable-element--greyed-out': titleGreyedOut}"
class="hideable-element"
v-if="!titleHidden"
>
<h3
:id="'chapter-' + index"
>
{{ chapter.title }} {{ chapter.title }}
</h3> </h3>
</div> </div>
<visibility-action <visibility-action :block="chapter" type="chapter-title" v-if="editMode" />
:block="chapter"
type="chapter-title"
v-if="editMode"
/>
<bookmark-actions <bookmark-actions
:bookmarked="!!chapter.bookmark" :bookmarked="!!chapter.bookmark"
@ -37,23 +23,13 @@
class="chapter__intro intro hideable-element" class="chapter__intro intro hideable-element"
v-if="!descriptionHidden" v-if="!descriptionHidden"
> >
<visibility-action <visibility-action :block="chapter" :chapter="true" type="chapter-description" v-if="editMode" />
:block="chapter" <p class="chapter__description">
:chapter="true"
type="chapter-description"
v-if="editMode"
/>
<p
class="chapter__description"
>
{{ chapter.description }} {{ chapter.description }}
</p> </p>
</div> </div>
<add-content-button <add-content-button :where="{ parent: chapter }" v-if="editMode" />
:where="{parent: chapter}"
v-if="editMode"
/>
<content-block <content-block
:content-block="contentBlock" :content-block="contentBlock"
@ -83,16 +59,16 @@
props: { props: {
chapter: { chapter: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
index: { index: {
type: Number, type: Number,
default: 0 default: 0,
}, },
editMode: { editMode: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
mixins: [me], mixins: [me],
@ -112,11 +88,14 @@
if (this.editMode) { if (this.editMode) {
return this.chapter.contentBlocks; return this.chapter.contentBlocks;
} }
return this.chapter.contentBlocks.filter(contentBlock => !hidden({ return this.chapter.contentBlocks.filter(
(contentBlock) =>
!hidden({
block: contentBlock, block: contentBlock,
schoolClass: this.schoolClass, schoolClass: this.schoolClass,
type: CONTENT_TYPE, type: CONTENT_TYPE,
})); })
);
}, },
note() { note() {
if (this.chapter && this.chapter.bookmark) { if (this.chapter && this.chapter.bookmark) {
@ -179,8 +158,8 @@
const data = { const data = {
chapter: { chapter: {
...chapter, ...chapter,
bookmark bookmark,
} },
}; };
store.writeQuery({ store.writeQuery({
@ -215,12 +194,11 @@
}); });
}, },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.chapter { .chapter {
position: relative; position: relative;

View File

@ -8,14 +8,8 @@
:key="index" :key="index"
@click="$emit('input', color.name)" @click="$emit('input', color.name)"
> >
<div <div :class="'color-chooser__color--' + color.name" class="color-chooser__color">
:class="'color-chooser__color--' + color.name" <tick class="color-chooser__selected-icon" v-if="selectedColor === color.name" />
class="color-chooser__color"
>
<tick
class="color-chooser__selected-icon"
v-if="selectedColor === color.name"
/>
</div> </div>
</div> </div>
</div> </div>
@ -28,33 +22,33 @@
props: ['selectedColor'], props: ['selectedColor'],
components: { components: {
Tick Tick,
}, },
data() { data() {
return { return {
colors: [ colors: [
{ {
name: 'yellow' name: 'yellow',
}, },
{ {
name: 'blue' name: 'blue',
}, },
{ {
name: 'red' name: 'red',
}, },
{ {
name: 'green' name: 'green',
} },
] ],
}; };
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
.color-chooser { .color-chooser {
display: flex; display: flex;
@ -81,7 +75,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
@supports (display: grid) { @supports (display: grid) {
display: grid display: grid;
} }
justify-items: center; justify-items: center;
align-items: center; align-items: center;

View File

@ -3,36 +3,18 @@
:class="{ 'hideable-element--greyed-out': hidden }" :class="{ 'hideable-element--greyed-out': hidden }"
class="content-block__container hideable-element content-list__parent" class="content-block__container hideable-element content-list__parent"
> >
<div <div :class="specialClass" :style="instrumentStyle" class="content-block" data-cy="content-block">
:class="specialClass" <div class="block-actions" v-if="canEditModule && !isInstrumentBlock">
:style="instrumentStyle" <user-widget v-bind="me" class="block-actions__user-widget content-block__user-widget" v-if="isMine" />
class="content-block"
data-cy="content-block"
>
<div
class="block-actions"
v-if="canEditModule && !isInstrumentBlock"
>
<user-widget
v-bind="me"
class="block-actions__user-widget content-block__user-widget"
v-if="isMine"
/>
<more-options-widget> <more-options-widget>
<li <li class="popover-links__link" v-if="!isInstrumentBlock">
class="popover-links__link"
v-if="!isInstrumentBlock"
>
<popover-link <popover-link
data-cy="duplicate-content-block-link" data-cy="duplicate-content-block-link"
text="Duplizieren" text="Duplizieren"
@link-action="duplicateContentBlock(contentBlock)" @link-action="duplicateContentBlock(contentBlock)"
/> />
</li> </li>
<li <li class="popover-links__link" v-if="isMine">
class="popover-links__link"
v-if="isMine"
>
<popover-link <popover-link
data-cy="delete-content-block-link" data-cy="delete-content-block-link"
text="Löschen" text="Löschen"
@ -40,22 +22,13 @@
/> />
</li> </li>
<li <li class="popover-links__link" v-if="isMine">
class="popover-links__link" <popover-link text="Bearbeiten" @link-action="editContentBlock(contentBlock)" />
v-if="isMine"
>
<popover-link
text="Bearbeiten"
@link-action="editContentBlock(contentBlock)"
/>
</li> </li>
</more-options-widget> </more-options-widget>
</div> </div>
<div class="content-block__visibility"> <div class="content-block__visibility">
<visibility-action <visibility-action :block="contentBlock" v-if="canEditModule" />
:block="contentBlock"
v-if="canEditModule"
/>
</div> </div>
<h3 <h3
@ -66,10 +39,7 @@
> >
{{ instrumentLabel }} {{ instrumentLabel }}
</h3> </h3>
<h4 <h4 class="content-block__title" v-if="!contentBlock.indent">
class="content-block__title"
v-if="!contentBlock.indent"
>
{{ contentBlock.title }} {{ contentBlock.title }}
</h4> </h4>
@ -85,10 +55,7 @@
/> />
</div> </div>
<add-content-button <add-content-button :where="{ after: contentBlock }" v-if="canEditModule" />
:where="{after: contentBlock}"
v-if="canEditModule"
/>
</div> </div>
</template> </template>
@ -111,8 +78,8 @@
import { EDIT_CONTENT_BLOCK_PAGE } from '@/router/module.names'; import { EDIT_CONTENT_BLOCK_PAGE } from '@/router/module.names';
import { instrumentCategory } from '@/helpers/instrumentType'; import { instrumentCategory } from '@/helpers/instrumentType';
const ContentComponent = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ContentComponent'); const ContentComponent = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ContentComponent');
export default { export default {
name: 'ContentBlock', name: 'ContentBlock',
@ -156,14 +123,15 @@
instrumentStyle() { instrumentStyle() {
if (this.isInstrumentBlock) { if (this.isInstrumentBlock) {
return { return {
backgroundColor: this.contentBlock.instrumentCategory.background backgroundColor: this.contentBlock.instrumentCategory.background,
}; };
} }
return {}; return {};
}, },
instrumentLabel() { instrumentLabel() {
const contentType = this.contentBlock.type.toLowerCase(); const contentType = this.contentBlock.type.toLowerCase();
if (contentType.startsWith('base')) { // all legacy instruments start with `base` if (contentType.startsWith('base')) {
// all legacy instruments start with `base`
return instrumentCategory(contentType); return instrumentCategory(contentType);
} }
if (this.isInstrumentBlock) { if (this.isInstrumentBlock) {
@ -175,7 +143,7 @@
instrumentLabelStyle() { instrumentLabelStyle() {
if (this.isInstrumentBlock) { if (this.isInstrumentBlock) {
return { return {
color: this.contentBlock.instrumentCategory.foreground color: this.contentBlock.instrumentCategory.foreground,
}; };
} }
return {}; return {};
@ -206,7 +174,8 @@
// collect content_list_items // collect content_list_items
if (content.type === 'content_list_item') { if (content.type === 'content_list_item') {
contentList = [...contentList, content]; contentList = [...contentList, content];
if (index === this.contentBlock.contents.length - 1) { // content is last element of contents array if (index === this.contentBlock.contents.length - 1) {
// content is last element of contents array
let updatedContent = [...newContents, ...this.createContentListOrBlocks(contentList)]; let updatedContent = [...newContents, ...this.createContentListOrBlocks(contentList)];
return updatedContent; return updatedContent;
} }
@ -248,14 +217,21 @@
id, id,
}, },
}, },
update(store, {data: {duplicateContentBlock: {contentBlock}}}) { update(
store,
{
data: {
duplicateContentBlock: { contentBlock },
},
}
) {
if (contentBlock) { if (contentBlock) {
const query = CHAPTER_QUERY; const query = CHAPTER_QUERY;
const variables = { const variables = {
id: parent.id, id: parent.id,
}; };
const { chapter } = store.readQuery({ query, variables }); const { chapter } = store.readQuery({ query, variables });
const index = chapter.contentBlocks.findIndex(contentBlock => contentBlock.id === id); const index = chapter.contentBlocks.findIndex((contentBlock) => contentBlock.id === id);
const contentBlocks = insertAtIndex(chapter.contentBlocks, index, contentBlock); const contentBlocks = insertAtIndex(chapter.contentBlocks, index, contentBlock);
const data = { const data = {
chapter: { chapter: {
@ -267,7 +243,6 @@
} }
}, },
}); });
}, },
editContentBlock(contentBlock) { editContentBlock(contentBlock) {
const route = { const route = {
@ -279,7 +254,9 @@
this.$router.push(route); this.$router.push(route);
}, },
deleteContentBlock(contentBlock) { deleteContentBlock(contentBlock) {
this.$modal.open('confirm').then(() => { this.$modal
.open('confirm')
.then(() => {
this.doDeleteContentBlock(contentBlock); this.doDeleteContentBlock(contentBlock);
}) })
.catch(); .catch();
@ -294,14 +271,21 @@
id, id,
}, },
}, },
update(store, {data: {deleteContentBlock: {success}}}) { update(
store,
{
data: {
deleteContentBlock: { success },
},
}
) {
if (success) { if (success) {
const query = CHAPTER_QUERY; const query = CHAPTER_QUERY;
const variables = { const variables = {
id: parent.id, id: parent.id,
}; };
const { chapter } = store.readQuery({ query, variables }); const { chapter } = store.readQuery({ query, variables });
const index = chapter.contentBlocks.findIndex(contentBlock => contentBlock.id === id); const index = chapter.contentBlocks.findIndex((contentBlock) => contentBlock.id === id);
const contentBlocks = removeAtIndex(chapter.contentBlocks, index); const contentBlocks = removeAtIndex(chapter.contentBlocks, index);
const data = { const data = {
chapter: { chapter: {
@ -315,18 +299,20 @@
}); });
}, },
createContentListOrBlocks(contentList) { createContentListOrBlocks(contentList) {
return [{ return [
{
type: 'content_list', type: 'content_list',
contents: contentList, contents: contentList,
id: contentList[0].id, id: contentList[0].id,
}]; },
];
}, },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.content-block { .content-block {
margin-bottom: $section-spacing; margin-bottom: $section-spacing;
@ -416,6 +402,5 @@
line-height: 1.5; line-height: 1.5;
} }
} }
} }
</style> </style>

View File

@ -1,13 +1,6 @@
<template> <template>
<modal <modal :hide-header="true" :fullscreen="true" class="fullscreen-image">
:hide-header="true" <img :src="imageUrl" class="fullscreen-image__image" />
:fullscreen="true"
class="fullscreen-image"
>
<img
:src="imageUrl"
class="fullscreen-image__image"
>
</modal> </modal>
</template> </template>
@ -16,14 +9,14 @@
export default { export default {
components: { components: {
Modal Modal,
}, },
computed: { computed: {
imageUrl() { imageUrl() {
return this.$store.state.imageUrl; return this.$store.state.imageUrl;
} },
} },
}; };
</script> </script>

View File

@ -1,22 +1,21 @@
<template> <template>
<modal :fullscreen="true"> <modal :fullscreen="true">
<component <component :value="value" :is="type" />
:value="value"
:is="type"
/>
</modal> </modal>
</template> </template>
<script> <script>
import Modal from '@/components/Modal'; import Modal from '@/components/Modal';
const InfogramBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/InfogramBlock'); const InfogramBlock = () =>
const GeniallyBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/GeniallyBlock'); import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/InfogramBlock');
const GeniallyBlock = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/GeniallyBlock');
export default { export default {
components: { components: {
Modal, Modal,
InfogramBlock, InfogramBlock,
GeniallyBlock GeniallyBlock,
}, },
computed: { computed: {
@ -28,9 +27,9 @@
}, },
value() { value() {
return { return {
id: this.id id: this.id,
}; };
} },
} },
}; };
</script> </script>

View File

@ -1,9 +1,5 @@
<template> <template>
<modal <modal :hide-header="true" :fullscreen="true" class="fullscreen-video">
:hide-header="true"
:fullscreen="true"
class="fullscreen-video"
>
<iframe <iframe
:src="src" :src="src"
width="2000" width="2000"
@ -22,7 +18,7 @@
export default { export default {
components: { components: {
Modal Modal,
}, },
computed: { computed: {
@ -31,8 +27,8 @@
}, },
src() { src() {
return `https://player.vimeo.com/video/${this.vimeoId}`; return `https://player.vimeo.com/video/${this.vimeoId}`;
} },
} },
}; };
</script> </script>

View File

@ -1,28 +1,15 @@
<template> <template>
<header class="header-bar"> <header class="header-bar">
<a <a class="header-bar__sidebar-link" data-cy="open-sidebar-link" @click.stop="openSidebar('navigation')">
class="header-bar__sidebar-link"
data-cy="open-sidebar-link"
@click.stop="openSidebar('navigation')"
>
<hamburger class="header-bar__sidebar-icon" /> <hamburger class="header-bar__sidebar-icon" />
</a> </a>
<content-navigation class="header-bar__content-navigation" /> <content-navigation class="header-bar__content-navigation" />
<div class="user-header"> <div class="user-header">
<a <a class="user-header__sidebar-link">
class="user-header__sidebar-link" <current-class class="user-header__current-class" @click.native.stop="openSidebar('profile')" />
>
<current-class
class="user-header__current-class"
@click.native.stop="openSidebar('profile')"
/>
</a> </a>
<user-widget <user-widget v-bind="me" data-cy="header-user-widget" @click.native.stop="openSidebar('profile')" />
v-bind="me"
data-cy="header-user-widget"
@click.native.stop="openSidebar('profile')"
/>
</div> </div>
</header> </header>
</template> </template>
@ -50,7 +37,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.header-bar { .header-bar {
display: flex; display: flex;

View File

@ -16,14 +16,14 @@
props: ['text'], props: ['text'],
components: { components: {
InfoIcon InfoIcon,
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
.helpful-tooltip { .helpful-tooltip {
position: relative; position: relative;
@ -65,7 +65,6 @@
height: 10px; height: 10px;
transform: rotate(-45deg) translateY(-50%); transform: rotate(-45deg) translateY(-50%);
} }
} }
&:hover &__tooltip { &:hover &__tooltip {

View File

@ -1,15 +1,9 @@
<template> <template>
<button <button :disabled="loading || disabled" class="loading-button button button--primary button--big">
:disabled="loading || disabled"
class="loading-button button button--primary button--big"
>
<template v-if="!loading"> <template v-if="!loading">
{{ label }} {{ label }}
</template> </template>
<loading-icon <loading-icon class="loading-button__icon" v-else />
class="loading-button__icon"
v-else
/>
</button> </button>
</template> </template>
@ -20,25 +14,25 @@
props: { props: {
loading: { loading: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
label: { label: {
type: String, type: String,
default: '' default: '',
} },
}, },
components: { components: {
LoadingIcon LoadingIcon,
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.loading-button { .loading-button {
height: 52px; height: 52px;

View File

@ -1,10 +1,6 @@
<template> <template>
<div class="logout-widget"> <div class="logout-widget">
<a <a class="logout-widget__logout" data-cy="logout" @click="logout()">Abmelden</a>
class="logout-widget__logout"
data-cy="logout"
@click="logout()"
>Abmelden</a>
</div> </div>
</template> </template>
@ -14,19 +10,23 @@
export default { export default {
methods: { methods: {
logout() { logout() {
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: LOGOUT_MUTATION, mutation: LOGOUT_MUTATION,
}).then(({data}) => { })
if (data.logout.success) { location.replace('/logout'); } .then(({ data }) => {
if (data.logout.success) {
location.replace('/logout');
}
}); });
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
.logout-widget { .logout-widget {
display: flex; display: flex;

View File

@ -4,17 +4,11 @@
<hamburger class="mobile-header__hamburger" /> <hamburger class="mobile-header__hamburger" />
</a> </a>
<router-link <router-link to="/" data-cy="mobile-home-link">
to="/"
data-cy="mobile-home-link"
>
<logo /> <logo />
</router-link> </router-link>
<user-widget <user-widget v-bind="me" @click.native.stop="openSidebar('profile')" />
v-bind="me"
@click.native.stop="openSidebar('profile')"
/>
</div> </div>
</template> </template>
@ -45,7 +39,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.mobile-header { .mobile-header {
justify-content: space-between; justify-content: space-between;

View File

@ -1,7 +1,11 @@
<template> <template>
<div class="modal__backdrop"> <div class="modal__backdrop">
<div <div
:class="{'modal--hide-header': hideHeader || fullscreen, 'modal--fullscreen': fullscreen, 'modal--small': small}" :class="{
'modal--hide-header': hideHeader || fullscreen,
'modal--fullscreen': fullscreen,
'modal--small': small,
}"
class="modal" class="modal"
> >
<div class="modal__header"> <div class="modal__header">
@ -9,20 +13,14 @@
</div> </div>
<div class="modal__body"> <div class="modal__body">
<slot /> <slot />
<div <div class="modal__close-button" @click="hideModal">
class="modal__close-button"
@click="hideModal"
>
<cross class="modal__close-icon" /> <cross class="modal__close-icon" />
</div> </div>
</div> </div>
<div class="modal__footer"> <div class="modal__footer">
<slot name="footer"> <slot name="footer">
<!--<a class="button button&#45;&#45;active">Speichern</a>--> <!--<a class="button button&#45;&#45;active">Speichern</a>-->
<a <a class="button" @click="hideModal">Abbrechen</a>
class="button"
@click="hideModal"
>Abbrechen</a>
</slot> </slot>
</div> </div>
</div> </div>
@ -36,32 +34,32 @@
props: { props: {
hideHeader: { hideHeader: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
fullscreen: { fullscreen: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
small: { small: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
components: { components: {
Cross Cross,
}, },
methods: { methods: {
hideModal() { hideModal() {
this.$store.dispatch('hideModal'); this.$store.dispatch('hideModal');
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
.modal { .modal {
align-self: center; align-self: center;
@ -77,7 +75,7 @@
display: grid; display: grid;
} }
grid-template-rows: auto 1fr 65px; grid-template-rows: auto 1fr 65px;
grid-template-areas: "header" "body" "footer"; grid-template-areas: 'header' 'body' 'footer';
-ms-grid-rows: auto 1fr 65px; -ms-grid-rows: auto 1fr 65px;
position: relative; position: relative;
@ -135,7 +133,7 @@
&--hide-header { &--hide-header {
grid-template-rows: 1fr 65px; grid-template-rows: 1fr 65px;
grid-template-areas: "body" "footer"; grid-template-areas: 'body' 'footer';
#{$parent}__header { #{$parent}__header {
display: none; display: none;
@ -144,7 +142,6 @@
#{$parent}__body { #{$parent}__body {
padding: $default-padding; padding: $default-padding;
} }
} }
&--fullscreen { &--fullscreen {
@ -152,7 +149,7 @@
height: auto; height: auto;
grid-template-rows: 1fr; grid-template-rows: 1fr;
-ms-grid-rows: 1fr; -ms-grid-rows: 1fr;
grid-template-areas: "body"; grid-template-areas: 'body';
overflow: hidden; overflow: hidden;
#{$parent}__footer { #{$parent}__footer {

View File

@ -6,25 +6,20 @@
:value="value" :value="value"
class="modal-input__inputfield skillbox-input" class="modal-input__inputfield skillbox-input"
@input="$emit('input', $event.target.value)" @input="$emit('input', $event.target.value)"
> />
<div <div class="modal-input__error" v-if="error">Für Inhaltsblöcke muss zwingend ein Titel erfasst werden.</div>
class="modal-input__error"
v-if="error"
>
Für Inhaltsblöcke muss zwingend ein Titel erfasst werden.
</div>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
props: ['value', 'error', 'placeholder'] props: ['value', 'error', 'placeholder'],
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_functions.scss"; @import '@/styles/_functions.scss';
.modal-input { .modal-input {
&__inputfield { &__inputfield {

View File

@ -1,17 +1,9 @@
<template> <template>
<div class="more-options"> <div class="more-options">
<a <a class="more-options__more-link" data-cy="more-options-link" @click.stop="showMenu = !showMenu">
class="more-options__more-link"
data-cy="more-options-link"
@click.stop="showMenu = !showMenu"
>
<ellipses class="more-options__ellipses" /> <ellipses class="more-options__ellipses" />
</a> </a>
<widget-popover <widget-popover class="more-options__popover" v-if="showMenu" @hide-me="showMenu = false">
class="more-options__popover"
v-if="showMenu"
@hide-me="showMenu = false"
>
<slot /> <slot />
</widget-popover> </widget-popover>
</div> </div>
@ -25,19 +17,19 @@
export default { export default {
components: { components: {
WidgetPopover, WidgetPopover,
Ellipses Ellipses,
}, },
data() { data() {
return { return {
showMenu: false showMenu: false,
}; };
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.more-options { .more-options {
display: flex; display: flex;

View File

@ -1,11 +1,5 @@
<template> <template>
<base-input <base-input :label="label" :checked="checked" :item="item" :type="'radiobutton'" @input="passOn" />
:label="label"
:checked="checked"
:item="item"
:type="'radiobutton'"
@input="passOn"
/>
</template> </template>
<script> <script>
@ -15,19 +9,19 @@
props: { props: {
label: String, label: String,
checked: { checked: {
type: Boolean type: Boolean,
}, },
item: Object item: Object,
}, },
components: { components: {
BaseInput BaseInput,
}, },
methods: { methods: {
passOn() { passOn() {
this.$emit('input', ...arguments); this.$emit('input', ...arguments);
} },
} },
}; };
</script> </script>

View File

@ -1,14 +1,7 @@
<template> <template>
<div <div class="read-only-banner" data-cy="read-only-banner" v-if="me.readOnly || me.selectedClass.readOnly">
class="read-only-banner"
data-cy="read-only-banner"
v-if="me.readOnly || me.selectedClass.readOnly"
>
<div class="read-only-banner__content"> <div class="read-only-banner__content">
<p class="read-only-banner__text"> <p class="read-only-banner__text">{{ readOnlyText }} Sie können Inhalte lesen, aber nicht bearbeiten.</p>
{{ readOnlyText }} Sie können Inhalte lesen, aber nicht
bearbeiten.
</p>
<div class="read-only-banner__buttons"> <div class="read-only-banner__buttons">
<router-link <router-link
:to="licenseActivationLink" :to="licenseActivationLink"
@ -18,11 +11,9 @@
> >
Neuen Lizenzcode eingeben Neuen Lizenzcode eingeben
</router-link> </router-link>
<a <a target="_blank" href="https://myskillbox.ch/lesemodus" class="button button--secondary"
target="_blank" >Mehr Informationen zum Lesemodus</a
href="https://myskillbox.ch/lesemodus" >
class="button button--secondary"
>Mehr Informationen zum Lesemodus</a>
</div> </div>
</div> </div>
</div> </div>
@ -102,7 +93,6 @@
} }
&__buttons { &__buttons {
} }
&__link { &__link {

View File

@ -1,10 +1,6 @@
<template> <template>
<transition name="fade"> <transition name="fade">
<a <a class="scroll-up" v-if="scroll > 200" @click="scrollTop">
class="scroll-up"
v-if="scroll>200"
@click="scrollTop"
>
<arrow-up class="scroll-up__icon" /> <arrow-up class="scroll-up__icon" />
</a> </a>
</transition> </transition>
@ -15,12 +11,12 @@
export default { export default {
components: { components: {
ArrowUp ArrowUp,
}, },
data() { data() {
return { return {
scroll: 0 scroll: 0,
}; };
}, },
@ -38,7 +34,7 @@
methods: { methods: {
scrollTop() { scrollTop() {
document.scrollingElement.scrollTop = 0; document.scrollingElement.scrollTop = 0;
} },
}, },
}; };
</script> </script>
@ -64,15 +60,14 @@
height: 50px; height: 50px;
fill: $color-brand; fill: $color-brand;
} }
} }
.fade-enter-active, .fade-leave-active { .fade-enter-active,
transition: opacity .3s; .fade-leave-active {
transition: opacity 0.3s;
} }
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
{
opacity: 0; opacity: 0;
} }
</style> </style>

View File

@ -1,18 +1,10 @@
<template> <template>
<div class="section-block"> <div class="section-block">
<div <div :class="{ 'section-block--navigatable': route }" class="section-block__illustration" @click="navigate()">
:class="{'section-block--navigatable': route}"
class="section-block__illustration"
@click="navigate()"
>
<slot /> <slot />
</div> </div>
<div <div :class="{ 'section-block--navigatable': route }" class="section-block__title block-title" @click="navigate()">
:class="{'section-block--navigatable': route}"
class="section-block__title block-title"
@click="navigate()"
>
<h2 class="block-title__title"> <h2 class="block-title__title">
{{ title }} {{ title }}
</h2> </h2>
@ -30,10 +22,7 @@
> >
{{ linkText }} {{ linkText }}
</a> </a>
<span <span class="subsection__content subsection__content--disabled" v-if="!route">Noch nicht verfügbar</span>
class="subsection__content subsection__content--disabled"
v-if="!route"
>Noch nicht verfügbar</span>
</div> </div>
</div> </div>
</div> </div>
@ -47,14 +36,14 @@
if (this.route) { if (this.route) {
this.$router.push(this.route); this.$router.push(this.route);
} }
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_functions.scss"; @import '@/styles/_functions.scss';
.section-block { .section-block {
border-radius: $default-border-radius; border-radius: $default-border-radius;
@ -72,7 +61,8 @@
} }
.block-title { .block-title {
&__title, &__subtitle { &__title,
&__subtitle {
color: $color-charcoal-dark; color: $color-charcoal-dark;
font-family: $sans-serif-font-family; font-family: $sans-serif-font-family;
} }
@ -109,5 +99,4 @@
} }
} }
} }
</style> </style>

View File

@ -5,24 +5,12 @@
</div> </div>
<div class="student-submission__entry entry"> <div class="student-submission__entry entry">
<p>{{ submission.text | trimToLength(50) }}</p> <p>{{ submission.text | trimToLength(50) }}</p>
<p <p class="entry__document" v-if="submission.document && submission.document.length > 0">
class="entry__document" <student-submission-document :document="submission.document" class="entry-document" />
v-if="submission.document && submission.document.length > 0"
>
<student-submission-document
:document="submission.document"
class="entry-document"
/>
</p> </p>
</div> </div>
<div <div class="student-submission__feedback entry" v-if="submission.submissionFeedback">
class="student-submission__feedback entry" <p :class="{ 'entry__text--final': submission.submissionFeedback.final }" class="entry__text">
v-if="submission.submissionFeedback"
>
<p
:class="{'entry__text--final': submission.submissionFeedback.final}"
class="entry__text"
>
{{ submission.submissionFeedback.text | trimToLength(50) }} {{ submission.submissionFeedback.text | trimToLength(50) }}
</p> </p>
</div> </div>
@ -35,7 +23,7 @@
export default { export default {
props: ['submission'], props: ['submission'],
components: { components: {
StudentSubmissionDocument StudentSubmissionDocument,
}, },
filters: { filters: {
trimToLength: function (text, numberOfChars) { trimToLength: function (text, numberOfChars) {
@ -50,20 +38,21 @@
return text; return text;
} }
return `${text.substring(0, index)}`; return `${text.substring(0, index)}`;
} },
}, },
computed: { computed: {
name() { name() {
return this.submission && this.submission.student return this.submission && this.submission.student
? `${this.submission.student.firstName} ${this.submission.student.lastName}` : ''; ? `${this.submission.student.firstName} ${this.submission.student.lastName}`
: '';
}, },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.student-submission { .student-submission {
@include table-row; @include table-row;

View File

@ -1,16 +1,12 @@
<template> <template>
<div class="submission-document"> <div class="submission-document">
<p <p class="submission-document__content content" v-if="document && document.length > 0">
class="submission-document__content content"
v-if="document && document.length > 0"
>
<document-icon class="content__icon" /><span class="content__text">{{ filename }}</span> <document-icon class="content__icon" /><span class="content__text">{{ filename }}</span>
</p> </p>
</div> </div>
</template> </template>
<script> <script>
import filenameFromUrl from '@/helpers/urls'; import filenameFromUrl from '@/helpers/urls';
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon'); const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon');
@ -22,7 +18,7 @@
computed: { computed: {
filename() { filename() {
return filenameFromUrl(this.document); return filenameFromUrl(this.document);
} },
}, },
}; };
</script> </script>

View File

@ -4,10 +4,7 @@
<avatar :avatar-url="avatarUrl" /> <avatar :avatar-url="avatarUrl" />
</div> </div>
<span class="user-widget__name">{{ firstName }} {{ lastName }}</span> <span class="user-widget__name">{{ firstName }} {{ lastName }}</span>
<span <span class="user-widget__date" v-if="date">{{ date }}</span>
class="user-widget__date"
v-if="date"
>{{ date }}</span>
</div> </div>
</template> </template>
@ -18,13 +15,13 @@
props: ['firstName', 'lastName', 'avatarUrl', 'date'], props: ['firstName', 'lastName', 'avatarUrl', 'date'],
components: { components: {
Avatar Avatar,
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
.user-widget { .user-widget {
color: $color-silver-dark; color: $color-silver-dark;

View File

@ -1,16 +1,7 @@
<template> <template>
<div <div :class="{ 'user-widget--is-profile': isProfile }" class="user-widget">
:class="{'user-widget--is-profile': isProfile}" <div class="user-widget__avatar" data-cy="user-widget-avatar">
class="user-widget" <avatar :avatar-url="avatarUrl" :icon-highlighted="isProfile" />
>
<div
class="user-widget__avatar"
data-cy="user-widget-avatar"
>
<avatar
:avatar-url="avatarUrl"
:icon-highlighted="isProfile"
/>
</div> </div>
</div> </div>
</template> </template>
@ -21,23 +12,23 @@
export default { export default {
props: { props: {
avatarUrl: { avatarUrl: {
type: String type: String,
} },
}, },
components: { components: {
Avatar Avatar,
}, },
computed: { computed: {
isProfile() { isProfile() {
return this.$route.meta.isProfile; return this.$route.meta.isProfile;
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.user-widget { .user-widget {
color: $color-silver-dark; color: $color-silver-dark;

View File

@ -1,8 +1,5 @@
<template> <template>
<nav <nav :class="{ 'content-navigation--sidebar': isSidebar }" class="content-navigation">
:class="{'content-navigation--sidebar': isSidebar}"
class="content-navigation"
>
<div class="content-navigation__primary"> <div class="content-navigation__primary">
<div class="content-navigation__item"> <div class="content-navigation__item">
<router-link <router-link
@ -15,9 +12,7 @@
{{ $flavor.textTopics }} {{ $flavor.textTopics }}
</router-link> </router-link>
<topic-navigation <topic-navigation v-if="isSidebar" />
v-if="isSidebar"
/>
</div> </div>
<div class="content-navigation__item"> <div class="content-navigation__item">
@ -45,12 +40,7 @@
</div> </div>
</div> </div>
<router-link <router-link to="/" class="content-navigation__logo" data-cy="home-link" v-if="!isSidebar">
to="/"
class="content-navigation__logo"
data-cy="home-link"
v-if="!isSidebar"
>
<logo class="content-navigation__logo-icon" /> <logo class="content-navigation__logo-icon" />
</router-link> </router-link>
@ -67,10 +57,7 @@
</router-link> </router-link>
</div> </div>
<div <div class="content-navigation__item content-navigation__item--secondary" v-if="showPortfolio">
class="content-navigation__item content-navigation__item--secondary"
v-if="showPortfolio"
>
<router-link <router-link
to="/portfolio" to="/portfolio"
active-class="content-navigation__link--active" active-class="content-navigation__link--active"
@ -80,10 +67,7 @@
Portfolio Portfolio
</router-link> </router-link>
</div> </div>
<div <div class="content-navigation__item content-navigation__item--secondary" v-if="isSidebar">
class="content-navigation__item content-navigation__item--secondary"
v-if="isSidebar"
>
<a <a
:href="$flavor.supportLink" :href="$flavor.supportLink"
target="_blank" target="_blank"
@ -107,21 +91,21 @@
export default { export default {
props: { props: {
isSidebar: { isSidebar: {
default: false default: false,
} },
}, },
mixins: [sidebarMixin, meMixin], mixins: [sidebarMixin, meMixin],
components: { components: {
TopicNavigation, TopicNavigation,
Logo Logo,
}, },
computed: { computed: {
showPortfolio() { showPortfolio() {
return this.$flavor.showPortfolio; return this.$flavor.showPortfolio;
} },
}, },
methods: { methods: {
@ -133,14 +117,14 @@
}, },
close() { close() {
this.closeSidebar('navigation'); this.closeSidebar('navigation');
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
.content-navigation { .content-navigation {
display: flex; display: flex;
@ -151,7 +135,8 @@
@include navigation-link; @include navigation-link;
} }
&__primary, &__secondary { &__primary,
&__secondary {
display: none; display: none;
flex-direction: row; flex-direction: row;
@ -161,7 +146,7 @@
} }
&__logo { &__logo {
color: #17A887; color: #17a887;
font-size: 36px; font-size: 36px;
font-weight: 800; font-weight: 800;
font-family: $sans-serif-font-family; font-family: $sans-serif-font-family;
@ -196,7 +181,8 @@
&--sidebar { &--sidebar {
flex-direction: column; flex-direction: column;
#{$parent}__primary, #{$parent}__secondary { #{$parent}__primary,
#{$parent}__secondary {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
@ -212,7 +198,6 @@
&:only-child { &:only-child {
margin-bottom: 0; margin-bottom: 0;
} }
} }
#{$parent}__item { #{$parent}__item {

View File

@ -1,18 +1,8 @@
<template> <template>
<transition name="slide"> <transition name="slide">
<div <div class="navigation-sidebar" v-if="sidebar.navigation" v-click-outside="close">
class="navigation-sidebar" <content-navigation :is-sidebar="true" class="navigation-sidebar__main" />
v-if="sidebar.navigation" <div class="navigation-sidebar__close-button" @click="close">
v-click-outside="close"
>
<content-navigation
:is-sidebar="true"
class="navigation-sidebar__main"
/>
<div
class="navigation-sidebar__close-button"
@click="close"
>
<cross class="navigation-sidebar__close-icon" /> <cross class="navigation-sidebar__close-icon" />
</div> </div>
</div> </div>
@ -31,21 +21,20 @@
components: { components: {
ContentNavigation, ContentNavigation,
Cross Cross,
}, },
methods: { methods: {
close() { close() {
this.closeSidebar('navigation'); this.closeSidebar('navigation');
}
}, },
},
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
$desktop-width: 285px; $desktop-width: 285px;
@ -67,10 +56,10 @@
grid-template-columns: 1fr 50px; grid-template-columns: 1fr 50px;
grid-template-rows: 50px max-content auto 100px; grid-template-rows: 50px max-content auto 100px;
grid-template-areas: "m m" "m m" "s s" "s s"; grid-template-areas: 'm m' 'm m' 's s' 's s';
&--with-subnavigation { &--with-subnavigation {
grid-template-areas: "m m" "m m" "sub sub" "s s"; grid-template-areas: 'm m' 'm m' 'sub sub' 's s';
} }
height: 100vh; height: 100vh;
@ -98,11 +87,13 @@
} }
.slide { .slide {
&-enter-active, &-leave-active { &-enter-active,
&-leave-active {
transition: left 0.2s; transition: left 0.2s;
} }
&-enter, &-leave-to { &-enter,
&-leave-to {
left: -100vw; left: -100vw;
@include desktop { @include desktop {
left: -$desktop-width; left: -$desktop-width;

View File

@ -1,21 +1,11 @@
<template> <template>
<div <div :class="{ 'sub-navigation-item--active': show }" class="sub-navigation-item" v-click-outside="close">
:class="{ 'sub-navigation-item--active': show}" <div class="sub-navigation-item__title" @click="show = !show">
class="sub-navigation-item"
v-click-outside="close"
>
<div
class="sub-navigation-item__title"
@click="show = !show"
>
{{ title }} {{ title }}
<chevron-down class="sub-navigation-item__icon sub-navigation-item__chevron-down" /> <chevron-down class="sub-navigation-item__icon sub-navigation-item__chevron-down" />
<chevron-up class="sub-navigation-item__icon sub-navigation-item__chevron-up" /> <chevron-up class="sub-navigation-item__icon sub-navigation-item__chevron-up" />
</div> </div>
<div <div class="sub-navigation-item__nav-items book-subnavigation" v-if="show">
class="sub-navigation-item__nav-items book-subnavigation"
v-if="show"
>
<slot /> <slot />
</div> </div>
</div> </div>
@ -30,25 +20,25 @@
components: { components: {
ChevronDown, ChevronDown,
ChevronUp ChevronUp,
}, },
data() { data() {
return { return {
show: false show: false,
}; };
}, },
watch: { watch: {
$route() { $route() {
this.show = false; this.show = false;
} },
}, },
methods: { methods: {
close() { close() {
this.show = false; this.show = false;
} },
} },
}; };
</script> </script>

View File

@ -23,22 +23,22 @@
export default { export default {
props: { props: {
mobile: { mobile: {
default: false default: false,
} },
}, },
mixins: [sidebarMixin], mixins: [sidebarMixin],
data() { data() {
return { return {
topics: [] topics: [],
}; };
}, },
methods: { methods: {
topicId(id) { topicId(id) {
return atob(id); return atob(id);
} },
}, },
apollo: { apollo: {
@ -49,14 +49,14 @@
if (!loading) { if (!loading) {
this.topics = this.$getRidOfEdges(data).topics; this.topics = this.$getRidOfEdges(data).topics;
} }
} },
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
.topic-navigation { .topic-navigation {
&__topic { &__topic {

View File

@ -1,15 +1,13 @@
<template> <template>
<a <a class="add-content-link" data-cy="add-content-link" @click="$emit('click')"
class="add-content-link" ><plus-icon class="add-content-link__icon" /> <span class="add-content-link__text">Inhalt hinzufügen</span></a
data-cy="add-content-link" >
@click="$emit('click')"
><plus-icon class="add-content-link__icon" /> <span class="add-content-link__text">Inhalt hinzufügen</span></a>
</template> </template>
<script> <script>
import PlusIcon from '@/components/icons/PlusIcon'; import PlusIcon from '@/components/icons/PlusIcon';
export default { export default {
components: { PlusIcon } components: { PlusIcon },
}; };
</script> </script>

View File

@ -1,10 +1,7 @@
<template> <template>
<div class="content-block-form content-list__parent"> <div class="content-block-form content-list__parent">
<div class="content-block-form__content"> <div class="content-block-form__content">
<h1 <h1 class="heading-1 content-block-form__heading" data-cy="content-block-form-heading">
class="heading-1 content-block-form__heading"
data-cy="content-block-form-heading"
>
{{ title }} {{ title }}
</h1> </h1>
@ -19,10 +16,7 @@
/> />
<!-- Form for title of content block --> <!-- Form for title of content block -->
<content-form-section <content-form-section data-cy="content-form-title-section" title="Titel (Pflichtfeld)">
data-cy="content-form-title-section"
title="Titel (Pflichtfeld)"
>
<input-with-label <input-with-label
:value="localContentBlock.title" :value="localContentBlock.title"
data-cy="content-block-title" data-cy="content-block-title"
@ -32,9 +26,7 @@
</content-form-section> </content-form-section>
<!-- Add content at top of content block --> <!-- Add content at top of content block -->
<add-content-link <add-content-link @click="addBlock(-1)" />
@click="addBlock(-1)"
/>
<!-- Loop for outer contents layer --> <!-- Loop for outer contents layer -->
<div <div
@ -43,11 +35,7 @@
:key="block.id" :key="block.id"
> >
<!-- If the block is a content list --> <!-- If the block is a content list -->
<div <div class="content-block-form__segment" data-cy="content-list" v-if="block.type === 'content_list_item'">
class="content-block-form__segment"
data-cy="content-list"
v-if="block.type === 'content_list_item'"
>
<content-element-actions <content-element-actions
class="content-block-form__actions" class="content-block-form__actions"
:actions="{ extended: true, up: outer > 0, down: outer < localContentBlock.contents.length }" :actions="{ extended: true, up: outer > 0, down: outer < localContentBlock.contents.length }"
@ -57,15 +45,8 @@
@move-top="top(outer)" @move-top="top(outer)"
@move-bottom="bottom(outer)" @move-bottom="bottom(outer)"
/> />
<ol <ol class="content-list__item" data-cy="content-list-item">
class="content-list__item" <li class="content-block-form__segment" v-for="(content, index) in block.contents" :key="content.id">
data-cy="content-list-item"
>
<li
class="content-block-form__segment"
v-for="(content, index) in block.contents"
:key="content.id"
>
<content-element <content-element
:first-element="index === 0" :first-element="index === 0"
:last-element="index === block.contents.length - 1" :last-element="index === block.contents.length - 1"
@ -80,10 +61,7 @@
@bottom="bottom(outer, index)" @bottom="bottom(outer, index)"
/> />
<add-content-link <add-content-link class="content-block-form__add-button" @click="addBlock(outer, index)" />
class="content-block-form__add-button"
@click="addBlock(outer, index)"
/>
</li> </li>
</ol> </ol>
</div> </div>
@ -105,11 +83,10 @@
/> />
<!-- Add element after the looped item --> <!-- Add element after the looped item -->
<add-content-link <add-content-link @click="addBlock(outer)" />
@click="addBlock(outer)"
/>
</div> </div>
</div><!-- --> </div>
<!-- -->
<!-- Save and Cancel buttons --> <!-- Save and Cancel buttons -->
<footer class="content-block-form__footer"> <footer class="content-block-form__footer">
<div class="content-block-form__buttons"> <div class="content-block-form__buttons">
@ -121,10 +98,7 @@
> >
Speichern Speichern
</button> </button>
<a <a class="button" @click="$emit('back')">Abbrechen</a>
class="button"
@click="$emit('back')"
>Abbrechen</a>
</div> </div>
</footer> </footer>
</div> </div>
@ -143,13 +117,13 @@
moveToIndex, moveToIndex,
removeAtIndex, removeAtIndex,
replaceAtIndex, replaceAtIndex,
swapElements swapElements,
} from '@/graphql/immutable-operations'; } from '@/graphql/immutable-operations';
import { CHOOSER, transformInnerContents } from '@/components/content-block-form/helpers.js'; import { CHOOSER, transformInnerContents } from '@/components/content-block-form/helpers.js';
import ContentElementActions from '@/components/content-block-form/ContentElementActions.vue'; import ContentElementActions from '@/components/content-block-form/ContentElementActions.vue';
import {ContentBlock, numberOrUndefined} from "@/@types"; import { ContentBlock, numberOrUndefined } from '@/@types';
import {DEFAULT_FEATURE_SET} from "@/consts/features.consts"; import { DEFAULT_FEATURE_SET } from '@/consts/features.consts';
// TODO: refactor this file, it's huuuuuge! // TODO: refactor this file, it's huuuuuge!
interface ContentBlockFormData { interface ContentBlockFormData {
@ -168,12 +142,12 @@
}, },
features: { features: {
type: String, type: String,
default: DEFAULT_FEATURE_SET default: DEFAULT_FEATURE_SET,
} },
}, },
provide(): object { provide(): object {
return { return {
features: this.features features: this.features,
}; };
}, },
components: { components: {
@ -186,13 +160,16 @@
}, },
data(): ContentBlockFormData { data(): ContentBlockFormData {
return { return {
localContentBlock: Object.assign({}, { localContentBlock: Object.assign(
{},
{
title: this.contentBlock.title, title: this.contentBlock.title,
// contents: [...this.contentBlock.contents], // contents: [...this.contentBlock.contents],
contents: transformInnerContents([...this.contentBlock.contents]), contents: transformInnerContents([...this.contentBlock.contents]),
id: this.contentBlock.id || undefined, id: this.contentBlock.id || undefined,
isAssignment: this.contentBlock.type && this.contentBlock.type.toLowerCase() === 'task', isAssignment: this.contentBlock.type && this.contentBlock.type.toLowerCase() === 'task',
}), }
),
}; };
}, },
computed: { computed: {
@ -201,7 +178,7 @@
}, },
hasDefaultFeatures(): boolean { hasDefaultFeatures(): boolean {
return this.features === DEFAULT_FEATURE_SET; return this.features === DEFAULT_FEATURE_SET;
} },
}, },
methods: { methods: {
update(index: number, element: any, parent?: number) { update(index: number, element: any, parent?: number) {
@ -243,13 +220,12 @@
}, },
remove(outer: number, inner?: number, askForConfirmation = true) { remove(outer: number, inner?: number, askForConfirmation = true) {
if (askForConfirmation) { if (askForConfirmation) {
this.$modal.open('confirm') this.$modal
.open('confirm')
.then(() => { .then(() => {
this.executeRemoval(outer, inner); this.executeRemoval(outer, inner);
}) })
.catch(() => { .catch(() => {});
});
} else { } else {
this.executeRemoval(outer, inner); this.executeRemoval(outer, inner);
} }
@ -329,7 +305,6 @@
this.$emit('save', contentBlock); this.$emit('save', contentBlock);
}, },
}, },
}); });
</script> </script>
@ -369,7 +344,6 @@
height: auto; height: auto;
} }
&__heading { &__heading {
@include heading-1; @include heading-1;
} }
@ -406,7 +380,8 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
& > * { // we make an exception and use a wildcard here & > * {
// we make an exception and use a wildcard here
width: 800px; width: 800px;
max-width: 100vw; max-width: 100vw;
box-sizing: border-box; box-sizing: border-box;

View File

@ -9,7 +9,6 @@
@remove="$emit('remove', false)" @remove="$emit('remove', false)"
/> />
<!-- Content Forms --> <!-- Content Forms -->
<content-form-section <content-form-section
:title="title" :title="title"
@ -27,14 +26,10 @@
:class="['content-element__component']" :class="['content-element__component']"
v-bind="element" v-bind="element"
:is="component" :is="component"
@change-text="changeText" @change-text="changeText"
@link-change-url="changeUrl" @link-change-url="changeUrl"
@change-url="changeUrl" @change-url="changeUrl"
@switch-to-document="switchToDocument" @switch-to-document="switchToDocument"
@assignment-change-title="changeAssignmentTitle" @assignment-change-title="changeAssignmentTitle"
@assignment-change-assignment="changeAssignmentAssignment" @assignment-change-assignment="changeAssignmentAssignment"
/> />
@ -48,23 +43,29 @@
import ContentElementActions from '@/components/content-block-form/ContentElementActions'; import ContentElementActions from '@/components/content-block-form/ContentElementActions';
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon'); const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon');
const ContentBlockElementChooserWidget = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/ContentBlockElementChooserWidget'); const ContentBlockElementChooserWidget = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/ContentBlockElementChooserWidget');
const LinkForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/LinkForm'); const LinkForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/LinkForm');
const VideoForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/VideoForm'); const VideoForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/VideoForm');
const ImageForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/ImageForm'); const ImageForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/ImageForm');
const DocumentForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/DocumentForm'); const DocumentForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/DocumentForm');
const AssignmentForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/AssignmentForm'); const AssignmentForm = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/AssignmentForm');
const TextForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/TipTap.vue'); const TextForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/TipTap.vue');
const SubtitleForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/SubtitleForm'); const SubtitleForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/SubtitleForm');
// readonly blocks // readonly blocks
const Assignment = () => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/assignment/Assignment'); const Assignment = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/assignment/Assignment');
const SurveyBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/SurveyBlock'); const SurveyBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/SurveyBlock');
const Solution = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/Solution'); const Solution = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/Solution');
const ImageBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ImageBlock'); const ImageBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ImageBlock');
const Instruction = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/Instruction'); const Instruction = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/Instruction');
const ModuleRoomSlug = () => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/ModuleRoomSlug'); const ModuleRoomSlug = () =>
const CmsDocumentBlock = () => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/CmsDocumentBlock'); import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ModuleRoomSlug');
const ThinglinkBlock = () => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/ThinglinkBlock'); const CmsDocumentBlock = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/CmsDocumentBlock');
const ThinglinkBlock = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ThinglinkBlock');
const InfogramBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/InfogramBlock'); const InfogramBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/InfogramBlock');
const CHOOSER = 'content-block-element-chooser-widget'; const CHOOSER = 'content-block-element-chooser-widget';
@ -110,7 +111,7 @@
CmsDocumentBlock, CmsDocumentBlock,
InfogramBlock, InfogramBlock,
ThinglinkBlock, ThinglinkBlock,
Assignment Assignment,
}, },
computed: { computed: {
@ -216,12 +217,12 @@
case 'thinglink_block': case 'thinglink_block':
return { return {
component: 'thinglink-block', component: 'thinglink-block',
title: 'Interaktive Grafik' title: 'Interaktive Grafik',
}; };
case 'infogram_block': case 'infogram_block':
return { return {
component: 'infogram-block', component: 'infogram-block',
title: 'Interaktive Grafik' title: 'Interaktive Grafik',
}; };
} }
return { return {
@ -295,9 +296,12 @@
case 'document_block': case 'document_block':
el = { el = {
...el, ...el,
value: Object.assign({ value: Object.assign(
{
url: '', url: '',
}, value), },
value
),
}; };
break; break;
case 'image_url_block': case 'image_url_block':

View File

@ -1,17 +1,9 @@
<template> <template>
<div class="content-element-actions"> <div class="content-element-actions">
<button <button class="icon-button" @click.stop="toggle(true)">
class="icon-button"
@click.stop="toggle(true)"
>
<ellipses class="icon-button__icon" /> <ellipses class="icon-button__icon" />
</button> </button>
<widget-popover <widget-popover class="content-element-actions__popover" :no-padding="true" v-if="show" @hide-me="toggle(false)">
class="content-element-actions__popover"
:no-padding="true"
v-if="show"
@hide-me="toggle(false)"
>
<section class="content-element-actions__section"> <section class="content-element-actions__section">
<button-with-icon-and-text <button-with-icon-and-text
class="content-element-actions__button" class="content-element-actions__button"
@ -67,7 +59,7 @@
import WidgetPopover from '@/components/ui/WidgetPopover.vue'; import WidgetPopover from '@/components/ui/WidgetPopover.vue';
import Ellipses from '@/components/icons/Ellipses.vue'; import Ellipses from '@/components/icons/Ellipses.vue';
import ButtonWithIconAndText from '@/components/ui/ButtonWithIconAndText.vue'; import ButtonWithIconAndText from '@/components/ui/ButtonWithIconAndText.vue';
import {ActionOptions} from "@/@types"; import { ActionOptions } from '@/@types';
interface Data { interface Data {
show: boolean; show: boolean;
@ -76,7 +68,7 @@
export default Vue.extend({ export default Vue.extend({
props: { props: {
actions: { actions: {
type: Object as () => ActionOptions type: Object as () => ActionOptions,
}, },
}, },
components: { ButtonWithIconAndText, Ellipses, WidgetPopover }, components: { ButtonWithIconAndText, Ellipses, WidgetPopover },
@ -111,7 +103,7 @@
emitAndClose(event: string) { emitAndClose(event: string) {
this.$emit(event); this.$emit(event);
this.close(); this.close();
} },
}, },
}); });
</script> </script>
@ -126,7 +118,6 @@
white-space: nowrap; white-space: nowrap;
top: 100%; top: 100%;
transform: translateY($small-spacing); transform: translateY($small-spacing);
} }
&__section { &__section {
@ -142,5 +133,4 @@
margin-bottom: $medium-spacing; margin-bottom: $medium-spacing;
} }
} }
</style> </style>

View File

@ -1,13 +1,8 @@
<template> <template>
<div class="content-form-section"> <div class="content-form-section">
<h2 class="content-form-section__heading"> <h2 class="content-form-section__heading">
<component <component class="content-form-section__icon" :is="icon" />
class="content-form-section__icon" <span class="content-form-section__title" data-cy="content-form-section-title">{{ title }}</span>
:is="icon"
/> <span
class="content-form-section__title"
data-cy="content-form-section-title"
>{{ title }}</span>
</h2> </h2>
<content-element-actions <content-element-actions
@ -29,28 +24,27 @@
<script lang="ts"> <script lang="ts">
import formElementIcons from '@/components/ui/form-element-icons.js'; import formElementIcons from '@/components/ui/form-element-icons.js';
import ContentElementActions from '@/components/content-block-form/ContentElementActions.vue'; import ContentElementActions from '@/components/content-block-form/ContentElementActions.vue';
import {ActionOptions} from "@/@types"; import { ActionOptions } from '@/@types';
export default { export default {
props: { props: {
title: { title: {
type: String, type: String,
default: '' default: '',
}, },
icon: { icon: {
type: String, type: String,
default: '' default: '',
}, },
actions: { actions: {
type: Object as () => ActionOptions, type: Object as () => ActionOptions,
default: () => {} default: () => {},
} },
}, },
components: { components: {
ContentElementActions, ContentElementActions,
...formElementIcons ...formElementIcons,
} },
}; };
</script> </script>

View File

@ -18,27 +18,11 @@
/> />
</template> </template>
<add-content-element <add-content-element :index="-1" class="contents-form__add" @add-element="addElement" />
:index="-1" <div class="contents-form__element" v-for="(element, index) in localContentBlock.contents" :key="index">
class="contents-form__add" <content-element :element="element" @update="update(index, $event)" @remove="remove(index)" />
@add-element="addElement"
/>
<div
class="contents-form__element"
v-for="(element, index) in localContentBlock.contents"
:key="index"
>
<content-element
:element="element"
@update="update(index, $event)"
@remove="remove(index)"
/>
<add-content-element <add-content-element :index="index" class="contents-form__add" @add-element="addElement" />
:index="index"
class="contents-form__add"
@add-element="addElement"
/>
</div> </div>
<template #footer> <template #footer>
@ -48,11 +32,9 @@
class="button button--primary" class="button button--primary"
data-cy="modal-save-button" data-cy="modal-save-button"
@click="save" @click="save"
>Speichern</a> >Speichern</a
<a >
class="button" <a class="button" @click="$emit('hide')">Abbrechen</a>
@click="$emit('hide')"
>Abbrechen</a>
</div> </div>
</template> </template>
</modal> </modal>
@ -63,7 +45,8 @@
const ModalInput = () => import(/* webpackChunkName: "content-forms" */ '@/components/ModalInput'); const ModalInput = () => import(/* webpackChunkName: "content-forms" */ '@/components/ModalInput');
const AddContentElement = () => import(/* webpackChunkName: "content-forms" */ '@/components/AddContentElement'); const AddContentElement = () => import(/* webpackChunkName: "content-forms" */ '@/components/AddContentElement');
const ContentElement = () => import(/* webpackChunkName: "content-forms" */'@/components/content-block-form/ContentElement'); const ContentElement = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/ContentElement');
const Modal = () => import('@/components/Modal.vue'); const Modal = () => import('@/components/Modal.vue');
const Checkbox = () => import('@/components/ui/Checkbox.vue'); const Checkbox = () => import('@/components/ui/Checkbox.vue');
@ -96,12 +79,15 @@
data() { data() {
return { return {
error: false, error: false,
localContentBlock: Object.assign({}, { localContentBlock: Object.assign(
{},
{
title: this.contentBlock.title, title: this.contentBlock.title,
contents: [...this.contentBlock.contents], contents: [...this.contentBlock.contents],
id: this.contentBlock.id || undefined, id: this.contentBlock.id || undefined,
isAssignment: this.contentBlock.type && this.contentBlock.type.toLowerCase() === 'task', isAssignment: this.contentBlock.type && this.contentBlock.type.toLowerCase() === 'task',
}), }
),
me: {}, me: {},
}; };
}, },
@ -147,19 +133,17 @@
remove(index) { remove(index) {
this.localContentBlock.contents.splice(index, 1); this.localContentBlock.contents.splice(index, 1);
}, },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.contents-form { .contents-form {
/* top level does not exist, because of the modal */ /* top level does not exist, because of the modal */
&__element { &__element {
} }
&__element-component { &__element-component {

View File

@ -1,10 +1,5 @@
<template> <template>
<contents-form <contents-form :content-block="contentBlock" :show-task-selection="true" @save="saveContentBlock" @hide="hideModal" />
:content-block="contentBlock"
:show-task-selection="true"
@save="saveContentBlock"
@hide="hideModal"
/>
</template> </template>
<script> <script>
@ -19,12 +14,12 @@
export default { export default {
components: { components: {
ContentsForm ContentsForm,
}, },
data() { data() {
return { return {
contentBlock: {} contentBlock: {},
}; };
}, },
@ -38,28 +33,32 @@
this.$store.dispatch('hideModal'); this.$store.dispatch('hideModal');
}, },
saveContentBlock(contentBlock) { saveContentBlock(contentBlock) {
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: EDIT_CONTENT_BLOCK_MUTATION, mutation: EDIT_CONTENT_BLOCK_MUTATION,
variables: { variables: {
input: { input: {
contentBlock: { contentBlock: {
title: contentBlock.title, title: contentBlock.title,
contents: contentBlock.contents.filter(value => Object.keys(value).length > 0), contents: contentBlock.contents.filter((value) => Object.keys(value).length > 0),
type: setUserBlockType(contentBlock.isAssignment) type: setUserBlockType(contentBlock.isAssignment),
}, },
id: contentBlock.id id: contentBlock.id,
}
}, },
refetchQueries: [{ },
refetchQueries: [
{
query: MODULE_DETAILS_QUERY, query: MODULE_DETAILS_QUERY,
variables: { variables: {
slug: this.$route.params.slug slug: this.$route.params.slug,
} },
}] },
}).then(() => { ],
})
.then(() => {
this.hideModal(); this.hideModal();
}); });
} },
}, },
apollo: { apollo: {
@ -67,11 +66,10 @@
return { return {
query: CONTENT_BLOCK_QUERY, query: CONTENT_BLOCK_QUERY,
variables: { variables: {
id: store.state.currentNoteBlock id: store.state.currentNoteBlock,
} },
}; };
} },
} },
}; };
</script> </script>

View File

@ -17,18 +17,16 @@
export default { export default {
components: { components: {
ContentsForm ContentsForm,
}, },
data() { data() {
return { return {
contentBlock: { contentBlock: {
title: '', title: '',
contents: [ contents: [{}],
{}
]
}, },
saving: false saving: false,
}; };
}, },
@ -39,30 +37,34 @@
}, },
saveContentBlock(contentBlock) { saveContentBlock(contentBlock) {
this.saving = true; this.saving = true;
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: NEW_CONTENT_BLOCK_MUTATION, mutation: NEW_CONTENT_BLOCK_MUTATION,
variables: { variables: {
input: { input: {
contentBlock: { contentBlock: {
title: contentBlock.title, title: contentBlock.title,
contents: contentBlock.contents.filter(value => Object.keys(value).length > 0), contents: contentBlock.contents.filter((value) => Object.keys(value).length > 0),
type: setUserBlockType(contentBlock.isAssignment) type: setUserBlockType(contentBlock.isAssignment),
}, },
after: this.$store.state.contentBlockPosition.after, after: this.$store.state.contentBlockPosition.after,
parent: this.$store.state.contentBlockPosition.parent parent: this.$store.state.contentBlockPosition.parent,
}
}, },
refetchQueries: [{ },
refetchQueries: [
{
query: MODULE_DETAILS_QUERY, query: MODULE_DETAILS_QUERY,
variables: { variables: {
slug: this.$route.params.slug slug: this.$route.params.slug,
} },
}] },
}).then(() => { ],
})
.then(() => {
this.saving = false; this.saving = false;
this.hideModal(); this.hideModal();
}); });
} },
}, },
}; };
</script> </script>

View File

@ -1,39 +1,46 @@
export const CHOOSER = 'content-block-element-chooser-widget'; export const CHOOSER = 'content-block-element-chooser-widget';
export const chooserFilter = value => value.type !== CHOOSER; export const chooserFilter = (value) => value.type !== CHOOSER;
export const USER_CONTENT_TYPES = ['subtitle', 'link_block', 'video_block', 'image_url_block', 'text_block', 'assignment', 'document_block']; export const USER_CONTENT_TYPES = [
'subtitle',
'link_block',
'video_block',
'image_url_block',
'text_block',
'assignment',
'document_block',
];
/* /*
Users can only edit certain types of contents, the rest can only be re-ordered. We only care about their id, we won't Users can only edit certain types of contents, the rest can only be re-ordered. We only care about their id, we won't
send anything else to the server about them send anything else to the server about them
*/ */
export const simplifyContents = (contents) => { export const simplifyContents = (contents) => {
return contents.map(c => { return contents.map((c) => {
if (USER_CONTENT_TYPES.includes(c.type)) { if (USER_CONTENT_TYPES.includes(c.type)) {
return c; return c;
} }
if (c.type === 'content_list_item') { if (c.type === 'content_list_item') {
return { return {
...c, ...c,
contents: simplifyContents(c.contents) contents: simplifyContents(c.contents),
}; };
} }
return { return {
id: c.id, id: c.id,
type: 'readonly' type: 'readonly',
}; };
}); });
}; };
export const cleanUpContents = (contents) => { export const cleanUpContents = (contents) => {
let filteredContents = contents let filteredContents = contents.filter(chooserFilter); // only use items that are not chooser elements
.filter(chooserFilter); // only use items that are not chooser elements
return filteredContents.map(content => { return filteredContents.map((content) => {
// if the element has a contents property, it's a list of contents, filter them // if the element has a contents property, it's a list of contents, filter them
if (content.contents) { if (content.contents) {
return { return {
...content, ...content,
contents: content.contents.filter(chooserFilter) contents: content.contents.filter(chooserFilter),
}; };
} }
// else just return it // else just return it
@ -49,7 +56,7 @@ export const transformInnerContents = (contents) => {
const { value, ...contentWithoutValue } = content; const { value, ...contentWithoutValue } = content;
ret.push({ ret.push({
...contentWithoutValue, ...contentWithoutValue,
contents: value contents: value,
}); });
} else { } else {
ret.push(content); ret.push(content);

View File

@ -1,14 +1,7 @@
<template> <template>
<div <div :class="{ 'cms-document-block--solution': solution }" class="cms-document-block">
:class="{'cms-document-block--solution': solution}"
class="cms-document-block"
>
<document-icon class="cms-document-block__icon" /> <document-icon class="cms-document-block__icon" />
<a <a :href="value.url" class="cms-document-block__link" target="_blank">{{ value.display_text }}</a>
:href="value.url"
class="cms-document-block__link"
target="_blank"
>{{ value.display_text }}</a>
</div> </div>
</template> </template>
@ -31,7 +24,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.cms-document-block { .cms-document-block {
display: grid; display: grid;
@ -44,7 +37,6 @@
height: 30px; height: 30px;
} }
&__link { &__link {
text-decoration: underline; text-decoration: underline;
} }
@ -60,7 +52,6 @@
#{$parent}__icon { #{$parent}__icon {
fill: $color-silver-dark; fill: $color-silver-dark;
} }
} }
} }
</style> </style>

View File

@ -1,9 +1,5 @@
<template> <template>
<div <div :class="componentClass" :data-scrollto="component.id" data-cy="content-component">
:class="componentClass"
:data-scrollto="component.id"
data-cy="content-component"
>
<bookmark-actions <bookmark-actions
:bookmarked="bookmarked" :bookmarked="bookmarked"
:note="note" :note="note"
@ -13,11 +9,7 @@
@edit-note="editNote" @edit-note="editNote"
@bookmark="bookmarkContent(component.id, !bookmarked)" @bookmark="bookmarkContent(component.id, !bookmarked)"
/> />
<component <component v-bind="component" :parent="parent" :is="component.type" />
v-bind="component"
:parent="parent"
:is="component.type"
/>
</div> </div>
</template> </template>
@ -25,84 +17,97 @@
import { constructContentComponentBookmarkMutation } from '@/helpers/update-content-bookmark-mutation'; import { constructContentComponentBookmarkMutation } from '@/helpers/update-content-bookmark-mutation';
const TextBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/TextBlock'); const TextBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/TextBlock');
const InstrumentWidget = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/InstrumentWidget'); const InstrumentWidget = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/InstrumentWidget');
const ImageBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ImageBlock'); const ImageBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ImageBlock');
const ImageUrlBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ImageUrlBlock'); const ImageUrlBlock = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ImageUrlBlock');
const VideoBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/VideoBlock'); const VideoBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/VideoBlock');
const LinkBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/LinkBlock'); const LinkBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/LinkBlock');
const DocumentBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'); const DocumentBlock = () =>
const CmsDocumentBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/CmsDocumentBlock'); import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/DocumentBlock');
const InfogramBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/InfogramBlock'); const CmsDocumentBlock = () =>
const ThinglinkBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ThinglinkBlock'); import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/CmsDocumentBlock');
const GeniallyBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/GeniallyBlock'); const InfogramBlock = () =>
const SubtitleBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/SubtitleBlock'); import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/InfogramBlock');
const SectionTitleBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/SectionTitleBlock'); const ThinglinkBlock = () =>
const ContentListBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ContentListBlock'); import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ThinglinkBlock');
const ModuleRoomSlug = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ModuleRoomSlug'); const GeniallyBlock = () =>
const Assignment = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/assignment/Assignment'); import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/GeniallyBlock');
const SubtitleBlock = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/SubtitleBlock');
const SectionTitleBlock = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/SectionTitleBlock');
const ContentListBlock = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ContentListBlock');
const ModuleRoomSlug = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ModuleRoomSlug');
const Assignment = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/assignment/Assignment');
const Survey = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/SurveyBlock'); const Survey = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/SurveyBlock');
const Solution = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Solution'); const Solution = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Solution');
const Instruction = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/Instruction'); const Instruction = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Instruction');
const BookmarkActions = () => import(/* webpackChunkName: "content-components" */ '@/components/notes/BookmarkActions'); const BookmarkActions = () => import(/* webpackChunkName: "content-components" */ '@/components/notes/BookmarkActions');
export default { export default {
props: { props: {
component: { component: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
parent: { parent: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
bookmarks: { bookmarks: {
type: Array, type: Array,
default: () => ([]) default: () => [],
}, },
notes: { notes: {
type: Array, type: Array,
default: () => ([]) default: () => [],
}, },
root: { root: {
type: String, type: String,
default: '' default: '',
}, },
editMode: { editMode: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
components: { components: {
'text_block': TextBlock, text_block: TextBlock,
'basic_knowledge': InstrumentWidget, // for legacy basic_knowledge: InstrumentWidget, // for legacy
'instrument': InstrumentWidget, instrument: InstrumentWidget,
'image_block': ImageBlock, image_block: ImageBlock,
'image_url_block': ImageUrlBlock, image_url_block: ImageUrlBlock,
'video_block': VideoBlock, video_block: VideoBlock,
'link_block': LinkBlock, link_block: LinkBlock,
'document_block': DocumentBlock, document_block: DocumentBlock,
'infogram_block': InfogramBlock, infogram_block: InfogramBlock,
'genially_block': GeniallyBlock, genially_block: GeniallyBlock,
'subtitle': SubtitleBlock, subtitle: SubtitleBlock,
'section_title': SectionTitleBlock, section_title: SectionTitleBlock,
'content_list': ContentListBlock, content_list: ContentListBlock,
'module_room_slug': ModuleRoomSlug, module_room_slug: ModuleRoomSlug,
'thinglink_block': ThinglinkBlock, thinglink_block: ThinglinkBlock,
'cms_document_block': CmsDocumentBlock, cms_document_block: CmsDocumentBlock,
Survey, Survey,
Solution, Solution,
Instruction, Instruction,
Assignment, Assignment,
BookmarkActions BookmarkActions,
}, },
computed: { computed: {
bookmarked() { bookmarked() {
return this.bookmarks && !!this.bookmarks.find(bookmark => bookmark.uuid === this.component.id); return this.bookmarks && !!this.bookmarks.find((bookmark) => bookmark.uuid === this.component.id);
}, },
note() { note() {
const bookmark = this.bookmarks && this.bookmarks.find(bookmark => bookmark.uuid === this.component.id); const bookmark = this.bookmarks && this.bookmarks.find((bookmark) => bookmark.uuid === this.component.id);
return bookmark && bookmark.note; return bookmark && bookmark.note;
}, },
showBookmarkActions() { showBookmarkActions() {
@ -114,18 +119,19 @@ export default {
classes.push('content-component--bookmarked'); classes.push('content-component--bookmarked');
} }
return classes; return classes;
} },
}, },
methods: { methods: {
addNote(id) { addNote(id) {
const type = Object.prototype.hasOwnProperty.call(this.parent, '__typename') const type = Object.prototype.hasOwnProperty.call(this.parent, '__typename')
? this.parent.__typename : 'ContentBlockNode'; ? this.parent.__typename
: 'ContentBlockNode';
this.$store.dispatch('addNote', { this.$store.dispatch('addNote', {
content: id, content: id,
type, type,
block: this.root block: this.root,
}); });
}, },
editNote() { editNote() {
@ -133,19 +139,18 @@ export default {
}, },
bookmarkContent(uuid, bookmarked) { bookmarkContent(uuid, bookmarked) {
this.$apollo.mutate(constructContentComponentBookmarkMutation(uuid, bookmarked, this.parent, this.root)); this.$apollo.mutate(constructContentComponentBookmarkMutation(uuid, bookmarked, this.parent, this.root));
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~styles/helpers"; @import '~styles/helpers';
.content-component { .content-component {
position: relative; position: relative;
&--bookmarked { &--bookmarked {
} }
&--subtitle { &--subtitle {

View File

@ -1,14 +1,7 @@
<template> <template>
<ol class="content-list"> <ol class="content-list">
<li <li class="content-list__item" v-for="(item, index) in items" :key="item.id">
class="content-list__item" <slot :item="item" :index="index">
v-for="(item, index) in items"
:key="item.id"
>
<slot
:item="item"
:index="index"
>
{{ item.id }} {{ item.id }}
</slot> </slot>
</li> </li>
@ -21,8 +14,8 @@
props: { props: {
items: { items: {
type: Array, type: Array,
default: () => ([]) default: () => [],
} },
}, },
}; };
</script> </script>

View File

@ -1,12 +1,7 @@
<template> <template>
<content-list <content-list :items="contentBlocks">
:items="contentBlocks"
>
<template #default="{ item }"> <template #default="{ item }">
<content-block <content-block :content-block="item" :parent="parent" />
:content-block="item"
:parent="parent"
/>
</template> </template>
</content-list> </content-list>
</template> </template>
@ -20,28 +15,26 @@
components: { components: {
ContentList, ContentList,
// https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components // https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
ContentBlock: () => import('@/components/ContentBlock.vue') ContentBlock: () => import('@/components/ContentBlock.vue'),
}, },
computed: { computed: {
contentBlocks() { contentBlocks() {
return this.contents.map(contentBlock => { return this.contents.map((contentBlock) => {
const contents = contentBlock.value ? [...contentBlock.value] : []; const contents = contentBlock.value ? [...contentBlock.value] : [];
return Object.assign({}, contentBlock, { return Object.assign({}, contentBlock, {
contents, contents,
indent: true, indent: true,
bookmarks: this.parent.bookmarks, bookmarks: this.parent.bookmarks,
notes: this.parent.notes, notes: this.parent.notes,
root: this.parent.id root: this.parent.id,
}); });
}); });
}
}, },
},
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
</style> </style>

View File

@ -1,16 +1,8 @@
<template> <template>
<div class="document-block"> <div class="document-block">
<document-icon class="document-block__icon" /> <document-icon class="document-block__icon" />
<a <a :href="value.url" class="document-block__link" target="_blank">{{ urlName }}</a>
:href="value.url" <a class="document-block__remove" v-if="showTrashIcon" @click="$emit('trash')">
class="document-block__link"
target="_blank"
>{{ urlName }}</a>
<a
class="document-block__remove"
v-if="showTrashIcon"
@click="$emit('trash')"
>
<trash-icon class="document-block__trash-icon" /> <trash-icon class="document-block__trash-icon" />
</a> </a>
</div> </div>
@ -38,13 +30,13 @@
return parts[parts.length - 1]; return parts[parts.length - 1];
} }
return null; return null;
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.document-block { .document-block {
display: grid; display: grid;

View File

@ -24,22 +24,22 @@
computed: { computed: {
src() { src() {
return `https://view.genial.ly/${this.value.id}`; return `https://view.genial.ly/${this.value.id}`;
} },
}, },
methods: { methods: {
openFullscreen() { openFullscreen() {
this.$store.dispatch('showFullscreenInfographic', { this.$store.dispatch('showFullscreenInfographic', {
id: this.value.id, id: this.value.id,
type: 'genially-block' type: 'genially-block',
}); });
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
// Styling and structure taken from original iframe // Styling and structure taken from original iframe
.genially-block { .genially-block {

View File

@ -1,10 +1,5 @@
<template> <template>
<img <img :src="value.path" alt="" class="image-block" @click="openFullscreen" />
:src="value.path"
alt=""
class="image-block"
@click="openFullscreen"
>
</template> </template>
<script> <script>
@ -13,14 +8,13 @@
methods: { methods: {
openFullscreen() { openFullscreen() {
this.$store.dispatch('showFullscreenImage', this.value.path); this.$store.dispatch('showFullscreenImage', this.value.path);
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.image-block { .image-block {
width: 100%; width: 100%;

View File

@ -1,10 +1,5 @@
<template> <template>
<img <img :src="value.url" alt="" class="image-block" @click="openFullscreen" />
:src="value.url"
alt=""
class="image-block"
@click="openFullscreen"
>
</template> </template>
<script> <script>
@ -13,9 +8,8 @@
methods: { methods: {
openFullscreen() { openFullscreen() {
this.$store.dispatch('showFullscreenImage', this.value.url); this.$store.dispatch('showFullscreenImage', this.value.url);
} },
} },
}; };
</script> </script>

View File

@ -7,7 +7,7 @@
class="infogram-block__iframe" class="infogram-block__iframe"
scrolling="no" scrolling="no"
frameborder="0" frameborder="0"
style="border:none;" style="border: none"
/> />
</div> </div>
</template> </template>
@ -18,7 +18,7 @@
data() { data() {
return { return {
height: 1 height: 1,
}; };
}, },
@ -34,12 +34,12 @@
}, },
title() { title() {
return this.value.title || 'Infografik'; return this.value.title || 'Infografik';
} },
}, },
mounted() { mounted() {
// from https://developers.infogr.am/oembed/ // from https://developers.infogr.am/oembed/
window.addEventListener('message', event => { window.addEventListener('message', (event) => {
try { try {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if (data.context === 'iframe.resize' && this.parseId(data.src) === this.id) { if (data.context === 'iframe.resize' && this.parseId(data.src) === this.id) {
@ -60,15 +60,15 @@
openFullscreen() { openFullscreen() {
this.$store.dispatch('showFullscreenInfographic', { this.$store.dispatch('showFullscreenInfographic', {
id: this.value.id, id: this.value.id,
type: 'infogram-block' type: 'infogram-block',
}); });
} },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
.infogram-block { .infogram-block {
margin-bottom: $large-spacing; margin-bottom: $large-spacing;

View File

@ -1,13 +1,7 @@
<template> <template>
<div <div class="instruction" v-if="me.isTeacher">
class="instruction"
v-if="me.isTeacher"
>
<bulb-icon class="instruction__icon" /> <bulb-icon class="instruction__icon" />
<a <a :href="url" class="instruction__link">{{ text }}</a>
:href="url"
class="instruction__link"
>{{ text }}</a>
</div> </div>
</template> </template>
@ -21,7 +15,7 @@
mixins: [me], mixins: [me],
components: { components: {
BulbIcon BulbIcon,
}, },
computed: { computed: {
@ -30,13 +24,13 @@
}, },
url() { url() {
return this.value.document ? this.value.document.url : this.value.url; return this.value.document ? this.value.document.url : this.value.url;
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
.instruction { .instruction {
margin-bottom: 1rem; margin-bottom: 1rem;

View File

@ -1,15 +1,12 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div class="instrument-widget"> <div class="instrument-widget">
<div <div class="instrument-widget__description" v-html="value.description" />
class="instrument-widget__description"
v-html="value.description"
/>
<router-link <router-link
:to="{ name: 'instrument', params: { slug: value.slug } }" :to="{ name: 'instrument', params: { slug: value.slug } }"
class="instrument-widget__button button" class="instrument-widget__button button"
:style="{ :style="{
borderColor: value.foreground borderColor: value.foreground,
}" }"
> >
{{ $flavor.textInstrument }} anzeigen {{ $flavor.textInstrument }} anzeigen
@ -25,7 +22,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/_variables.scss"; @import '~styles/_variables.scss';
.instrument-widget { .instrument-widget {
margin-bottom: $small-spacing; margin-bottom: $small-spacing;
@ -35,7 +32,6 @@
} }
&__button { &__button {
} }
} }
</style> </style>

View File

@ -1,14 +1,7 @@
<template> <template>
<div <div :class="{ 'link-block--no-margin': noMargin }" class="link-block">
:class="{ 'link-block--no-margin': noMargin}"
class="link-block"
>
<link-icon class="link-block__icon" /> <link-icon class="link-block__icon" />
<a <a :href="href" class="link-block__link" target="_blank">{{ value.text }}</a>
:href="href"
class="link-block__link"
target="_blank"
>{{ value.text }}</a>
</div> </div>
</template> </template>
@ -19,20 +12,20 @@
props: { props: {
value: Object, value: Object,
noMargin: { noMargin: {
default: false default: false,
} },
}, },
components: { components: {
LinkIcon LinkIcon,
}, },
computed: { computed: {
href() { href() {
const url = this.value.url; const url = this.value.url;
return url.startsWith('http') ? this.value.url : `http://${this.value.url}`; return url.startsWith('http') ? this.value.url : `http://${this.value.url}`;
} },
} },
}; };
</script> </script>

View File

@ -1,9 +1,6 @@
<template> <template>
<div class="module-slug"> <div class="module-slug">
<router-link <router-link :to="{ name: 'moduleRoom', params: { slug: value.slug } }" class="button button--primary">
:to="{name: 'moduleRoom', params: { slug: value.slug }}"
class="button button--primary"
>
Raum anzeigen Raum anzeigen
</router-link> </router-link>
</div> </div>
@ -11,12 +8,12 @@
<script> <script>
export default { export default {
props: ['value'] props: ['value'],
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
.module-slug { .module-slug {
margin-bottom: $large-spacing; margin-bottom: $large-spacing;

View File

@ -1,20 +1,17 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<h4 <h4 class="section-title" v-html="value.text" />
class="section-title"
v-html="value.text"
/>
</template> </template>
<script> <script>
export default { export default {
props: ['value'] props: ['value'],
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
.section-title { .section-title {
margin-bottom: 30px; margin-bottom: 30px;

View File

@ -1,34 +1,15 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div <div class="solution" data-cy="solution">
class="solution" <a class="solution__toggle" data-cy="show-solution" @click="toggle"
data-cy="solution"
>
<a
class="solution__toggle"
data-cy="show-solution"
@click="toggle"
>Lösung >Lösung
<template v-if="!visible">anzeigen</template> <template v-if="!visible">anzeigen</template>
<template v-else>ausblenden</template> <template v-else>ausblenden</template>
</a> </a>
<transition name="fade"> <transition name="fade">
<div <div class="solution__hidden fade" v-if="visible">
class="solution__hidden fade" <p class="solution__text solution-text" data-cy="solution-text" v-html="sanitizedText" />
v-if="visible" <cms-document-block :solution="true" class="solution__document" :value="value.document" v-if="value.document" />
>
<p
class="solution__text solution-text"
data-cy="solution-text"
v-html="sanitizedText"
/>
<cms-document-block
:solution="true"
class="solution__document"
:value="value.document"
v-if="value.document"
/>
</div> </div>
</transition> </transition>
</div> </div>
@ -63,7 +44,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.solution { .solution {
display: grid; display: grid;
@ -104,7 +85,7 @@
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity .3s; transition: opacity 0.3s;
} }
.fade-enter, .fade-enter,

View File

@ -1,10 +1,6 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<h5 <h5 class="subtitle" data-cy="subtitle-block" v-html="sanitizedText" />
class="subtitle"
data-cy="subtitle-block"
v-html="sanitizedText"
/>
</template> </template>
<script> <script>
@ -16,13 +12,13 @@
computed: { computed: {
sanitizedText() { sanitizedText() {
return sanitize(this.value.text); return sanitize(this.value.text);
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.subtitle { .subtitle {
padding-top: 1px; padding-top: 1px;

View File

@ -1,12 +1,6 @@
<template> <template>
<div <div :data-scrollto="value.id" class="survey-block">
:data-scrollto="value.id" <router-link :to="{ name: 'survey', params: { id: value.id } }" class="button button--primary">
class="survey-block"
>
<router-link
:to="{name: 'survey', params: {id:value.id}}"
class="button button--primary"
>
Übung anzeigen Übung anzeigen
</router-link> </router-link>
</div> </div>
@ -19,7 +13,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
.survey-block { .survey-block {
margin-bottom: $large-spacing; margin-bottom: $large-spacing;

View File

@ -1,21 +1,18 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div class="task"> <div class="task">
<div <div class="task__text" v-html="value.text" />
class="task__text"
v-html="value.text"
/>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
props: ['value'] props: ['value'],
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
.task { .task {
margin-bottom: 30px; margin-bottom: 30px;

View File

@ -1,10 +1,6 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div <div class="text-block" data-cy="text-block" v-html="sanitizedText" />
class="text-block"
data-cy="text-block"
v-html="sanitizedText"
/>
</template> </template>
<script> <script>
@ -15,13 +11,13 @@
sanitizedText() { sanitizedText() {
// don't need to sanitize the input, server does this // don't need to sanitize the input, server does this
return this.value.text; return this.value.text;
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.text-block { .text-block {
margin-bottom: $medium-spacing; // if calc is not supported margin-bottom: $medium-spacing; // if calc is not supported

View File

@ -26,22 +26,22 @@
computed: { computed: {
src() { src() {
return `https://www.thinglink.com/card/${this.value.id}`; return `https://www.thinglink.com/card/${this.value.id}`;
} },
}, },
methods: { methods: {
openFullscreen() { openFullscreen() {
this.$store.dispatch('showFullscreenInfographic', { this.$store.dispatch('showFullscreenInfographic', {
id: this.value.id, id: this.value.id,
type: 'thinglink-block' type: 'thinglink-block',
}); });
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
// Styling and structure taken from original iframe // Styling and structure taken from original iframe
.thinglink-block { .thinglink-block {

View File

@ -1,17 +1,8 @@
<template> <template>
<div class="video-block"> <div class="video-block">
<youtube-embed <youtube-embed :url="value.url" v-if="isYoutube" />
:url="value.url" <vimeo-embed :url="value.url" v-if="isVimeo" />
v-if="isYoutube" <srf-embed :url="value.url" v-if="isSrf" />
/>
<vimeo-embed
:url="value.url"
v-if="isVimeo"
/>
<srf-embed
:url="value.url"
v-if="isSrf"
/>
</div> </div>
</template> </template>
@ -27,7 +18,7 @@
components: { components: {
YoutubeEmbed, YoutubeEmbed,
VimeoEmbed, VimeoEmbed,
SrfEmbed SrfEmbed,
}, },
computed: { computed: {
@ -39,8 +30,8 @@
}, },
isSrf() { isSrf() {
return isSrfUrl(this.value.url); return isSrfUrl(this.value.url);
} },
} },
}; };
</script> </script>

View File

@ -1,19 +1,9 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div <div :data-scrollto="value.id" class="assignment">
:data-scrollto="value.id" <p class="assignment__main-text" data-cy="assignment-main-text" v-html="assignment.assignment" />
class="assignment"
>
<p
class="assignment__main-text"
data-cy="assignment-main-text"
v-html="assignment.assignment"
/>
<solution <solution :value="solution" v-if="assignment.solution" />
:value="solution"
v-if="assignment.solution"
/>
<template v-if="isStudent"> <template v-if="isStudent">
<submission-form <submission-form
@ -33,24 +23,13 @@
@spellcheck="spellcheck" @spellcheck="spellcheck"
/> />
<spell-check <spell-check :corrections="corrections" :text="submission.text" />
:corrections="corrections"
:text="submission.text"
/>
<p <p class="assignment__feedback" v-if="assignment.submission.submissionFeedback" v-html="feedbackText" />
class="assignment__feedback"
v-if="assignment.submission.submissionFeedback"
v-html="feedbackText"
/>
</template> </template>
<template v-if="!isStudent"> <template v-if="!isStudent">
<router-link <router-link :to="{ name: 'submissions', params: { id: assignment.id } }" class="button button--primary">
:to="{name: 'submissions', params: { id: assignment.id }}" Zu den Ergebnissen
class="button button--primary"
>
Zu den
Ergebnissen
</router-link> </router-link>
</template> </template>
</div> </div>
@ -67,9 +46,11 @@
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import { sanitize } from '@/helpers/text'; import { sanitize } from '@/helpers/text';
const SubmissionForm = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/assignment/SubmissionForm'); const SubmissionForm = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/assignment/SubmissionForm');
const Solution = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Solution'); const Solution = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Solution');
const SpellCheck = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/assignment/SpellCheck'); const SpellCheck = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/assignment/SpellCheck');
export default { export default {
props: ['value'], props: ['value'],
@ -126,7 +107,8 @@
...mapActions(['scrollToAssignmentReady']), ...mapActions(['scrollToAssignmentReady']),
_save: debounce(function (submission) { _save: debounce(function (submission) {
this.saving++; this.saving++;
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS, mutation: UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS,
variables: { variables: {
input: { input: {
@ -137,7 +119,14 @@
}, },
}, },
}, },
update(store, {data: {updateAssignment: {successful, updatedAssignment}}}) { update(
store,
{
data: {
updateAssignment: { successful, updatedAssignment },
},
}
) {
try { try {
if (successful) { if (successful) {
const query = ASSIGNMENT_QUERY; const query = ASSIGNMENT_QUERY;
@ -148,7 +137,7 @@
submission, submission,
}); });
const data = { const data = {
assignment assignment,
}; };
store.writeQuery({ query, variables, data }); store.writeQuery({ query, variables, data });
} }
@ -157,7 +146,8 @@
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing // Query did not exist in the cache, and apollo throws a generic Error. Do nothing
} }
}, },
}).then(() => { })
.then(() => {
this.saving--; this.saving--;
if (this.saving === 0) { if (this.saving === 0) {
this.unsaved = false; this.unsaved = false;
@ -221,7 +211,8 @@
spellcheck() { spellcheck() {
let self = this; let self = this;
this.spellcheckLoading = true; this.spellcheckLoading = true;
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: SPELL_CHECK_MUTATION, mutation: SPELL_CHECK_MUTATION,
variables: { variables: {
input: { input: {
@ -229,10 +220,18 @@
text: this.assignment.submission.text, text: this.assignment.submission.text,
}, },
}, },
update(store, {data: {spellCheck: {results}}}) { update(
store,
{
data: {
spellCheck: { results },
},
}
) {
self.corrections = results; self.corrections = results;
}, },
}).then(() => { })
.then(() => {
this.spellcheckLoading = false; this.spellcheckLoading = false;
}); });
}, },
@ -278,7 +277,7 @@
&__main-text { &__main-text {
:deep(ul) { :deep(ul) {
@include list-parent @include list-parent;
} }
:deep(li) { :deep(li) {
@ -312,7 +311,5 @@
&__feedback { &__feedback {
@include regular-text; @include regular-text;
} }
} }
</style> </style>

View File

@ -1,29 +1,20 @@
<template> <template>
<div <div class="final-submission" data-cy="final-submission">
class="final-submission" <document-block :value="{ url: userInput.document }" class="final-submission__document" v-if="userInput.document" />
data-cy="final-submission"
>
<document-block
:value="{url: userInput.document}"
class="final-submission__document"
v-if="userInput.document"
/>
<div class="final-submission__explanation"> <div class="final-submission__explanation">
<info-icon class="final-submission__explanation-icon" /> <info-icon class="final-submission__explanation-icon" />
<span class="final-submission__explanation-text">{{ sharedMsg }}</span> <span class="final-submission__explanation-text">{{ sharedMsg }}</span>
<a <a class="final-submission__reopen" data-cy="final-submission-reopen" v-if="showReopen" @click="$emit('reopen')"
class="final-submission__reopen" >Bearbeiten</a
data-cy="final-submission-reopen" >
v-if="showReopen"
@click="$emit('reopen')"
>Bearbeiten</a>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { newLineToParagraph } from '@/helpers/text'; import { newLineToParagraph } from '@/helpers/text';
const DocumentBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'); const DocumentBlock = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/DocumentBlock');
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon'); const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon');
@ -31,16 +22,16 @@
props: { props: {
userInput: { userInput: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
showReopen: { showReopen: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
sharedMsg: { sharedMsg: {
type: String, type: String,
default: '' default: '',
} },
}, },
components: { components: {
@ -51,13 +42,13 @@
computed: { computed: {
text() { text() {
return newLineToParagraph(this.userInput.text); return newLineToParagraph(this.userInput.text);
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.final-submission { .final-submission {
&__text { &__text {

View File

@ -1,9 +1,6 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<p <p class="spellcheck" v-if="corrections">
class="spellcheck"
v-if="corrections"
>
<span class="inline-title">Rechtschreibung:</span> <span v-html="highlightedText" /> <span class="inline-title">Rechtschreibung:</span> <span v-html="highlightedText" />
</p> </p>
</template> </template>
@ -20,24 +17,27 @@
let parts = []; let parts = [];
let index = 0; let index = 0;
[...this.corrections] // no side effects, as sort changes the source array [...this.corrections] // no side effects, as sort changes the source array
.sort((e1, e2) => (e1.offset + e1.sentenceOffset) - (e2.offset + e2.sentenceOffset)) .sort((e1, e2) => e1.offset + e1.sentenceOffset - (e2.offset + e2.sentenceOffset))
.forEach(current => { .forEach((current) => {
let realOffset = current.offset + current.sentenceOffset; let realOffset = current.offset + current.sentenceOffset;
parts.push({ parts.push(
{
correct: true, correct: true,
text: this.text.substring(index, realOffset) text: this.text.substring(index, realOffset),
}, { },
{
correct: false, correct: false,
text: this.text.substring(realOffset, realOffset + current.length) text: this.text.substring(realOffset, realOffset + current.length),
}); }
);
index = realOffset + current.length; index = realOffset + current.length;
}); });
parts.push({ parts.push({
correct: true, correct: true,
text: this.text.substring(index, this.text.length + 1) text: this.text.substring(index, this.text.length + 1),
}); });
return parts return parts
.filter(part => part.text.length) .filter((part) => part.text.length)
.reduce((previous, part) => { .reduce((previous, part) => {
if (part.correct) { if (part.correct) {
return `${previous}${part.text}`; return `${previous}${part.text}`;
@ -45,13 +45,13 @@
return `${previous}<span data-cy="spellcheck-correction" class="spellcheck__correction">${part.text}</span>`; return `${previous}<span data-cy="spellcheck-correction" class="spellcheck__correction">${part.text}</span>`;
} }
}, ''); }, '');
} },
} },
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
.spellcheck { .spellcheck {
@include regular-text; @include regular-text;

View File

@ -10,10 +10,7 @@
/> />
</div> </div>
<div <div class="submission-form-container__actions" v-if="!isFinalOrReadOnly">
class="submission-form-container__actions"
v-if="!isFinalOrReadOnly"
>
<button <button
class="submission-form-container__submit button button--primary button--white-bg" class="submission-form-container__submit button button--primary button--white-bg"
data-cy="submission-form-submit" data-cy="submission-form-submit"
@ -29,11 +26,7 @@
> >
{{ spellcheckText }} {{ spellcheckText }}
</button> </button>
<file-upload <file-upload :document="userInput.document" v-if="allowsDocuments" @change-document-url="changeDocumentUrl" />
:document="userInput.document"
v-if="allowsDocuments"
@change-document-url="changeDocumentUrl"
/>
<slot /> <slot />
</div> </div>
@ -52,7 +45,6 @@
const FinalSubmission = () => import('@/components/content-blocks/assignment/FinalSubmission.vue'); const FinalSubmission = () => import('@/components/content-blocks/assignment/FinalSubmission.vue');
const FileUpload = () => import('@/components/ui/file-upload/FileUpload.vue'); const FileUpload = () => import('@/components/ui/file-upload/FileUpload.vue');
export default { export default {
props: { props: {
userInput: Object, userInput: Object,
@ -115,7 +107,6 @@
this.$emit('changeDocumentUrl', documentUrl); this.$emit('changeDocumentUrl', documentUrl);
}, },
}, },
}; };
</script> </script>
@ -158,5 +149,4 @@
display: inline-block; display: inline-block;
} }
} }
</style> </style>

View File

@ -11,16 +11,10 @@
v-auto-grow v-auto-grow
@input="$emit('input', $event.target.value)" @input="$emit('input', $event.target.value)"
/> />
<div <div class="submission-form__save-status submission-form__save-status--saved" v-if="saved">
class="submission-form__save-status submission-form__save-status--saved"
v-if="saved"
>
<tick-circle-icon class="submission-form__save-status-icon" /> <tick-circle-icon class="submission-form__save-status-icon" />
</div> </div>
<div <div class="submission-form__save-status submission-form__save-status--unsaved" v-if="!saved">
class="submission-form__save-status submission-form__save-status--unsaved"
v-if="!saved"
>
<loading-icon class="submission-form__save-status-icon submission-form__saving-icon" /> <loading-icon class="submission-form__save-status-icon submission-form__saving-icon" />
</div> </div>
</div> </div>
@ -37,18 +31,18 @@
readonly: Boolean, readonly: Boolean,
placeholder: { placeholder: {
type: String, type: String,
default: 'Ergebnis erfassen' default: 'Ergebnis erfassen',
} },
}, },
components: { components: {
TickCircleIcon, TickCircleIcon,
LoadingIcon LoadingIcon,
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.submission-form { .submission-form {
display: flex; display: flex;

View File

@ -5,7 +5,7 @@
class="assignment-form__title skillbox-input" class="assignment-form__title skillbox-input"
placeholder="Aufgabentitel" placeholder="Aufgabentitel"
@input="$emit('assignment-change-title', $event.target.value, index)" @input="$emit('assignment-change-title', $event.target.value, index)"
> />
<textarea <textarea
:value="value.assignment" :value="value.assignment"
class="assignment-form__exercise-text skillbox-textarea" class="assignment-form__exercise-text skillbox-textarea"
@ -26,12 +26,12 @@
props: ['value', 'index'], props: ['value', 'index'],
components: { components: {
InfoIcon InfoIcon,
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.assignment-form { .assignment-form {
display: grid; display: grid;

View File

@ -1,13 +1,6 @@
<template> <template>
<div <div :class="['chooser-element', subclass]" :data-cy="cy" @click="$emit('select')">
:class="['chooser-element', subclass]" <component class="chooser-element__icon" :is="icon" />
:data-cy="cy"
@click="$emit('select')"
>
<component
class="chooser-element__icon"
:is="icon"
/>
<div class="chooser-element__title"> <div class="chooser-element__title">
{{ title }} {{ title }}
</div> </div>
@ -32,7 +25,7 @@
title: { title: {
type: String, type: String,
default() { default() {
return this.type.replace(/^\w/, c => c.toUpperCase()); return this.type.replace(/^\w/, (c) => c.toUpperCase());
}, },
}, },
}, },
@ -72,5 +65,4 @@
align-self: end; align-self: end;
} }
} }
</style> </style>

View File

@ -1,20 +1,10 @@
<template> <template>
<div class="content-block-element-chooser-widget__wrapper"> <div class="content-block-element-chooser-widget__wrapper">
<button <button class="content-block-element-chooser-widget__remove-button icon-button" @click="remove">
class="content-block-element-chooser-widget__remove-button icon-button"
@click="remove"
>
<cross-icon class="icon-button__icon" /> <cross-icon class="icon-button__icon" />
</button> </button>
<h3 <h3 class="content-block-element-chooser-widget__heading" data-cy="chooser-heading">Neuer Inhalt</h3>
class="content-block-element-chooser-widget__heading" <template v-if="includeListOption && hasDefaultFeatures">
data-cy="chooser-heading"
>
Neuer Inhalt
</h3>
<template
v-if="includeListOption && hasDefaultFeatures"
>
<checkbox <checkbox
class="content-block-element-chooser-widget__list-toggle" class="content-block-element-chooser-widget__list-toggle"
:checked="convertToList" :checked="convertToList"
@ -48,7 +38,6 @@
import ChooserElement from '@/components/content-forms/ChooserElement'; import ChooserElement from '@/components/content-forms/ChooserElement';
import { DEFAULT_FEATURE_SET } from '@/consts/features.consts'; import { DEFAULT_FEATURE_SET } from '@/consts/features.consts';
export default { export default {
props: { props: {
element: {}, element: {},
@ -107,27 +96,25 @@
block: 'assignment', block: 'assignment',
icon: 'speech-bubble-icon', icon: 'speech-bubble-icon',
title: 'Aufgabe & Ergebnis', title: 'Aufgabe & Ergebnis',
show: !this.hideAssignment && hasDefaultFeatures show: !this.hideAssignment && hasDefaultFeatures,
}, },
{ {
type: 'document', type: 'document',
block: 'document_block', block: 'document_block',
title: 'Dokument', title: 'Dokument',
show: hasDefaultFeatures show: hasDefaultFeatures,
}, },
], ],
}; };
}, },
computed: { computed: {
filteredChooserTypes() { filteredChooserTypes() {
return this.chooserTypes.filter(type => !("show" in type) || type.show ); // display element if `show` is not set or if `show` evaluates to true return this.chooserTypes.filter((type) => !('show' in type) || type.show); // display element if `show` is not set or if `show` evaluates to true
}, },
hasDefaultFeatures() { hasDefaultFeatures() {
return this.features === DEFAULT_FEATURE_SET; return this.features === DEFAULT_FEATURE_SET;
} },
}, },
methods: { methods: {
@ -145,7 +132,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.content-block-element-chooser-widget { .content-block-element-chooser-widget {
display: -ms-grid; display: -ms-grid;
@ -216,7 +203,5 @@
grid-column: 2; grid-column: 2;
grid-row: 1; grid-row: 1;
} }
} }
</style> </style>

View File

@ -1,28 +1,12 @@
<template> <template>
<div <div class="document-form" ref="documentform">
class="document-form" <div v-if="!value.url" ref="uploadcare-panel" />
ref="documentform" <div class="document-form__spinner" v-if="loading">
>
<div
v-if="!value.url"
ref="uploadcare-panel"
/>
<div
class="document-form__spinner"
v-if="loading"
>
<loading-icon class="document-form__loading-icon" /> <loading-icon class="document-form__loading-icon" />
</div> </div>
<div <div class="document-form__uploaded" v-if="value.url">
class="document-form__uploaded"
v-if="value.url"
>
<document-icon class="document-form__icon" /> <document-icon class="document-form__icon" />
<a <a :href="previewUrl" class="document-form__link" target="_blank">{{ previewLink }}</a>
:href="previewUrl"
class="document-form__link"
target="_blank"
>{{ previewLink }}</a>
</div> </div>
</div> </div>
</template> </template>
@ -64,19 +48,22 @@
}, },
mounted() { mounted() {
uploadcare(this, url => { uploadcare(
this,
(url) => {
this.$emit('change-url', url, this.index); this.$emit('change-url', url, this.index);
this.loading = false; this.loading = false;
}, () => { },
() => {
this.loading = true; this.loading = true;
}
}); );
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.document-form { .document-form {
&__uploaded { &__uploaded {

View File

@ -1,32 +1,17 @@
<template> <template>
<div class="image-form"> <div class="image-form">
<div <div class="image-form__error" v-if="hadError">
class="image-form__error" Ups, das scheint kein Bild zu sein. Bitte versuche es nochmal mit einer anderen Datei, oder lade die Datei als
v-if="hadError" <a class="image-form__link" @click="switchToDocument">Dokument</a> hoch.
>
Ups, das scheint kein Bild zu sein. Bitte versuche es nochmal mit einer anderen Datei, oder lade die Datei als <a
class="image-form__link"
@click="switchToDocument"
>Dokument</a> hoch.
</div> </div>
<div <div class="image-form__spinner" v-if="loading">
class="image-form__spinner"
v-if="loading"
>
<loading-icon class="image-form__loading-icon" /> <loading-icon class="image-form__loading-icon" />
</div> </div>
<div <div v-if="!value.url || hadError" ref="uploadcare-panel" />
v-if="!value.url || hadError"
ref="uploadcare-panel"
/>
<div v-if="value.url && !hadError"> <div v-if="value.url && !hadError">
<img <img alt="" :src="previewUrl" @error="error" />
alt=""
:src="previewUrl"
@error="error"
>
</div> </div>
</div> </div>
</template> </template>
@ -71,9 +56,9 @@
tabs: ['file'], tabs: ['file'],
}); });
this.uploadcarePanel.done(panelResult => { this.uploadcarePanel.done((panelResult) => {
this.loading = true; this.loading = true;
panelResult.done(fileInfo => { panelResult.done((fileInfo) => {
this.hadError = false; this.hadError = false;
this.loading = false; this.loading = false;
this.url = fileInfo.cdnUrl; this.url = fileInfo.cdnUrl;
@ -96,7 +81,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.image-form { .image-form {
&__error { &__error {

View File

@ -5,25 +5,25 @@
placeholder="Name erfassen..." placeholder="Name erfassen..."
class="link-form__text skillbox-input" class="link-form__text skillbox-input"
@input="$emit('change-text', $event.target.value, index)" @input="$emit('change-text', $event.target.value, index)"
> />
<input <input
:value="value.url" :value="value.url"
placeholder="URL einfügen..." placeholder="URL einfügen..."
class="link-form__url skillbox-input" class="link-form__url skillbox-input"
@input="$emit('change-url', $event.target.value, index)" @input="$emit('change-url', $event.target.value, index)"
> />
</div> </div>
</template> </template>
<script> <script>
export default { export default {
props: ['value', 'index'] props: ['value', 'index'],
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.link-form { .link-form {
display: grid; display: grid;

View File

@ -21,25 +21,25 @@
default: null, default: null,
validator(value) { validator(value) {
return Object.prototype.hasOwnProperty.call(value, 'text'); return Object.prototype.hasOwnProperty.call(value, 'text');
} },
}, },
index: { index: {
type: Number, type: Number,
default: -1 default: -1,
} },
}, },
components: { InputWithLabel }, components: { InputWithLabel },
computed: { computed: {
text() { text() {
return this.value.text; return this.value.text;
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.subtitle-form { .subtitle-form {
&__input { &__input {

View File

@ -18,24 +18,24 @@
default: null, default: null,
validator(value) { validator(value) {
return Object.prototype.hasOwnProperty.call(value, 'text'); return Object.prototype.hasOwnProperty.call(value, 'text');
} },
}, },
index: { index: {
type: Number, type: Number,
default: -1 default: -1,
} },
}, },
computed: { computed: {
text() { text() {
return this.value.text ? this.value.text.replace(/<br(\/)?>/, '\n').replace(/(<([^>]+)>)/ig, '') : ''; return this.value.text ? this.value.text.replace(/<br(\/)?>/, '\n').replace(/(<([^>]+)>)/gi, '') : '';
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.text-form { .text-form {
&__input { &__input {

View File

@ -4,10 +4,7 @@
<span class="text-form-with-help-text__title">{{ title }}</span> <span class="text-form-with-help-text__title">{{ title }}</span>
<helpful-tooltip :text="helpText" /> <helpful-tooltip :text="helpText" />
</h3> </h3>
<text-form <text-form :value="v" @change-text="$emit('change', $event)" />
:value="v"
@change-text="$emit('change', $event)"
/>
</div> </div>
</template> </template>
@ -20,22 +17,22 @@
components: { components: {
TextForm, TextForm,
HelpfulTooltip HelpfulTooltip,
}, },
computed: { computed: {
v() { v() {
return { return {
text: this.value text: this.value,
}; };
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_functions.scss"; @import '@/styles/_functions.scss';
.text-form-with-help-text { .text-form-with-help-text {
margin-bottom: 30px; margin-bottom: 30px;

View File

@ -1,28 +1,20 @@
<template> <template>
<div class="tip-tap"> <div class="tip-tap">
<editor-content <editor-content class="tip-tap__editor-wrapper" :editor="editor" />
class="tip-tap__editor-wrapper"
:editor="editor"
/>
<toggle <toggle :bordered="false" :checked="isList" label="Als Liste formatieren" @input="toggleList" />
:bordered="false"
:checked="isList"
label="Als Liste formatieren"
@input="toggleList"
/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue, { PropType } from 'vue'; import Vue, { PropType } from 'vue';
import {Editor, EditorContent} from "@tiptap/vue-2"; import { Editor, EditorContent } from '@tiptap/vue-2';
import Document from '@tiptap/extension-document'; import Document from '@tiptap/extension-document';
import Paragraph from '@tiptap/extension-paragraph'; import Paragraph from '@tiptap/extension-paragraph';
import Text from '@tiptap/extension-text'; import Text from '@tiptap/extension-text';
import BulletList from '@tiptap/extension-bullet-list'; import BulletList from '@tiptap/extension-bullet-list';
import ListItem from '@tiptap/extension-list-item'; import ListItem from '@tiptap/extension-list-item';
import Toggle from "@/components/ui/Toggle.vue"; import Toggle from '@/components/ui/Toggle.vue';
interface Data { interface Data {
editor: Editor | undefined; editor: Editor | undefined;
@ -43,7 +35,7 @@
components: { components: {
Toggle, Toggle,
EditorContent EditorContent,
}, },
data(): Data { data(): Data {
@ -58,7 +50,7 @@
}, },
text(): string { text(): string {
return this.value.text; return this.value.text;
} },
}, },
watch: { watch: {
@ -71,29 +63,23 @@
} }
editor.commands.setContent(text, false); editor.commands.setContent(text, false);
} },
}, },
mounted() { mounted() {
this.editor = new Editor({ this.editor = new Editor({
editorProps: { editorProps: {
attributes: { attributes: {
class: 'tip-tap__editor' class: 'tip-tap__editor',
} },
}, },
content: this.text, content: this.text,
extensions: [ extensions: [Document, Paragraph, Text, BulletList, ListItem],
Document,
Paragraph,
Text,
BulletList,
ListItem
],
onUpdate: () => { onUpdate: () => {
const text = (this.editor as Editor).getHTML(); const text = (this.editor as Editor).getHTML();
this.$emit('input', text); this.$emit('input', text);
this.$emit('change-text', text); this.$emit('change-text', text);
} },
}); });
}, },
@ -106,16 +92,14 @@
const editor = this.editor as Editor; const editor = this.editor as Editor;
editor.chain().selectAll().toggleBulletList().run(); editor.chain().selectAll().toggleBulletList().run();
}, },
} },
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import '~styles/helpers';
.tip-tap { .tip-tap {
&__editor-wrapper { &__editor-wrapper {
margin-bottom: $medium-spacing; margin-bottom: $medium-spacing;
} }

View File

@ -1,21 +1,11 @@
<template> <template>
<div> <div>
<div <div class="video-form" v-if="!isVimeo && !isYoutube && !isSrf">
class="video-form"
v-if="!isVimeo && !isYoutube && !isSrf"
>
<info-icon class="video-form__help-icon help-text__icon" /> <info-icon class="video-form__help-icon help-text__icon" />
<p class="video-form__help-description help-text__description"> <p class="video-form__help-description help-text__description">
Sie können Videos auf <a Sie können Videos auf
class="video-form__platform-link help-text__link" <a class="video-form__platform-link help-text__link" href="https://youtube.com/" target="_blank">Youtube</a>
href="https://youtube.com/" oder <a class="video-form__platform-link help-text__link" href="https://vimeo.com/" target="_blank">Vimeo</a>
target="_blank"
>Youtube</a>
oder <a
class="video-form__platform-link help-text__link"
href="https://vimeo.com/"
target="_blank"
>Vimeo</a>
hochladen und anschliessen einen Link hier einfügen. hochladen und anschliessen einen Link hier einfügen.
</p> </p>
@ -24,7 +14,7 @@
class="video-form__video-link skillbox-input" class="video-form__video-link skillbox-input"
placeholder="Bsp: https://www.youtube.com/watch?v=dQw4w9WgXcQ" placeholder="Bsp: https://www.youtube.com/watch?v=dQw4w9WgXcQ"
@input="$emit('change-url', $event.target.value, index)" @input="$emit('change-url', $event.target.value, index)"
> />
</div> </div>
<div v-if="isYoutube"> <div v-if="isYoutube">
@ -53,7 +43,7 @@
InfoIcon, InfoIcon,
YoutubeEmbed, YoutubeEmbed,
VimeoEmbed, VimeoEmbed,
SrfEmbed SrfEmbed,
}, },
computed: { computed: {
@ -65,14 +55,14 @@
}, },
isSrf() { isSrf() {
return isSrfUrl(this.value.url); return isSrfUrl(this.value.url);
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_functions.scss"; @import '@/styles/_functions.scss';
.video-form { .video-form {
display: grid; display: grid;
@ -83,11 +73,9 @@
align-items: center; align-items: center;
&__help-icon { &__help-icon {
} }
&__help-description { &__help-description {
} }
&__platform-link { &__platform-link {
@ -99,7 +87,7 @@
&__video-link { &__video-link {
grid-column: 1 / span 2; grid-column: 1 / span 2;
width: $modal-input-width width: $modal-input-width;
} }
} }
</style> </style>

Some files were not shown because too many files have changed in this diff Show More