Merge branch 'feature/vue3-upgrade-2023-01-25' into develop
This commit is contained in:
commit
300cb8681f
|
|
@ -42,7 +42,7 @@ module.exports = {
|
||||||
'@': resolve('src'),
|
'@': resolve('src'),
|
||||||
styles: resolve('src/styles'),
|
styles: resolve('src/styles'),
|
||||||
gql: resolve('src/graphql/gql'),
|
gql: resolve('src/graphql/gql'),
|
||||||
// vue: '@vue/compat',
|
vue: '@vue/compat',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
|
|
@ -60,7 +60,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
compatConfig: {
|
compatConfig: {
|
||||||
MODE: 2,
|
MODE: 2
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import module from '../../../fixtures/module.minimal';
|
import module from '../../../fixtures/module.minimal';
|
||||||
import { getMinimalMe } from '../../../support/helpers';
|
import {getMinimalMe} from '../../../support/helpers';
|
||||||
import { hasOperationName } from '../../../support/graphql';
|
import {hasOperationName} from '../../../support/graphql';
|
||||||
|
|
||||||
let snapshotTitle;
|
let snapshotTitle;
|
||||||
let deleteSuccess;
|
let deleteSuccess;
|
||||||
let page;
|
|
||||||
|
|
||||||
const moduleWithSnapshots = {
|
const moduleWithSnapshots = {
|
||||||
...module,
|
...module,
|
||||||
|
|
@ -28,68 +27,71 @@ const moduleWithSnapshots = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockDeleteSnapshot = (success) => {
|
// const mockDeleteSnapshot = (success) => {
|
||||||
cy.intercept('POST', '/api/graphql', (req) => {
|
// cy.intercept('POST', '/api/graphql', (req) => {
|
||||||
if (hasOperationName(req, 'DeleteSnapshot')) {
|
// if (hasOperationName(req, 'DeleteSnapshot')) {
|
||||||
let result;
|
// let result;
|
||||||
if (success) {
|
// if (success) {
|
||||||
result = {
|
// result = {
|
||||||
message: 'yay!',
|
// message: 'yay!',
|
||||||
__typename: 'Success',
|
// __typename: 'Success',
|
||||||
};
|
// };
|
||||||
} else {
|
// } else {
|
||||||
result = {
|
// result = {
|
||||||
reason: 'Not the owner',
|
// reason: 'Not the owner',
|
||||||
__typename: 'NotOwner',
|
// __typename: 'NotOwner',
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
req.reply({
|
// req.reply({
|
||||||
data: {
|
// data: {
|
||||||
deleteSnapshot: {
|
// deleteSnapshot: {
|
||||||
result,
|
// result,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
}
|
//
|
||||||
});
|
// }
|
||||||
};
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
const mockUpdateSnapshot = (title) => {
|
// const mockUpdateSnapshot = (title) => {
|
||||||
cy.intercept('POST', '/api/graphql', (req) => {
|
// cy.intercept('POST', '/api/graphql', (req) => {
|
||||||
if (hasOperationName(req, 'UpdateSnapshot')) {
|
// if (hasOperationName(req, 'UpdateSnapshot')) {
|
||||||
let snapshot;
|
// let snapshot;
|
||||||
if (title) {
|
// if (title) {
|
||||||
snapshot = {
|
// snapshot = {
|
||||||
__typename: 'SnapshotNode',
|
// __typename: 'SnapshotNode',
|
||||||
id: 'U25hcHNob3ROb2RlOjQ=',
|
// id: 'U25hcHNob3ROb2RlOjQ=',
|
||||||
title,
|
// title,
|
||||||
};
|
// };
|
||||||
} else {
|
// } else {
|
||||||
snapshot = {
|
// snapshot = {
|
||||||
__typename: 'NotOwner',
|
// __typename: 'NotOwner',
|
||||||
reason: 'Not the owner',
|
// reason: 'Not the owner',
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
req.reply({
|
// req.reply({
|
||||||
data: {
|
// data: {
|
||||||
updateSnapshot: {
|
// updateSnapshot: {
|
||||||
snapshot,
|
// snapshot,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
};
|
//
|
||||||
|
// };
|
||||||
|
|
||||||
// wait for the specified amount of requests in the test, so they don't spill over to the next test
|
// wait for the specified amount of requests in the test, so they don't spill over to the next test
|
||||||
const waitForNRequests = (n) => {
|
const waitNTimes = (n) => {
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
cy.wait('@graphqlRequest');
|
cy.wait('@graphqlRequest');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
describe('Snapshot', () => {
|
describe('Snapshot', () => {
|
||||||
const operations = (isTeacher) => ({
|
const operations = isTeacher => ({
|
||||||
operations: {
|
operations: {
|
||||||
UpdateSnapshot: {
|
UpdateSnapshot: {
|
||||||
updateSnapshot: {
|
updateSnapshot: {
|
||||||
|
|
@ -127,22 +129,21 @@ describe('Snapshot', () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MeQuery: getMinimalMe({ isTeacher }),
|
MeQuery: getMinimalMe({isTeacher}),
|
||||||
ModuleDetailsQuery: {
|
ModuleDetailsQuery: {
|
||||||
module,
|
module,
|
||||||
},
|
},
|
||||||
CreateSnapshot: {
|
CreateSnapshot: {
|
||||||
createSnapshot: {
|
createSnapshot: {
|
||||||
snapshot: {
|
snapshot: {
|
||||||
id: 'snapshot-id',
|
id: '',
|
||||||
title: 'Mi Snapshot',
|
title: '',
|
||||||
created: '2022-01-01',
|
created: '',
|
||||||
creator: 'me',
|
creator: '',
|
||||||
shared: false,
|
|
||||||
mine: true,
|
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
|
|
@ -178,7 +179,6 @@ describe('Snapshot', () => {
|
||||||
},
|
},
|
||||||
SnapshotDetail: {
|
SnapshotDetail: {
|
||||||
snapshot: {
|
snapshot: {
|
||||||
title: 'Shared snapshot',
|
|
||||||
chapters: [],
|
chapters: [],
|
||||||
module: {},
|
module: {},
|
||||||
},
|
},
|
||||||
|
|
@ -194,7 +194,6 @@ describe('Snapshot', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
snapshotTitle = false;
|
snapshotTitle = false;
|
||||||
deleteSuccess = false;
|
deleteSuccess = false;
|
||||||
page = moduleWithSnapshots;
|
|
||||||
cy.setup();
|
cy.setup();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -202,7 +201,7 @@ describe('Snapshot', () => {
|
||||||
cy.mockGraphqlOps(operations(true));
|
cy.mockGraphqlOps(operations(true));
|
||||||
cy.visit('module/miteinander-reden/');
|
cy.visit('module/miteinander-reden/');
|
||||||
cy.getByDataCy('snapshot-menu').should('be.visible');
|
cy.getByDataCy('snapshot-menu').should('be.visible');
|
||||||
waitForNRequests(4);
|
waitNTimes(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Menu is not visible for student', () => {
|
it('Menu is not visible for student', () => {
|
||||||
|
|
@ -211,27 +210,19 @@ describe('Snapshot', () => {
|
||||||
|
|
||||||
cy.getByDataCy('module-title').should('be.visible');
|
cy.getByDataCy('module-title').should('be.visible');
|
||||||
cy.getByDataCy('snapshot-menu').should('not.exist');
|
cy.getByDataCy('snapshot-menu').should('not.exist');
|
||||||
waitForNRequests(3);
|
waitNTimes(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Creates Snapshot', () => {
|
it('Creates Snapshot', () => {
|
||||||
cy.mockGraphqlOps(operations(true));
|
cy.mockGraphqlOps(operations(true));
|
||||||
cy.visit('module/miteinander-reden/snapshots');
|
cy.visit('module/miteinander-reden/');
|
||||||
cy.getByDataCy('snapshot-list')
|
|
||||||
.should('exist')
|
|
||||||
.within(() => {
|
|
||||||
cy.get('.snapshots__snapshot').should('have.length', 1);
|
|
||||||
});
|
|
||||||
cy.getByDataCy('back-link').click();
|
|
||||||
cy.getByDataCy('module-snapshots-button').click();
|
cy.getByDataCy('module-snapshots-button').click();
|
||||||
cy.getByDataCy('create-snapshot-button').click();
|
cy.getByDataCy('create-snapshot-button').click();
|
||||||
cy.getByDataCy('show-all-snapshots-button').click();
|
cy.getByDataCy('show-all-snapshots-button').click();
|
||||||
cy.getByDataCy('snapshot-list')
|
cy.getByDataCy('snapshot-list').should('exist').within(() => {
|
||||||
.should('exist')
|
cy.get('.snapshots__snapshot').should('have.length', 1);
|
||||||
.within(() => {
|
});
|
||||||
cy.get('.snapshots__snapshot').should('have.length', 2);
|
waitNTimes(7);
|
||||||
});
|
|
||||||
waitForNRequests(7);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Applies Snapshot', () => {
|
it('Applies Snapshot', () => {
|
||||||
|
|
@ -243,7 +234,7 @@ describe('Snapshot', () => {
|
||||||
|
|
||||||
cy.getByDataCy('module-title').should('exist');
|
cy.getByDataCy('module-title').should('exist');
|
||||||
cy.getByDataCy('snapshot-header').should('not.exist');
|
cy.getByDataCy('snapshot-header').should('not.exist');
|
||||||
waitForNRequests(8);
|
waitNTimes(9);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renames Snapshot', () => {
|
it('Renames Snapshot', () => {
|
||||||
|
|
@ -252,12 +243,12 @@ describe('Snapshot', () => {
|
||||||
snapshotTitle = newTitle;
|
snapshotTitle = newTitle;
|
||||||
// mockUpdateSnapshot(newTitle);
|
// mockUpdateSnapshot(newTitle);
|
||||||
cy.visit('module/miteinander-reden/snapshots');
|
cy.visit('module/miteinander-reden/snapshots');
|
||||||
cy.getByDataCy('snapshot-link').should('contain.text', 'Old Title');
|
cy.getByDataCy('snapshot-link').should('have.text', 'Old Title');
|
||||||
cy.getByDataCy('rename-snapshot-button').click();
|
cy.getByDataCy('rename-snapshot-button').click();
|
||||||
cy.getByDataCy('edit-name-input').clear().type(newTitle);
|
cy.getByDataCy('edit-name-input').clear().type(newTitle);
|
||||||
cy.getByDataCy('modal-save-button').click();
|
cy.getByDataCy('modal-save-button').click();
|
||||||
cy.getByDataCy('snapshot-link').should('contain.text', 'New Title');
|
cy.getByDataCy('snapshot-link').should('have.text', 'New Title');
|
||||||
waitForNRequests(5);
|
waitNTimes(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Deletes Snapshot', () => {
|
it('Deletes Snapshot', () => {
|
||||||
|
|
@ -269,7 +260,7 @@ describe('Snapshot', () => {
|
||||||
cy.getByDataCy('delete-snapshot-button').click();
|
cy.getByDataCy('delete-snapshot-button').click();
|
||||||
cy.getByDataCy('modal-save-button').click();
|
cy.getByDataCy('modal-save-button').click();
|
||||||
cy.getByDataCy('snapshot-entry').should('have.length', 0);
|
cy.getByDataCy('snapshot-entry').should('have.length', 0);
|
||||||
waitForNRequests(6);
|
waitNTimes(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays the Snapshot list correcly', () => {
|
it('Displays the Snapshot list correcly', () => {
|
||||||
|
|
@ -278,16 +269,13 @@ describe('Snapshot', () => {
|
||||||
cy.getByDataCy('snapshot-entry').should('have.length', 1);
|
cy.getByDataCy('snapshot-entry').should('have.length', 1);
|
||||||
cy.getByDataCy('delete-snapshot-button').should('exist');
|
cy.getByDataCy('delete-snapshot-button').should('exist');
|
||||||
cy.getByDataCy('rename-snapshot-button').should('exist');
|
cy.getByDataCy('rename-snapshot-button').should('exist');
|
||||||
cy.getByDataCy('snapshot-link').should('contain.text', 'Old Title');
|
cy.getByDataCy('snapshot-link').should('have.text', 'Old Title');
|
||||||
cy.getByDataCy('team-snapshots-link').click();
|
cy.getByDataCy('team-snapshots-link').click();
|
||||||
cy.getByDataCy('snapshot-entry').should('have.length', 1);
|
cy.getByDataCy('snapshot-entry').should('have.length', 1);
|
||||||
cy.getByDataCy('snapshot-link').should('contain.text', 'Shared snapshot');
|
cy.getByDataCy('snapshot-link').should('have.text', 'Shared snapshot');
|
||||||
cy.getByDataCy('delete-snapshot-button').should('not.exist');
|
cy.getByDataCy('delete-snapshot-button').should('not.exist');
|
||||||
cy.getByDataCy('rename-snapshot-button').should('not.exist');
|
cy.getByDataCy('rename-snapshot-button').should('not.exist');
|
||||||
cy.getByDataCy('snapshot-link').click();
|
waitNTimes(4);
|
||||||
cy.getByDataCy('module-title').should('contain.text', 'Shared snapshot');
|
|
||||||
waitForNRequests(5);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {});
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
describe('Room Team Management - Read only', () => {
|
describe('Room Team Management - Read only', () => {
|
||||||
const SELECTED_CLASS_ID = 'selectedClassId';
|
const SELECTED_CLASS_ID = 'selectedClassId';
|
||||||
|
|
||||||
const getOperations = ({ readOnly, classReadOnly }) => ({
|
const getOperations = ({readOnly, classReadOnly}) => ({
|
||||||
MeQuery: {
|
MeQuery: {
|
||||||
me: {
|
me: {
|
||||||
readOnly,
|
readOnly,
|
||||||
|
|
@ -13,25 +13,23 @@ describe('Room Team Management - Read only', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RoomsQuery: {
|
RoomsQuery: {
|
||||||
rooms: [
|
rooms: [{
|
||||||
{
|
id: '',
|
||||||
id: '',
|
slug: 'some-room',
|
||||||
slug: '',
|
title: 'some room',
|
||||||
title: 'some room',
|
entryCount: 3,
|
||||||
entryCount: 3,
|
appearance: 'red',
|
||||||
appearance: 'red',
|
description: 'some description',
|
||||||
description: 'some description',
|
schoolClass: {
|
||||||
schoolClass: {
|
id: SELECTED_CLASS_ID,
|
||||||
id: SELECTED_CLASS_ID,
|
name: 'bla',
|
||||||
name: 'bla',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
}],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkRoomsReadOnly = ({ editable, readOnly, classReadOnly = false }) => {
|
const checkRoomsReadOnly = ({editable, readOnly, classReadOnly = false}) => {
|
||||||
const operations = getOperations({ readOnly, classReadOnly });
|
const operations = getOperations({readOnly, classReadOnly});
|
||||||
|
|
||||||
cy.mockGraphqlOps({
|
cy.mockGraphqlOps({
|
||||||
operations,
|
operations,
|
||||||
|
|
@ -50,14 +48,14 @@ describe('Room Team Management - Read only', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can edit room', () => {
|
it('can edit room', () => {
|
||||||
checkRoomsReadOnly({ editable: true, readOnly: false });
|
checkRoomsReadOnly({editable: true, readOnly: false});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can not edit room', () => {
|
it('can not edit room', () => {
|
||||||
checkRoomsReadOnly({ editable: false, readOnly: true });
|
checkRoomsReadOnly({editable: false, readOnly: true});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can not edit room of inactive class', () => {
|
it('can not edit room of inactive class', () => {
|
||||||
checkRoomsReadOnly({ editable: false, readOnly: false, classReadOnly: true });
|
checkRoomsReadOnly({editable: false, readOnly: false, classReadOnly: true});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ describe('The Room Page (Teacher)', () => {
|
||||||
AddRoomEntry: {
|
AddRoomEntry: {
|
||||||
addRoomEntry: {
|
addRoomEntry: {
|
||||||
roomEntry: {
|
roomEntry: {
|
||||||
|
slug: 'entry-slug',
|
||||||
title: entryTitle,
|
title: entryTitle,
|
||||||
contents: [
|
contents: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -321,8 +321,9 @@ describe('Teacher Class Management', () => {
|
||||||
|
|
||||||
it('tries to create a new class with duplicate name', () => {
|
it('tries to create a new class with duplicate name', () => {
|
||||||
const name = 'Hill Billy Valley';
|
const name = 'Hill Billy Valley';
|
||||||
|
const oldName = 'Some stupid class';
|
||||||
let selectedClass = teacher.selectedClass;
|
let selectedClass = teacher.selectedClass;
|
||||||
selectedClass.name = 'Some stupid class';
|
selectedClass.name = oldName;
|
||||||
|
|
||||||
const schoolClasses = [teacher.selectedClass];
|
const schoolClasses = [teacher.selectedClass];
|
||||||
|
|
||||||
|
|
@ -337,10 +338,6 @@ describe('Teacher Class Management', () => {
|
||||||
MeQuery: () => ({
|
MeQuery: () => ({
|
||||||
me: me(),
|
me: me(),
|
||||||
}),
|
}),
|
||||||
WhateverNode() {
|
|
||||||
console.log('Through here');
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
MySchoolClassQuery: () => ({
|
MySchoolClassQuery: () => ({
|
||||||
me: me(),
|
me: me(),
|
||||||
}),
|
}),
|
||||||
|
|
@ -366,6 +363,7 @@ describe('Teacher Class Management', () => {
|
||||||
cy.visit('/me/my-class');
|
cy.visit('/me/my-class');
|
||||||
|
|
||||||
cy.get('h1').should('exist');
|
cy.get('h1').should('exist');
|
||||||
|
cy.getByDataCy('group-list-name').should('contain', oldName);
|
||||||
|
|
||||||
cy.get('[data-cy=header-user-widget]').within(() => {
|
cy.get('[data-cy=header-user-widget]').within(() => {
|
||||||
cy.get('[data-cy=user-widget-avatar]').click();
|
cy.get('[data-cy=user-widget-avatar]').click();
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,42 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
moduleFileExtensions: ['js', 'jsx', 'ts', 'json', 'vue'],
|
moduleFileExtensions: [
|
||||||
|
'js',
|
||||||
|
'jsx',
|
||||||
|
'ts',
|
||||||
|
'json',
|
||||||
|
'vue',
|
||||||
|
],
|
||||||
transform: {
|
transform: {
|
||||||
'\\.(gql|graphql)$': 'jest-transform-graphql',
|
'\\.(gql|graphql)$': '@graphql-tools/jest-transform',
|
||||||
'^.+\\.js$': 'babel-jest',
|
'^.+\\.js$': 'babel-jest',
|
||||||
'^.+\\.ts$': 'babel-jest',
|
'^.+\\.ts$': 'babel-jest',
|
||||||
'^.+\\.vue$': '@vue/vue2-jest',
|
'^.+\\.vue$': '@vue/vue3-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: ['<rootDir>/src', '<rootDir>/node_modules'],
|
modulePaths: [
|
||||||
transformIgnorePatterns: ['/node_modules/'],
|
'<rootDir>/src',
|
||||||
|
'<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: ['<rootDir>/node_modules/jest-serializer-vue'],
|
snapshotSerializers: [
|
||||||
|
'<rootDir>/node_modules/jest-serializer-vue',
|
||||||
|
],
|
||||||
testEnvironment: 'jsdom',
|
testEnvironment: 'jsdom',
|
||||||
testMatch: ['**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'],
|
testEnvironmentOptions: {
|
||||||
testURL: 'http://localhost/',
|
url: 'http://localhost/',
|
||||||
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
|
customExportConditions: ['node', 'node-addons'] // needed according to https://github.com/vuejs/vue-jest/issues/479
|
||||||
|
},
|
||||||
|
testMatch: [
|
||||||
|
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)',
|
||||||
|
],
|
||||||
|
watchPlugins: [
|
||||||
|
'jest-watch-typeahead/filename',
|
||||||
|
'jest-watch-typeahead/testname',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -30,11 +30,10 @@
|
||||||
"prettier:check": "prettier . --check"
|
"prettier:check": "prettier . --check"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.5.8",
|
"@apollo/client": "^3.5.10",
|
||||||
"@babel/core": "^7.16.7",
|
"@babel/core": "^7.16.7",
|
||||||
"@babel/eslint-plugin": "^7.16.5",
|
"@babel/eslint-plugin": "^7.16.5",
|
||||||
"@babel/plugin-transform-runtime": "^7.5.0",
|
"@babel/plugin-transform-runtime": "^7.5.0",
|
||||||
"@babel/polyfill": "^7.4.4",
|
|
||||||
"@babel/preset-env": "^7.5.4",
|
"@babel/preset-env": "^7.5.4",
|
||||||
"@babel/preset-stage-2": "^7.0.0",
|
"@babel/preset-stage-2": "^7.0.0",
|
||||||
"@babel/preset-typescript": "^7.16.7",
|
"@babel/preset-typescript": "^7.16.7",
|
||||||
|
|
@ -48,17 +47,21 @@
|
||||||
"@tiptap/extension-list-item": "^2.0.0-beta.20",
|
"@tiptap/extension-list-item": "^2.0.0-beta.20",
|
||||||
"@tiptap/extension-paragraph": "^2.0.0-beta.23",
|
"@tiptap/extension-paragraph": "^2.0.0-beta.23",
|
||||||
"@tiptap/extension-text": "^2.0.0-beta.15",
|
"@tiptap/extension-text": "^2.0.0-beta.15",
|
||||||
"@tiptap/vue-2": "^2.0.0-beta.77",
|
"@tiptap/vue-3": "^2.0.0-beta.90",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||||
"@typescript-eslint/parser": "^5.10.0",
|
"@typescript-eslint/parser": "^5.10.0",
|
||||||
"@vue/test-utils": "^1.3.0",
|
"@vue/apollo-option": "^4.0.0-alpha.16",
|
||||||
"@vue/vue2-jest": "^27.0.0",
|
"@vue/compat": "3.2.30",
|
||||||
|
"@vue/compiler-sfc": "3.2.30",
|
||||||
|
"@vue/test-utils": "^2.2.0",
|
||||||
|
"@vue/vue3-jest": "^29.1.1",
|
||||||
"autoprefixer": "^10.4.12",
|
"autoprefixer": "^10.4.12",
|
||||||
"babel-core": "^7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-jest": "^27.5.1",
|
"babel-jest": "^29.2.2",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.0.6",
|
||||||
"chalk": "^2.0.1",
|
"chalk": "^2.0.1",
|
||||||
"copy-webpack-plugin": "^10.1.0",
|
"copy-webpack-plugin": "^10.1.0",
|
||||||
|
"core-js": "^3.26.0",
|
||||||
"css-loader": "^3.6.0",
|
"css-loader": "^3.6.0",
|
||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||||
"cy2": "^1.2.1",
|
"cy2": "^1.2.1",
|
||||||
|
|
@ -76,7 +79,8 @@
|
||||||
"graphql-tag": "^2.10.1",
|
"graphql-tag": "^2.10.1",
|
||||||
"graphql-tools": "^8.2.5",
|
"graphql-tools": "^8.2.5",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^29.2.2",
|
||||||
|
"jest-environment-jsdom": "^29.2.2",
|
||||||
"jest-serializer-vue": "^2.0.2",
|
"jest-serializer-vue": "^2.0.2",
|
||||||
"jest-transform-graphql": "^2.1.0",
|
"jest-transform-graphql": "^2.1.0",
|
||||||
"jest-transform-stub": "^2.0.0",
|
"jest-transform-stub": "^2.0.0",
|
||||||
|
|
@ -85,6 +89,7 @@
|
||||||
"loglevel": "^1.8.0",
|
"loglevel": "^1.8.0",
|
||||||
"mini-css-extract-plugin": "^2.4.5",
|
"mini-css-extract-plugin": "^2.4.5",
|
||||||
"mock-apollo-client": "^1.2.0",
|
"mock-apollo-client": "^1.2.0",
|
||||||
|
"node-sass": "^7.0.3",
|
||||||
"ora": "^1.2.0",
|
"ora": "^1.2.0",
|
||||||
"portfinder": "^1.0.13",
|
"portfinder": "^1.0.13",
|
||||||
"postcss-import": "^15.0.0",
|
"postcss-import": "^15.0.0",
|
||||||
|
|
@ -92,26 +97,24 @@
|
||||||
"postcss-url": "^10.1.3",
|
"postcss-url": "^10.1.3",
|
||||||
"prettier": "2.8.2",
|
"prettier": "2.8.2",
|
||||||
"rimraf": "^2.6.0",
|
"rimraf": "^2.6.0",
|
||||||
"sass": "^1.56.1",
|
|
||||||
"sass-loader": "^12.6.0",
|
"sass-loader": "^12.6.0",
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
"shelljs": "^0.8.5",
|
"shelljs": "^0.8.5",
|
||||||
"survey-knockout": "^1.9.41",
|
"survey-core": "1.9.41",
|
||||||
|
"survey-knockout-ui": "1.9.41",
|
||||||
"ts-loader": "^8.3.0",
|
"ts-loader": "^8.3.0",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
"uploadcare-widget": "^3.6.0",
|
"uploadcare-widget": "^3.6.0",
|
||||||
"url-loader": "^4.1.1",
|
"vee-validate": "^4.5.10",
|
||||||
"vee-validate": "^3.4.14",
|
"vue": "3.2.30",
|
||||||
"vue": "^2.7.13",
|
"vue-loader": "^16.8.3",
|
||||||
"vue-apollo": "^3.1.0",
|
|
||||||
"vue-loader": "^15.10.0",
|
|
||||||
"vue-matomo": "^4.1.0",
|
"vue-matomo": "^4.1.0",
|
||||||
"vue-router": "^3.5.3",
|
"vue-router": "^4.0.14",
|
||||||
"vue-scrollto": "^2.11.0",
|
"vue-scrollto": "^2.20.0",
|
||||||
"vue-style-loader": "^3.0.1",
|
"vue-style-loader": "^3.0.1",
|
||||||
"vue-template-compiler": "^2.7.13",
|
"vue-vimeo-player": "^1.1.2",
|
||||||
"vue-vimeo-player": "^0.2.2",
|
"vuejs3-logger": "1.0.0",
|
||||||
"vuex": "^3.0.1",
|
"vuex": "4.0.1",
|
||||||
"webpack": "^5.67.0",
|
"webpack": "^5.67.0",
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
"webpack-bundle-analyzer": "^4.5.0",
|
||||||
"webpack-cli": "^4.9.1",
|
"webpack-cli": "^4.9.1",
|
||||||
|
|
@ -126,11 +129,5 @@
|
||||||
"> 1%",
|
"> 1%",
|
||||||
"last 2 versions",
|
"last 2 versions",
|
||||||
"not ie <= 8"
|
"not ie <= 8"
|
||||||
],
|
]
|
||||||
"resolutions": {
|
|
||||||
"vue": "2.6.14"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"fsevents": "^2.3.2"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,38 +9,54 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { defineAsyncComponent } from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import ScrollUp from '@/components/ScrollUp';
|
import ScrollUp from '@/components/ScrollUp';
|
||||||
import ReadOnlyBanner from '@/components/ReadOnlyBanner';
|
import ReadOnlyBanner from '@/components/ReadOnlyBanner';
|
||||||
|
|
||||||
import modals from '@/components/modals';
|
import modals from '@/components/modals';
|
||||||
|
|
||||||
const NewContentBlockWizard = () =>
|
const NewContentBlockWizard = defineAsyncComponent(() =>
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/NewContentBlockWizard');
|
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/NewContentBlockWizard')
|
||||||
const EditContentBlockWizard = () =>
|
);
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/EditContentBlockWizard');
|
const EditContentBlockWizard = defineAsyncComponent(() =>
|
||||||
const EditRoomEntryWizard = () =>
|
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/EditContentBlockWizard')
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/rooms/room-entries/EditRoomEntryWizard');
|
);
|
||||||
const NewProjectEntryWizard = () =>
|
const EditRoomEntryWizard = defineAsyncComponent(() =>
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/NewProjectEntryWizard');
|
import(/* webpackChunkName: "content-forms" */ '@/components/rooms/room-entries/EditRoomEntryWizard')
|
||||||
const EditProjectEntryWizard = () =>
|
);
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/EditProjectEntryWizard');
|
const NewProjectEntryWizard = defineAsyncComponent(() =>
|
||||||
const NewObjectiveWizard = () =>
|
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/NewProjectEntryWizard')
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/objective-groups/NewObjectiveWizard');
|
);
|
||||||
const NewNoteWizard = () => import(/* webpackChunkName: "content-forms" */ '@/components/notes/NewNoteWizard');
|
const EditProjectEntryWizard = defineAsyncComponent(() =>
|
||||||
const EditNoteWizard = () => import(/* webpackChunkName: "content-forms" */ '@/components/notes/EditNoteWizard');
|
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/EditProjectEntryWizard')
|
||||||
const EditClassNameWizard = () =>
|
);
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/school-class/EditClassNameWizard');
|
const NewObjectiveWizard = defineAsyncComponent(() =>
|
||||||
const EditTeamNameWizard = () =>
|
import(/* webpackChunkName: "content-forms" */ '@/components/objective-groups/NewObjectiveWizard')
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/profile/EditTeamNameWizard');
|
);
|
||||||
const EditSnapshotTitleWizard = () =>
|
const NewNoteWizard = defineAsyncComponent(() =>
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/snapshots/EditSnapshotTitleWizard');
|
import(/* webpackChunkName: "content-forms" */ '@/components/notes/NewNoteWizard')
|
||||||
const DefaultLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/DefaultLayout');
|
);
|
||||||
const SimpleLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/SimpleLayout');
|
const EditNoteWizard = defineAsyncComponent(() =>
|
||||||
const FullScreenLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/FullScreenLayout');
|
import(/* webpackChunkName: "content-forms" */ '@/components/notes/EditNoteWizard')
|
||||||
const PublicLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/PublicLayout');
|
);
|
||||||
const BlankLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/BlankLayout');
|
const EditClassNameWizard = defineAsyncComponent(() =>
|
||||||
const SplitLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/SplitLayout');
|
import(/* webpackChunkName: "content-forms" */ '@/components/school-class/EditClassNameWizard')
|
||||||
|
);
|
||||||
|
const EditTeamNameWizard = defineAsyncComponent(() =>
|
||||||
|
import(/* webpackChunkName: "content-forms" */ '@/components/profile/EditTeamNameWizard')
|
||||||
|
);
|
||||||
|
const EditSnapshotTitleWizard = defineAsyncComponent(() =>
|
||||||
|
import(/* webpackChunkName: "content-forms" */ '@/components/snapshots/EditSnapshotTitleWizard')
|
||||||
|
);
|
||||||
|
const DefaultLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/DefaultLayout'));
|
||||||
|
const SimpleLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/SimpleLayout'));
|
||||||
|
const FullScreenLayout = defineAsyncComponent(() =>
|
||||||
|
import(/* webpackChunkName: "layouts" */ '@/layouts/FullScreenLayout')
|
||||||
|
);
|
||||||
|
const PublicLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/PublicLayout'));
|
||||||
|
const BlankLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/BlankLayout'));
|
||||||
|
const SplitLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/SplitLayout'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
|
|
|
||||||
|
|
@ -1,98 +1,105 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="add-content">
|
<div class="add-content">
|
||||||
<a class="add-content__button" @click="addContent">
|
<a
|
||||||
|
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 { CREATE_CONTENT_BLOCK_AFTER_PAGE, CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE } from '@/router/module.names';
|
import {
|
||||||
|
CREATE_CONTENT_BLOCK_AFTER_PAGE,
|
||||||
|
CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE,
|
||||||
|
} from '@/router/module.names';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const AddPointer = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddPointer');
|
const AddPointer = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddPointer'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
where: {
|
where: {
|
||||||
type: Object,
|
type: Object,
|
||||||
validator(prop) {
|
validator(prop) {
|
||||||
return (
|
return Object.prototype.hasOwnProperty.call(prop, 'after' )
|
||||||
Object.prototype.hasOwnProperty.call(prop, 'after') || Object.prototype.hasOwnProperty.call(prop, 'parent')
|
|| Object.prototype.hasOwnProperty.call(prop, 'parent');
|
||||||
);
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
AddPointer,
|
AddPointer
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
parent() {
|
parent() {
|
||||||
return this.where.parent;
|
return this.where.parent;
|
||||||
},
|
},
|
||||||
after() {
|
after() {
|
||||||
return this.where.after;
|
return this.where.after;
|
||||||
},
|
},
|
||||||
isObjectiveGroup() {
|
isObjectiveGroup() {
|
||||||
return this.parent && this.parent.__typename === 'ObjectiveGroupNode';
|
return this.parent && this.parent.__typename === 'ObjectiveGroupNode';
|
||||||
},
|
},
|
||||||
slug() {
|
slug() {
|
||||||
return this.$route.params.slug;
|
return this.$route.params.slug;
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
addContent() {
|
|
||||||
if (this.isObjectiveGroup) {
|
|
||||||
this.$modal.open('new-objective-wizard', { parent: this.parent.id });
|
|
||||||
} else {
|
|
||||||
let route;
|
|
||||||
if (this.after && this.after.id) {
|
|
||||||
route = {
|
|
||||||
name: CREATE_CONTENT_BLOCK_AFTER_PAGE,
|
|
||||||
params: {
|
|
||||||
after: this.after.id,
|
|
||||||
slug: this.slug,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
route = {
|
|
||||||
name: CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE,
|
|
||||||
params: {
|
|
||||||
parent: this.parent.id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.$router.push(route);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
|
||||||
|
methods: {
|
||||||
|
addContent() {
|
||||||
|
if (this.isObjectiveGroup) {
|
||||||
|
this.$modal.open('new-objective-wizard', {parent: this.parent.id});
|
||||||
|
} else {
|
||||||
|
let route;
|
||||||
|
if (this.after && this.after.id) {
|
||||||
|
route = {
|
||||||
|
name: CREATE_CONTENT_BLOCK_AFTER_PAGE,
|
||||||
|
params: {
|
||||||
|
after: this.after.id,
|
||||||
|
slug: this.slug
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
route = {
|
||||||
|
name: CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE,
|
||||||
|
params: {
|
||||||
|
parent: this.parent.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
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;
|
||||||
position: relative;
|
position: relative;
|
||||||
@include desktop {
|
@include desktop {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
}
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
margin-right: -85px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
width: 40px;
|
||||||
|
fill: $color-silver-dark;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
&__button {
|
|
||||||
margin-right: -85px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__icon {
|
|
||||||
width: 40px;
|
|
||||||
fill: $color-silver-dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,42 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="add-content-element" @click="$emit('add-element', index)">
|
<div
|
||||||
|
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>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const AddIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const AddIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
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;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-bottom: 2px solid $color-silver-dark;
|
border-bottom: 2px solid $color-silver-dark;
|
||||||
margin-bottom: 21px + 25px;
|
margin-bottom: 21px + 25px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
fill: $color-silver-dark;
|
fill: $color-silver-dark;
|
||||||
margin-bottom: -21px;
|
margin-bottom: -21px;
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -11,71 +11,69 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const AddIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddIcon.vue');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const AddIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddIcon.vue'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
route: {
|
route: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null
|
||||||
|
},
|
||||||
|
reverse: { // use reverse colors
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
click: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
},
|
},
|
||||||
reverse: {
|
|
||||||
// use reverse colors
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
click: {
|
|
||||||
type: Function,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
AddIcon,
|
AddIcon
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
component() {
|
component() {
|
||||||
// only use the router link if the route prop is provided, otherwise render a normal anchor tag
|
// only use the router link if the route prop is provided, otherwise render a normal anchor tag
|
||||||
return this.route ? 'router-link' : 'a';
|
return this.route ? 'router-link' : 'a';
|
||||||
|
},
|
||||||
|
properties() {
|
||||||
|
return this.route ? {
|
||||||
|
to: this.route,
|
||||||
|
tag: 'div'
|
||||||
|
} : {};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
properties() {
|
};
|
||||||
return this.route
|
|
||||||
? {
|
|
||||||
to: this.route,
|
|
||||||
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;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@include widget-shadow;
|
@include widget-shadow;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__add {
|
||||||
|
width: 80px;
|
||||||
|
fill: $color-silver-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--reverse {
|
||||||
|
@include widget-shadow-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--reverse &__add {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__add {
|
|
||||||
width: 80px;
|
|
||||||
fill: $color-silver-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--reverse {
|
|
||||||
@include widget-shadow-reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--reverse &__add {
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,76 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link :to="to" data-cy="back-link" class="sub-navigation-item back-link">
|
<router-link
|
||||||
|
:to="to"
|
||||||
|
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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { MODULE_PAGE } from '@/router/module.names';
|
import { MODULE_PAGE } from '@/router/module.names';
|
||||||
import { ROOMS_PAGE } from '@/router/room.names';
|
import { ROOMS_PAGE } from '@/router/room.names';
|
||||||
import { PROJECTS_PAGE } from '@/router/portfolio.names';
|
import { PROJECTS_PAGE } from '@/router/portfolio.names';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const ChevronLeft = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronLeft');
|
const ChevronLeft = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronLeft'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'topic',
|
||||||
|
},
|
||||||
|
slug: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: 'topic',
|
|
||||||
},
|
|
||||||
slug: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
to() {
|
to() {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'topic':
|
case 'topic':
|
||||||
return { name: 'topic', params: { topicSlug: this.slug } };
|
if (this.slug) {
|
||||||
case 'module':
|
return {name: 'topic', params: {topicSlug: this.slug}};
|
||||||
return { name: MODULE_PAGE };
|
} else {
|
||||||
case 'portfolio':
|
return {};
|
||||||
return { name: PROJECTS_PAGE };
|
}
|
||||||
default:
|
case 'module':
|
||||||
return { name: ROOMS_PAGE };
|
return {name: MODULE_PAGE};
|
||||||
}
|
case 'portfolio':
|
||||||
|
return {name: PROJECTS_PAGE};
|
||||||
|
default:
|
||||||
|
return {name: ROOMS_PAGE};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fullTitle() {
|
||||||
|
switch (this.type) {
|
||||||
|
case 'topic':
|
||||||
|
return `${this.$flavor.textTopic}: ${this.title}`;
|
||||||
|
case 'module':
|
||||||
|
return `${this.$flavor.textModule}: ${this.title}`;
|
||||||
|
default:
|
||||||
|
return this.title;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fullTitle() {
|
};
|
||||||
switch (this.type) {
|
|
||||||
case 'topic':
|
|
||||||
return `${this.$flavor.textTopic}: ${this.title}`;
|
|
||||||
case 'module':
|
|
||||||
return `${this.$flavor.textModule}: ${this.title}`;
|
|
||||||
default:
|
|
||||||
return this.title;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.back-link {
|
.back-link {
|
||||||
@include regular-text;
|
@include regular-text;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,93 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="color-chooser">
|
<div class="color-chooser">
|
||||||
<div
|
<div
|
||||||
:class="{ 'color-chooser__color-wrapper--selected': selectedColor === color.name }"
|
:class="{'color-chooser__color-wrapper--selected': selectedColor === color.name}"
|
||||||
class="color-chooser__color-wrapper"
|
class="color-chooser__color-wrapper"
|
||||||
data-cy="color-select"
|
data-cy="color-select"
|
||||||
v-for="(color, index) in colors"
|
v-for="(color, index) in colors"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="$emit('input', color.name)"
|
@click="$emit('input', color.name)"
|
||||||
>
|
>
|
||||||
<div :class="'color-chooser__color--' + color.name" class="color-chooser__color">
|
<div
|
||||||
<tick class="color-chooser__selected-icon" v-if="selectedColor === color.name" />
|
:class="'color-chooser__color--' + color.name"
|
||||||
|
class="color-chooser__color"
|
||||||
|
>
|
||||||
|
<tick
|
||||||
|
class="color-chooser__selected-icon"
|
||||||
|
v-if="selectedColor === color.name"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const Tick = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Tick');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const Tick = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Tick'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
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;
|
|
||||||
|
|
||||||
&__color-wrapper {
|
|
||||||
margin-right: 10px;
|
|
||||||
border-radius: 50px;
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
&--selected {
|
|
||||||
border: 1px solid $color-charcoal-dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__selected-icon {
|
|
||||||
width: 17px;
|
|
||||||
fill: $color-charcoal-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__color {
|
|
||||||
width: 46px;
|
|
||||||
height: 46px;
|
|
||||||
border-radius: 23px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
@supports (display: grid) {
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
justify-items: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
@include skillbox-colors;
|
&__color-wrapper {
|
||||||
|
margin-right: 10px;
|
||||||
|
border-radius: 50px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&--selected {
|
||||||
|
border: 1px solid $color-charcoal-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__selected-icon {
|
||||||
|
width: 17px;
|
||||||
|
fill: $color-charcoal-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__color {
|
||||||
|
width: 46px;
|
||||||
|
height: 46px;
|
||||||
|
border-radius: 23px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
@supports (display: grid) {
|
||||||
|
display: grid
|
||||||
|
}
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
@include skillbox-colors;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
: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 :class="specialClass" :style="instrumentStyle" class="content-block" data-cy="content-block">
|
<div
|
||||||
<div class="block-actions" v-if="canEditModule && !isInstrumentBlock">
|
:class="specialClass"
|
||||||
<user-widget v-bind="me" class="block-actions__user-widget content-block__user-widget" v-if="isMine" />
|
:style="instrumentStyle"
|
||||||
|
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 class="popover-links__link" v-if="!isInstrumentBlock">
|
<li
|
||||||
|
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 class="popover-links__link" v-if="isMine">
|
<li
|
||||||
|
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"
|
||||||
|
|
@ -22,13 +40,22 @@
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="popover-links__link" v-if="isMine">
|
<li
|
||||||
<popover-link text="Bearbeiten" @link-action="editContentBlock(contentBlock)" />
|
class="popover-links__link"
|
||||||
|
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 :block="contentBlock" v-if="canEditModule" />
|
<visibility-action
|
||||||
|
:block="contentBlock"
|
||||||
|
v-if="canEditModule"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3
|
<h3
|
||||||
|
|
@ -39,7 +66,10 @@
|
||||||
>
|
>
|
||||||
{{ instrumentLabel }}
|
{{ instrumentLabel }}
|
||||||
</h3>
|
</h3>
|
||||||
<h4 class="content-block__title" v-if="!contentBlock.indent">
|
<h4
|
||||||
|
class="content-block__title"
|
||||||
|
v-if="!contentBlock.indent"
|
||||||
|
>
|
||||||
{{ contentBlock.title }}
|
{{ contentBlock.title }}
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
|
|
@ -55,107 +85,110 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<add-content-button :where="{ after: contentBlock }" v-if="canEditModule" />
|
<add-content-button
|
||||||
|
:where="{after: contentBlock}"
|
||||||
|
v-if="canEditModule"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AddContentButton from '@/components/AddContentButton';
|
import AddContentButton from '@/components/AddContentButton';
|
||||||
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
||||||
import UserWidget from '@/components/UserWidget';
|
import UserWidget from '@/components/UserWidget';
|
||||||
import VisibilityAction from '@/components/visibility/VisibilityAction';
|
import VisibilityAction from '@/components/visibility/VisibilityAction';
|
||||||
|
|
||||||
import CHAPTER_QUERY from '@/graphql/gql/queries/chapterQuery.gql';
|
import CHAPTER_QUERY from '@/graphql/gql/queries/chapterQuery.gql';
|
||||||
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
|
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
|
||||||
import DUPLICATE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/duplicateContentBlock.gql';
|
import DUPLICATE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/duplicateContentBlock.gql';
|
||||||
|
|
||||||
import me from '@/mixins/me';
|
import me from '@/mixins/me';
|
||||||
|
|
||||||
import { hidden } from '@/helpers/visibility';
|
import {hidden} from '@/helpers/visibility';
|
||||||
import { CONTENT_TYPE } from '@/consts/types';
|
import {CONTENT_TYPE} from '@/consts/types';
|
||||||
import PopoverLink from '@/components/ui/PopoverLink';
|
import PopoverLink from '@/components/ui/PopoverLink';
|
||||||
import { insertAtIndex, removeAtIndex } from '@/graphql/immutable-operations';
|
import {insertAtIndex, removeAtIndex} from '@/graphql/immutable-operations';
|
||||||
import { EDIT_CONTENT_BLOCK_PAGE } from '@/router/module.names';
|
import {EDIT_CONTENT_BLOCK_PAGE} from '@/router/module.names';
|
||||||
import { instrumentCategory } from '@/helpers/instrumentType';
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
import {instrumentCategory} from '@/helpers/instrumentType';
|
||||||
|
|
||||||
const ContentComponent = () =>
|
const ContentComponent = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ContentComponent'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ContentComponent');
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ContentBlock',
|
|
||||||
props: {
|
|
||||||
contentBlock: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
parent: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
editMode: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mixins: [me],
|
export default {
|
||||||
|
name: 'ContentBlock',
|
||||||
|
props: {
|
||||||
|
contentBlock: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
editMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
components: {
|
mixins: [me],
|
||||||
PopoverLink,
|
|
||||||
ContentComponent,
|
|
||||||
AddContentButton,
|
|
||||||
VisibilityAction,
|
|
||||||
MoreOptionsWidget,
|
|
||||||
UserWidget,
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
components: {
|
||||||
canEditModule() {
|
PopoverLink,
|
||||||
return !this.contentBlock.indent && this.editMode;
|
ContentComponent,
|
||||||
|
AddContentButton,
|
||||||
|
VisibilityAction,
|
||||||
|
MoreOptionsWidget,
|
||||||
|
UserWidget,
|
||||||
},
|
},
|
||||||
specialClass() {
|
|
||||||
return `content-block--${this.contentBlock.type.toLowerCase()}`;
|
computed: {
|
||||||
},
|
canEditModule() {
|
||||||
isInstrumentBlock() {
|
return !this.contentBlock.indent && this.editMode;
|
||||||
return !!this.contentBlock.instrumentCategory;
|
},
|
||||||
},
|
specialClass() {
|
||||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
return `content-block--${this.contentBlock.type.toLowerCase()}`;
|
||||||
instrumentStyle() {
|
},
|
||||||
if (this.isInstrumentBlock) {
|
isInstrumentBlock() {
|
||||||
return {
|
return !!this.contentBlock.instrumentCategory;
|
||||||
backgroundColor: this.contentBlock.instrumentCategory.background,
|
},
|
||||||
};
|
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||||
}
|
instrumentStyle() {
|
||||||
return {};
|
if (this.isInstrumentBlock) {
|
||||||
},
|
return {
|
||||||
instrumentLabel() {
|
backgroundColor: this.contentBlock.instrumentCategory.background
|
||||||
const contentType = this.contentBlock.type.toLowerCase();
|
};
|
||||||
if (contentType.startsWith('base')) {
|
}
|
||||||
// all legacy instruments start with `base`
|
return {};
|
||||||
return instrumentCategory(contentType);
|
},
|
||||||
}
|
instrumentLabel() {
|
||||||
if (this.isInstrumentBlock) {
|
const contentType = this.contentBlock.type.toLowerCase();
|
||||||
return instrumentCategory(this.contentBlock.instrumentCategory.name);
|
if (contentType.startsWith('base')) { // all legacy instruments start with `base`
|
||||||
}
|
return instrumentCategory(contentType);
|
||||||
return '';
|
}
|
||||||
},
|
if (this.isInstrumentBlock) {
|
||||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
return instrumentCategory(this.contentBlock.instrumentCategory.name);
|
||||||
instrumentLabelStyle() {
|
}
|
||||||
if (this.isInstrumentBlock) {
|
return '';
|
||||||
return {
|
},
|
||||||
color: this.contentBlock.instrumentCategory.foreground,
|
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||||
};
|
instrumentLabelStyle() {
|
||||||
}
|
if (this.isInstrumentBlock) {
|
||||||
return {};
|
return {
|
||||||
},
|
color: this.contentBlock.instrumentCategory.foreground
|
||||||
canEditContentBlock() {
|
};
|
||||||
return this.isMine && !this.contentBlock.indent;
|
}
|
||||||
},
|
return {};
|
||||||
isMine() {
|
},
|
||||||
return this.contentBlock.mine;
|
canEditContentBlock() {
|
||||||
},
|
return this.isMine && !this.contentBlock.indent;
|
||||||
contentBlocksWithContentLists() {
|
},
|
||||||
/*
|
isMine() {
|
||||||
|
return this.contentBlock.mine;
|
||||||
|
},
|
||||||
|
contentBlocksWithContentLists() {
|
||||||
|
/*
|
||||||
collects all content_list_items in content_lists:
|
collects all content_list_items in content_lists:
|
||||||
{
|
{
|
||||||
text_block,
|
text_block,
|
||||||
|
|
@ -169,238 +202,221 @@ export default {
|
||||||
text_block
|
text_block
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
let contentList = [];
|
let contentList = [];
|
||||||
let newContent = this.contentBlock.contents.reduce((newContents, content, index) => {
|
let newContent = this.contentBlock.contents.reduce((newContents, content, index) => {
|
||||||
// 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) {
|
if (index === this.contentBlock.contents.length - 1) { // content is last element of contents array
|
||||||
// content is last element of contents array
|
let updatedContent = [...newContents, ...this.createContentListOrBlocks(contentList)];
|
||||||
let updatedContent = [...newContents, ...this.createContentListOrBlocks(contentList)];
|
return updatedContent;
|
||||||
return updatedContent;
|
}
|
||||||
}
|
|
||||||
return newContents;
|
|
||||||
} else {
|
|
||||||
// handle all other items and reset current content_list if necessary
|
|
||||||
if (contentList.length !== 0) {
|
|
||||||
newContents = [...newContents, ...this.createContentListOrBlocks(contentList), content];
|
|
||||||
contentList = [];
|
|
||||||
return newContents;
|
return newContents;
|
||||||
} else {
|
} else {
|
||||||
return [...newContents, content];
|
// handle all other items and reset current content_list if necessary
|
||||||
|
if (contentList.length !== 0) {
|
||||||
|
newContents = [...newContents, ...this.createContentListOrBlocks(contentList), content];
|
||||||
|
contentList = [];
|
||||||
|
return newContents;
|
||||||
|
} else {
|
||||||
|
return [...newContents, content];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}, []);
|
||||||
}, []);
|
return Object.assign({}, this.contentBlock, {
|
||||||
return Object.assign({}, this.contentBlock, {
|
contents: newContent,
|
||||||
contents: newContent,
|
});
|
||||||
});
|
},
|
||||||
|
hidden() {
|
||||||
|
return hidden({
|
||||||
|
block: this.contentBlock,
|
||||||
|
schoolClass: this.schoolClass,
|
||||||
|
type: CONTENT_TYPE,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
root() {
|
||||||
|
// we need the root content block id, not the generated content block if inside a content list block
|
||||||
|
return this.contentBlock.root ? this.contentBlock.root : this.contentBlock.id;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
hidden() {
|
methods: {
|
||||||
return hidden({
|
duplicateContentBlock({id}) {
|
||||||
block: this.contentBlock,
|
const parent = this.parent;
|
||||||
schoolClass: this.schoolClass,
|
this.$apollo.mutate({
|
||||||
type: CONTENT_TYPE,
|
mutation: DUPLICATE_CONTENT_BLOCK_MUTATION,
|
||||||
});
|
variables: {
|
||||||
},
|
input: {
|
||||||
root() {
|
id,
|
||||||
// we need the root content block id, not the generated content block if inside a content list block
|
|
||||||
return this.contentBlock.root ? this.contentBlock.root : this.contentBlock.id;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
duplicateContentBlock({ id }) {
|
|
||||||
const parent = this.parent;
|
|
||||||
this.$apollo.mutate({
|
|
||||||
mutation: DUPLICATE_CONTENT_BLOCK_MUTATION,
|
|
||||||
variables: {
|
|
||||||
input: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
update(
|
|
||||||
store,
|
|
||||||
{
|
|
||||||
data: {
|
|
||||||
duplicateContentBlock: { contentBlock },
|
|
||||||
},
|
},
|
||||||
}
|
|
||||||
) {
|
|
||||||
if (contentBlock) {
|
|
||||||
const query = CHAPTER_QUERY;
|
|
||||||
const variables = {
|
|
||||||
id: parent.id,
|
|
||||||
};
|
|
||||||
const { chapter } = store.readQuery({ query, variables });
|
|
||||||
const index = chapter.contentBlocks.findIndex((contentBlock) => contentBlock.id === id);
|
|
||||||
const contentBlocks = insertAtIndex(chapter.contentBlocks, index, contentBlock);
|
|
||||||
const data = {
|
|
||||||
chapter: {
|
|
||||||
...chapter,
|
|
||||||
contentBlocks,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
store.writeQuery({ query, variables, data });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
editContentBlock(contentBlock) {
|
|
||||||
const route = {
|
|
||||||
name: EDIT_CONTENT_BLOCK_PAGE,
|
|
||||||
params: {
|
|
||||||
id: contentBlock.id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
this.$router.push(route);
|
|
||||||
},
|
|
||||||
deleteContentBlock(contentBlock) {
|
|
||||||
this.$modal
|
|
||||||
.open('confirm')
|
|
||||||
.then(() => {
|
|
||||||
this.doDeleteContentBlock(contentBlock);
|
|
||||||
})
|
|
||||||
.catch();
|
|
||||||
},
|
|
||||||
doDeleteContentBlock(contentBlock) {
|
|
||||||
const parent = this.parent;
|
|
||||||
const id = contentBlock.id;
|
|
||||||
this.$apollo.mutate({
|
|
||||||
mutation: DELETE_CONTENT_BLOCK_MUTATION,
|
|
||||||
variables: {
|
|
||||||
input: {
|
|
||||||
id,
|
|
||||||
},
|
},
|
||||||
},
|
update(store, {data: {duplicateContentBlock: {contentBlock}}}) {
|
||||||
update(
|
if (contentBlock) {
|
||||||
store,
|
const query = CHAPTER_QUERY;
|
||||||
{
|
const variables = {
|
||||||
data: {
|
id: parent.id,
|
||||||
deleteContentBlock: { success },
|
};
|
||||||
|
const {chapter} = store.readQuery({query, variables});
|
||||||
|
const index = chapter.contentBlocks.findIndex(contentBlock => contentBlock.id === id);
|
||||||
|
const contentBlocks = insertAtIndex(chapter.contentBlocks, index, contentBlock);
|
||||||
|
const data = {
|
||||||
|
chapter: {
|
||||||
|
...chapter,
|
||||||
|
contentBlocks,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
store.writeQuery({query, variables, data});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
editContentBlock(contentBlock) {
|
||||||
|
const route = {
|
||||||
|
name: EDIT_CONTENT_BLOCK_PAGE,
|
||||||
|
params: {
|
||||||
|
id: contentBlock.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.$router.push(route);
|
||||||
|
},
|
||||||
|
deleteContentBlock(contentBlock) {
|
||||||
|
this.$modal.open('confirm').then(() => {
|
||||||
|
this.doDeleteContentBlock(contentBlock);
|
||||||
|
})
|
||||||
|
.catch();
|
||||||
|
},
|
||||||
|
doDeleteContentBlock(contentBlock) {
|
||||||
|
const parent = this.parent;
|
||||||
|
const id = contentBlock.id;
|
||||||
|
this.$apollo.mutate({
|
||||||
|
mutation: DELETE_CONTENT_BLOCK_MUTATION,
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
id,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
) {
|
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: {
|
||||||
...chapter,
|
...chapter,
|
||||||
contentBlocks,
|
contentBlocks,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
store.writeQuery({ query, variables, data });
|
store.writeQuery({query, variables, data});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
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;
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
|
||||||
|
|
||||||
&__title {
|
&__container {
|
||||||
line-height: 1.5;
|
position: relative;
|
||||||
margin-top: -0.5rem; // to offset the 1.5 line height, it leaves a padding on top
|
|
||||||
}
|
|
||||||
|
|
||||||
&__instrument-label {
|
|
||||||
margin-bottom: $medium-spacing;
|
|
||||||
@include regular-text();
|
|
||||||
}
|
|
||||||
|
|
||||||
&__action-button {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__user-widget {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--base_communication {
|
|
||||||
@include content-box($color-accent-1-list);
|
|
||||||
|
|
||||||
.content-block__instrument-label {
|
|
||||||
color: $color-accent-1-dark;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&--task {
|
&__title {
|
||||||
@include light-border(bottom);
|
line-height: 1.5;
|
||||||
|
margin-top: -0.5rem; // to offset the 1.5 line height, it leaves a padding on top
|
||||||
|
}
|
||||||
|
|
||||||
.content-block__title {
|
&__instrument-label {
|
||||||
color: $color-brand;
|
margin-bottom: $medium-spacing;
|
||||||
margin-top: $default-padding;
|
@include regular-text();
|
||||||
margin-bottom: $large-spacing;
|
}
|
||||||
@include light-border(bottom);
|
|
||||||
|
|
||||||
@include desktop {
|
&__action-button {
|
||||||
margin-top: 0;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__user-widget {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--base_communication {
|
||||||
|
@include content-box($color-accent-1-list);
|
||||||
|
|
||||||
|
.content-block__instrument-label {
|
||||||
|
color: $color-accent-1-dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&--base_society {
|
&--task {
|
||||||
@include content-box($color-accent-2-list);
|
@include light-border(bottom);
|
||||||
|
|
||||||
.content-block__instrument-label {
|
.content-block__title {
|
||||||
color: $color-accent-2-dark;
|
color: $color-brand;
|
||||||
}
|
margin-top: $default-padding;
|
||||||
}
|
margin-bottom: $large-spacing;
|
||||||
|
@include light-border(bottom);
|
||||||
|
|
||||||
&--base_interdisciplinary {
|
@include desktop {
|
||||||
@include content-box($color-accent-4-list);
|
margin-top: 0;
|
||||||
|
}
|
||||||
.content-block__instrument-label {
|
}
|
||||||
color: $color-accent-4-dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--instrument {
|
|
||||||
@include content-box-base;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(p) {
|
|
||||||
line-height: 1.5;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.text-block) {
|
|
||||||
ul {
|
|
||||||
@include list-parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
&--base_society {
|
||||||
@include list-child;
|
@include content-box($color-accent-2-list);
|
||||||
|
|
||||||
|
.content-block__instrument-label {
|
||||||
|
color: $color-accent-2-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--base_interdisciplinary {
|
||||||
|
@include content-box($color-accent-4-list);
|
||||||
|
|
||||||
|
.content-block__instrument-label {
|
||||||
|
color: $color-accent-4-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--instrument {
|
||||||
|
@include content-box-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ p {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/deep/ .text-block {
|
||||||
|
ul {
|
||||||
|
@include list-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
@include list-child;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,37 @@
|
||||||
<template>
|
<template>
|
||||||
<modal :fullscreen="true">
|
<modal :fullscreen="true">
|
||||||
<component :value="value" :is="type" />
|
<component
|
||||||
|
:value="value"
|
||||||
|
:is="type"
|
||||||
|
/>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
const InfogramBlock = () =>
|
import {defineAsyncComponent} from 'vue';
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/InfogramBlock');
|
const InfogramBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/InfogramBlock'));
|
||||||
const GeniallyBlock = () =>
|
const GeniallyBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/GeniallyBlock'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/GeniallyBlock');
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Modal,
|
Modal,
|
||||||
InfogramBlock,
|
InfogramBlock,
|
||||||
GeniallyBlock,
|
GeniallyBlock
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
id() {
|
id() {
|
||||||
return this.$store.state.infographic.id;
|
return this.$store.state.infographic.id;
|
||||||
},
|
},
|
||||||
type() {
|
type() {
|
||||||
return this.$store.state.infographic.type;
|
return this.$store.state.infographic.type;
|
||||||
},
|
},
|
||||||
value() {
|
value() {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,126 +1,140 @@
|
||||||
<template>
|
<template>
|
||||||
<header class="header-bar">
|
<header class="header-bar">
|
||||||
<a class="header-bar__sidebar-link" data-cy="open-sidebar-link" @click.stop="openSidebar('navigation')">
|
<a
|
||||||
|
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 class="user-header__sidebar-link">
|
<a
|
||||||
<current-class class="user-header__current-class" @click.native.stop="openSidebar('profile')" />
|
class="user-header__sidebar-link"
|
||||||
|
>
|
||||||
|
<current-class
|
||||||
|
class="user-header__current-class"
|
||||||
|
@click="openSidebar('profile')"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<user-widget v-bind="me" data-cy="header-user-widget" @click.native.stop="openSidebar('profile')" />
|
<user-widget
|
||||||
|
:avatar-url="me.avatarUrl"
|
||||||
|
data-cy="header-user-widget"
|
||||||
|
@click="openSidebar('profile')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ContentNavigation from '@/components/book-navigation/ContentNavigation.vue';
|
import ContentNavigation from '@/components/book-navigation/ContentNavigation.vue';
|
||||||
import UserWidget from '@/components/UserWidget.vue';
|
import UserWidget from '@/components/UserWidget.vue';
|
||||||
import CurrentClass from '@/components/school-class/CurrentClass';
|
import CurrentClass from '@/components/school-class/CurrentClass';
|
||||||
|
|
||||||
import openSidebar from '@/mixins/open-sidebar';
|
import openSidebar from '@/mixins/open-sidebar';
|
||||||
import me from '@/mixins/me';
|
import me from '@/mixins/me';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const Hamburger = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Hamburger');
|
const Hamburger = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Hamburger'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [openSidebar, me],
|
mixins: [openSidebar, me],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ContentNavigation,
|
ContentNavigation,
|
||||||
UserWidget,
|
UserWidget,
|
||||||
CurrentClass,
|
CurrentClass,
|
||||||
Hamburger,
|
Hamburger,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.header-bar {
|
.header-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@supports (display: grid) {
|
@supports (display: grid) {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
|
||||||
background-color: $color-white;
|
|
||||||
grid-auto-rows: 50px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100vw;
|
|
||||||
|
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
|
||||||
|
|
||||||
@include desktop {
|
|
||||||
grid-template-columns: 50px 1fr auto;
|
|
||||||
grid-template-rows: 50px;
|
|
||||||
grid-auto-rows: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For IE10+
|
|
||||||
*/
|
|
||||||
-ms-grid-columns: 1fr 1fr 1fr;
|
|
||||||
-ms-grid-rows: 50px 50px;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For IE10+
|
|
||||||
*/
|
|
||||||
& > :nth-child(1) {
|
|
||||||
-ms-grid-column: 1;
|
|
||||||
-ms-grid-row-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content-navigation {
|
|
||||||
grid-column: 2;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
background-color: $color-white;
|
||||||
|
grid-auto-rows: 50px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100vw;
|
||||||
|
|
||||||
&__sidebar-link {
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
padding: $small-spacing;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__sidebar-icon {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For IE10+
|
|
||||||
*/
|
|
||||||
& > :nth-child(3) {
|
|
||||||
-ms-grid-column: 3;
|
|
||||||
-ms-grid-row-align: center;
|
|
||||||
-ms-grid-column-align: end;
|
|
||||||
|
|
||||||
justify-self: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > :nth-child(4) {
|
|
||||||
-ms-grid-row: 2;
|
|
||||||
-ms-grid-column: 1;
|
|
||||||
-ms-grid-column-span: 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-header {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&__current-class {
|
|
||||||
margin-right: $large-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__sidebar-link {
|
|
||||||
cursor: pointer;
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
display: flex;
|
grid-template-columns: 50px 1fr auto;
|
||||||
|
grid-template-rows: 50px;
|
||||||
|
grid-auto-rows: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For IE10+
|
||||||
|
*/
|
||||||
|
-ms-grid-columns: 1fr 1fr 1fr;
|
||||||
|
-ms-grid-rows: 50px 50px;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For IE10+
|
||||||
|
*/
|
||||||
|
& > :nth-child(1) {
|
||||||
|
-ms-grid-column: 1;
|
||||||
|
-ms-grid-row-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content-navigation {
|
||||||
|
grid-column: 2;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__sidebar-link {
|
||||||
|
padding: $small-spacing;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__sidebar-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For IE10+
|
||||||
|
*/
|
||||||
|
& > :nth-child(3) {
|
||||||
|
-ms-grid-column: 3;
|
||||||
|
-ms-grid-row-align: center;
|
||||||
|
-ms-grid-column-align: end;
|
||||||
|
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > :nth-child(4) {
|
||||||
|
-ms-grid-row: 2;
|
||||||
|
-ms-grid-column: 1;
|
||||||
|
-ms-grid-column-span: 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-header {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&__current-class {
|
||||||
|
margin-right: $large-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__sidebar-link {
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -10,65 +10,67 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/InfoIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
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;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
fill: $color-silver-dark;
|
fill: $color-silver-dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__tooltip {
|
&__tooltip {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
position: absolute;
|
|
||||||
left: 30px;
|
|
||||||
top: 0px;
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
display: inline-table;
|
|
||||||
width: auto;
|
|
||||||
|
|
||||||
background-color: $color-white;
|
|
||||||
border: 1px solid $color-silver-dark;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: $small-spacing;
|
|
||||||
@include small-text;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 30px;
|
||||||
top: 18px;
|
top: 0px;
|
||||||
margin-left: -1px;
|
width: 400px;
|
||||||
border-left: 1px solid $color-silver-dark;
|
}
|
||||||
border-top: 1px solid $color-silver-dark;
|
|
||||||
|
&__text {
|
||||||
|
display: inline-table;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
width: 10px;
|
border: 1px solid $color-silver-dark;
|
||||||
height: 10px;
|
border-radius: 5px;
|
||||||
transform: rotate(-45deg) translateY(-50%);
|
padding: $small-spacing;
|
||||||
|
@include small-text;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 18px;
|
||||||
|
margin-left: -1px;
|
||||||
|
border-left: 1px solid $color-silver-dark;
|
||||||
|
border-top: 1px solid $color-silver-dark;
|
||||||
|
background-color: $color-white;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
transform: rotate(-45deg) translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover &__tooltip {
|
||||||
|
visibility: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover &__tooltip {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,58 @@
|
||||||
<template>
|
<template>
|
||||||
<button :disabled="loading || disabled" class="loading-button button button--primary button--big">
|
<button
|
||||||
|
: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 class="loading-button__icon" v-else />
|
<loading-icon
|
||||||
|
class="loading-button__icon"
|
||||||
|
v-else
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const LoadingIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/LoadingIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const LoadingIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LoadingIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disabled: {
|
components: {
|
||||||
type: Boolean,
|
LoadingIcon
|
||||||
default: false,
|
}
|
||||||
},
|
};
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
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;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@include spin;
|
@include spin;
|
||||||
fill: $color-brand;
|
fill: $color-brand;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -4,59 +4,66 @@
|
||||||
<hamburger class="mobile-header__hamburger" />
|
<hamburger class="mobile-header__hamburger" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<router-link to="/" data-cy="mobile-home-link">
|
<router-link
|
||||||
|
to="/"
|
||||||
|
data-cy="mobile-home-link"
|
||||||
|
>
|
||||||
<logo />
|
<logo />
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<user-widget v-bind="me" @click.native.stop="openSidebar('profile')" />
|
<user-widget
|
||||||
|
v-bind="me"
|
||||||
|
@click.stop="openSidebar('profile')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import UserWidget from '@/components/UserWidget';
|
import UserWidget from '@/components/UserWidget';
|
||||||
|
|
||||||
import me from '@/mixins/me';
|
import me from '@/mixins/me';
|
||||||
import openSidebar from '@/mixins/open-sidebar';
|
import openSidebar from '@/mixins/open-sidebar';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const Logo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Logo');
|
const Logo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Logo'));
|
||||||
const Hamburger = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Hamburger');
|
const Hamburger = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Hamburger'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [me, openSidebar],
|
mixins: [me, openSidebar],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Logo,
|
Logo,
|
||||||
Hamburger,
|
Hamburger,
|
||||||
UserWidget,
|
UserWidget,
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
showMobileNavigation() {
|
|
||||||
this.$store.dispatch('showMobileNavigation', true);
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
methods: {
|
||||||
|
showMobileNavigation() {
|
||||||
|
this.$store.dispatch('showMobileNavigation', true);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</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;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
display: none;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: 0 $medium-spacing;
|
||||||
|
|
||||||
|
&__hamburger {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
fill: $color-silver-dark;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
padding: 0 $medium-spacing;
|
|
||||||
|
|
||||||
&__hamburger {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
fill: $color-silver-dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="modal__backdrop">
|
<div class="modal__backdrop">
|
||||||
<div
|
<div
|
||||||
:class="{
|
:class="{'modal--hide-header': hideHeader || fullscreen, 'modal--fullscreen': fullscreen, 'modal--small': small}"
|
||||||
'modal--hide-header': hideHeader || fullscreen,
|
|
||||||
'modal--fullscreen': fullscreen,
|
|
||||||
'modal--small': small,
|
|
||||||
}"
|
|
||||||
class="modal"
|
class="modal"
|
||||||
>
|
>
|
||||||
<div class="modal__header">
|
<div class="modal__header">
|
||||||
|
|
@ -13,14 +9,20 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal__body">
|
<div class="modal__body">
|
||||||
<slot />
|
<slot />
|
||||||
<div class="modal__close-button" @click="hideModal">
|
<div
|
||||||
|
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--active">Speichern</a>-->
|
<!--<a class="button button--active">Speichern</a>-->
|
||||||
<a class="button" @click="hideModal">Abbrechen</a>
|
<a
|
||||||
|
class="button"
|
||||||
|
@click="hideModal"
|
||||||
|
>Abbrechen</a>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -28,158 +30,160 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const Cross = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
hideHeader: {
|
hideHeader: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false
|
||||||
|
},
|
||||||
|
fullscreen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
small: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
fullscreen: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
small: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Cross,
|
Cross
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
hideModal() {
|
|
||||||
this.$store.dispatch('hideModal');
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
methods: {
|
||||||
|
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;
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
width: 700px;
|
width: 700px;
|
||||||
height: 80vh;
|
height: 80vh;
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.15);
|
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.15);
|
||||||
border: 1px solid $color-silver-light;
|
border: 1px solid $color-silver-light;
|
||||||
display: -ms-grid;
|
display: -ms-grid;
|
||||||
@supports (display: grid) {
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
grid-template-rows: auto 1fr 65px;
|
|
||||||
grid-template-areas: 'header' 'body' 'footer';
|
|
||||||
-ms-grid-rows: auto 1fr 65px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&__backdrop {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
@supports (display: grid) {
|
@supports (display: grid) {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
position: fixed;
|
grid-template-rows: auto 1fr 65px;
|
||||||
top: 0;
|
grid-template-areas: "header" "body" "footer";
|
||||||
left: 0;
|
-ms-grid-rows: auto 1fr 65px;
|
||||||
bottom: 0;
|
position: relative;
|
||||||
right: 0;
|
|
||||||
background-color: rgba($color-white, 0.8);
|
|
||||||
z-index: 90;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header {
|
&__backdrop {
|
||||||
grid-area: header;
|
display: flex;
|
||||||
-ms-grid-row: 1;
|
justify-content: center;
|
||||||
padding: 10px $modal-lateral-padding;
|
@supports (display: grid) {
|
||||||
border-bottom: 1px solid $color-silver-light;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba($color-white, 0.8);
|
||||||
|
z-index: 90;
|
||||||
|
}
|
||||||
|
|
||||||
&__body {
|
&__header {
|
||||||
grid-area: body;
|
grid-area: header;
|
||||||
-ms-grid-row: 2;
|
-ms-grid-row: 1;
|
||||||
padding: 10px $modal-lateral-padding;
|
padding: 10px $modal-lateral-padding;
|
||||||
overflow: auto;
|
border-bottom: 1px solid $color-silver-light;
|
||||||
box-sizing: border-box;
|
}
|
||||||
min-height: 30vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__close-button {
|
&__body {
|
||||||
display: none;
|
grid-area: body;
|
||||||
cursor: pointer;
|
-ms-grid-row: 2;
|
||||||
position: absolute;
|
padding: 10px $modal-lateral-padding;
|
||||||
right: 15px;
|
overflow: auto;
|
||||||
top: 15px;
|
box-sizing: border-box;
|
||||||
background: rgba($color-white, 0.5);
|
min-height: 30vh;
|
||||||
border-radius: 40px;
|
}
|
||||||
padding: 10px;
|
|
||||||
align-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
&__close-button {
|
||||||
grid-area: footer;
|
|
||||||
-ms-grid-row: 3;
|
|
||||||
border-top: 1px solid $color-silver-light;
|
|
||||||
padding: 16px $modal-lateral-padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parent: &;
|
|
||||||
|
|
||||||
&--hide-header {
|
|
||||||
grid-template-rows: 1fr 65px;
|
|
||||||
grid-template-areas: 'body' 'footer';
|
|
||||||
|
|
||||||
#{$parent}__header {
|
|
||||||
display: none;
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 15px;
|
||||||
|
background: rgba($color-white, 0.5);
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 10px;
|
||||||
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$parent}__body {
|
&__footer {
|
||||||
padding: $default-padding;
|
grid-area: footer;
|
||||||
}
|
-ms-grid-row: 3;
|
||||||
}
|
border-top: 1px solid $color-silver-light;
|
||||||
|
padding: 16px $modal-lateral-padding;
|
||||||
&--fullscreen {
|
|
||||||
width: 95vw;
|
|
||||||
height: auto;
|
|
||||||
grid-template-rows: 1fr;
|
|
||||||
-ms-grid-rows: 1fr;
|
|
||||||
grid-template-areas: 'body';
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
#{$parent}__footer {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$parent}__body {
|
$parent: &;
|
||||||
padding: 0;
|
|
||||||
scrollbar-width: none;
|
|
||||||
margin-right: -5px;
|
|
||||||
|
|
||||||
height: auto;
|
&--hide-header {
|
||||||
max-height: 95vh;
|
grid-template-rows: 1fr 65px;
|
||||||
|
grid-template-areas: "body" "footer";
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
#{$parent}__header {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#{$parent}__body {
|
||||||
|
padding: $default-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&--fullscreen {
|
||||||
|
width: 95vw;
|
||||||
|
height: auto;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
-ms-grid-rows: 1fr;
|
||||||
|
grid-template-areas: "body";
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
#{$parent}__footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$parent}__body {
|
||||||
|
padding: 0;
|
||||||
|
scrollbar-width: none;
|
||||||
|
margin-right: -5px;
|
||||||
|
|
||||||
|
height: auto;
|
||||||
|
max-height: 95vh;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$parent}__close-button {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$parent}__close-button {
|
&--small {
|
||||||
display: flex;
|
height: auto;
|
||||||
|
|
||||||
|
#{$parent}__body {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--small {
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
#{$parent}__body {
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,68 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="more-options">
|
<div class="more-options">
|
||||||
<a class="more-options__more-link" data-cy="more-options-link" @click.stop="showMenu = !showMenu">
|
<a
|
||||||
|
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 class="more-options__popover" v-if="showMenu" @hide-me="showMenu = false">
|
<widget-popover
|
||||||
|
class="more-options__popover"
|
||||||
|
v-if="showMenu"
|
||||||
|
@hide-me="showMenu = false"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</widget-popover>
|
</widget-popover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import WidgetPopover from '@/components/ui/WidgetPopover';
|
import WidgetPopover from '@/components/ui/WidgetPopover';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const Ellipses = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Ellipses.vue');
|
const Ellipses = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Ellipses.vue'));
|
||||||
|
|
||||||
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;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
&__ellipses {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
fill: $color-charcoal-dark;
|
|
||||||
margin-top: -7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__more-link {
|
|
||||||
background-color: rgba($color-white, 0.9);
|
|
||||||
width: 35px;
|
|
||||||
height: 15px;
|
|
||||||
border-radius: 15px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: flex-end;
|
||||||
}
|
|
||||||
|
|
||||||
&__popover {
|
&__ellipses {
|
||||||
min-width: 200px;
|
width: 30px;
|
||||||
@include popover-defaults();
|
height: 30px;
|
||||||
|
fill: $color-charcoal-dark;
|
||||||
|
margin-top: -7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__more-link {
|
||||||
|
background-color: rgba($color-white, 0.9);
|
||||||
|
width: 35px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__popover {
|
||||||
|
min-width: 200px;
|
||||||
|
@include popover-defaults();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,79 @@
|
||||||
<template>
|
<template>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<a class="scroll-up" v-if="scroll > 200" @click="scrollTop">
|
<a
|
||||||
|
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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const ArrowUp = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowUp');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const ArrowUp = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowUp'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ArrowUp,
|
ArrowUp
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
scroll: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
let html = document.scrollingElement;
|
|
||||||
document.body.onscroll = () => {
|
|
||||||
this.scroll = html.scrollTop;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyed() {
|
|
||||||
document.body.onscroll = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
scrollTop() {
|
|
||||||
document.scrollingElement.scrollTop = 0;
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
data() {
|
||||||
|
return {
|
||||||
|
scroll: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
let html = document.scrollingElement;
|
||||||
|
document.body.onscroll = () => {
|
||||||
|
this.scroll = html.scrollTop;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
unmounted() {
|
||||||
|
document.body.onscroll = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
scrollTop() {
|
||||||
|
document.scrollingElement.scrollTop = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
</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';
|
||||||
|
|
||||||
.scroll-up {
|
.scroll-up {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: $large-spacing;
|
right: $large-spacing;
|
||||||
bottom: $large-spacing;
|
bottom: $large-spacing;
|
||||||
padding: $medium-spacing;
|
padding: $medium-spacing;
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
@include default-box-shadow;
|
@include default-box-shadow;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
border: 1px solid $color-silver;
|
border: 1px solid $color-silver;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
fill: $color-brand;
|
||||||
|
}
|
||||||
|
|
||||||
&__icon {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
fill: $color-brand;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active,
|
.fade-enter-active, .fade-leave-active {
|
||||||
.fade-leave-active {
|
transition: opacity .3s;
|
||||||
transition: opacity 0.3s;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
.fade-enter-from, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
|
||||||
opacity: 0;
|
{
|
||||||
}
|
opacity: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@
|
||||||
{{ name }}
|
{{ name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="student-submission__entry entry">
|
<div class="student-submission__entry entry">
|
||||||
<p>{{ submission.text | trimToLength(50) }}</p>
|
<p>{{ text }}</p>
|
||||||
<p class="entry__document" v-if="submission.document && submission.document.length > 0">
|
<p class="entry__document" v-if="submission.document && submission.document.length > 0">
|
||||||
<student-submission-document :document="submission.document" class="entry-document" />
|
<student-submission-document :document="submission.document" class="entry-document" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="student-submission__feedback entry" v-if="submission.submissionFeedback">
|
<div class="student-submission__feedback entry" v-if="submission.submissionFeedback">
|
||||||
<p :class="{ 'entry__text--final': submission.submissionFeedback.final }" class="entry__text">
|
<p :class="{ 'entry__text--final': submission.submissionFeedback.final }" class="entry__text">
|
||||||
{{ submission.submissionFeedback.text | trimToLength(50) }}
|
{{ feedback }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -25,7 +25,21 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
StudentSubmissionDocument,
|
StudentSubmissionDocument,
|
||||||
},
|
},
|
||||||
filters: {
|
|
||||||
|
computed: {
|
||||||
|
text() {
|
||||||
|
return this.trimToLength(this.submission.text, 50);
|
||||||
|
},
|
||||||
|
feedback() {
|
||||||
|
return this.trimToLength(this.submission.submissionFeedback.text, 50);
|
||||||
|
},
|
||||||
|
name() {
|
||||||
|
return this.submission && this.submission.student
|
||||||
|
? `${this.submission.student.firstName} ${this.submission.student.lastName}`
|
||||||
|
: '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
trimToLength: function (text, numberOfChars) {
|
trimToLength: function (text, numberOfChars) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return '';
|
return '';
|
||||||
|
|
@ -40,14 +54,6 @@ export default {
|
||||||
return `${text.substring(0, index)}…`;
|
return `${text.substring(0, index)}…`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
|
||||||
name() {
|
|
||||||
return this.submission && this.submission.student
|
|
||||||
? `${this.submission.student.firstName} ${this.submission.student.lastName}`
|
|
||||||
: '';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,44 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="submission-document">
|
<div class="submission-document">
|
||||||
<p class="submission-document__content content" v-if="document && document.length > 0">
|
<p
|
||||||
|
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 {defineAsyncComponent} from 'vue';
|
||||||
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon');
|
import filenameFromUrl from '@/helpers/urls';
|
||||||
|
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'StudentSubmissionDocument',
|
name: 'StudentSubmissionDocument',
|
||||||
props: ['document'],
|
props: ['document'],
|
||||||
components: { DocumentIcon },
|
components: { DocumentIcon },
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
filename() {
|
filename() {
|
||||||
return filenameFromUrl(this.document);
|
return filenameFromUrl(this.document);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,82 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ 'user-widget--is-profile': isProfile }" class="user-widget">
|
<div
|
||||||
<div class="user-widget__avatar" data-cy="user-widget-avatar">
|
:class="{'user-widget--is-profile': isProfile}"
|
||||||
<avatar :avatar-url="avatarUrl" :icon-highlighted="isProfile" />
|
class="user-widget"
|
||||||
|
@click.stop="$emit('click', $event)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="user-widget__avatar"
|
||||||
|
data-cy="user-widget-avatar"
|
||||||
|
>
|
||||||
|
<avatar
|
||||||
|
:avatar-url="avatarUrl"
|
||||||
|
:icon-highlighted="isProfile"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Avatar from '@/components/profile/Avatar';
|
import Avatar from '@/components/profile/Avatar';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
avatarUrl: {
|
avatarUrl: {
|
||||||
type: String,
|
type: String
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
emits: ['click'],
|
||||||
Avatar,
|
|
||||||
},
|
components: {
|
||||||
computed: {
|
Avatar
|
||||||
isProfile() {
|
|
||||||
return this.$route.meta.isProfile;
|
|
||||||
},
|
},
|
||||||
},
|
computed: {
|
||||||
};
|
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;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
// todo: do we need the margin right always? just do it where needed --> content block actions and objecives override this
|
|
||||||
margin-right: $medium-spacing;
|
|
||||||
|
|
||||||
&__popover {
|
|
||||||
top: 40px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
padding: 0px $small-spacing;
|
|
||||||
color: $color-silver-dark;
|
color: $color-silver-dark;
|
||||||
font-family: $sans-serif-font-family;
|
display: flex;
|
||||||
}
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
// todo: do we need the margin right always? just do it where needed --> content block actions and objecives override this
|
||||||
|
margin-right: $medium-spacing;
|
||||||
|
|
||||||
&__date {
|
&__popover {
|
||||||
font-family: $sans-serif-font-family;
|
top: 40px;
|
||||||
}
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
&__avatar {
|
&__name {
|
||||||
width: 30px;
|
padding: 0px $small-spacing;
|
||||||
height: 30px;
|
color: $color-silver-dark;
|
||||||
fill: $color-silver-dark;
|
font-family: $sans-serif-font-family;
|
||||||
cursor: pointer;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&--is-profile {
|
&__date {
|
||||||
& > span {
|
font-family: $sans-serif-font-family;
|
||||||
color: $color-brand;
|
}
|
||||||
|
|
||||||
|
&__avatar {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
fill: $color-silver-dark;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--is-profile {
|
||||||
|
& > span {
|
||||||
|
color: $color-brand;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<nav :class="{ 'content-navigation--sidebar': isSidebar }" class="content-navigation">
|
<nav
|
||||||
|
: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
|
||||||
:class="{ 'content-navigation__link--active': isActive('book') }"
|
:class="{'content-navigation__link--active': isActive('book')}"
|
||||||
:to="topicRoute"
|
:to="topicRoute"
|
||||||
active-class="content-navigation__link--active"
|
active-class="content-navigation__link--active"
|
||||||
class="content-navigation__link"
|
class="content-navigation__link"
|
||||||
@click.native="close"
|
@click="close"
|
||||||
>
|
>
|
||||||
{{ $flavor.textTopics }}
|
{{ $flavor.textTopics }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<topic-navigation v-if="isSidebar" />
|
<topic-navigation
|
||||||
|
v-if="isSidebar"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-navigation__item">
|
<div class="content-navigation__item">
|
||||||
|
|
@ -20,7 +25,7 @@
|
||||||
to="/instruments"
|
to="/instruments"
|
||||||
active-class="content-navigation__link--active"
|
active-class="content-navigation__link--active"
|
||||||
class="content-navigation__link"
|
class="content-navigation__link"
|
||||||
@click.native="close"
|
@click="close"
|
||||||
>
|
>
|
||||||
{{ $flavor.textInstruments }}
|
{{ $flavor.textInstruments }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
@ -28,52 +33,63 @@
|
||||||
|
|
||||||
<div class="content-navigation__item">
|
<div class="content-navigation__item">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'news' }"
|
:to="{name: 'news'}"
|
||||||
active-class="content-navigation__link--active"
|
active-class="content-navigation__link--active"
|
||||||
class="content-navigation__link"
|
class="content-navigation__link"
|
||||||
data-cy="news-navigation-link"
|
data-cy="news-navigation-link"
|
||||||
v-if="!me.readOnly"
|
v-if="!me.readOnly"
|
||||||
@click.native="close"
|
@click="close"
|
||||||
>
|
>
|
||||||
News
|
News
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<router-link to="/" class="content-navigation__logo" data-cy="home-link" v-if="!isSidebar">
|
<router-link
|
||||||
|
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>
|
||||||
|
|
||||||
<div class="content-navigation__secondary">
|
<div class="content-navigation__secondary">
|
||||||
<div class="content-navigation__item content-navigation__item--secondary">
|
<div class="content-navigation__item content-navigation__item--secondary">
|
||||||
<router-link
|
<router-link
|
||||||
:class="{ 'content-navigation__link--active': isRoomUrl() }"
|
:class="{'content-navigation__link--active': isRoomUrl()}"
|
||||||
to="/rooms"
|
to="/rooms"
|
||||||
active-class="content-navigation__link--active"
|
active-class="content-navigation__link--active"
|
||||||
class="content-navigation__link content-navigation__link--secondary"
|
class="content-navigation__link content-navigation__link--secondary"
|
||||||
@click.native="close"
|
@click="close"
|
||||||
>
|
>
|
||||||
Räume
|
Räume
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-navigation__item content-navigation__item--secondary" v-if="showPortfolio">
|
<div
|
||||||
|
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"
|
||||||
class="content-navigation__link content-navigation__link--secondary"
|
class="content-navigation__link content-navigation__link--secondary"
|
||||||
@click.native="close"
|
@click="close"
|
||||||
>
|
>
|
||||||
Portfolio
|
Portfolio
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-navigation__item content-navigation__item--secondary" v-if="isSidebar">
|
<div
|
||||||
|
class="content-navigation__item content-navigation__item--secondary"
|
||||||
|
v-if="isSidebar"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
:href="$flavor.supportLink"
|
:href="$flavor.supportLink"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="content-navigation__link content-navigation__link--secondary"
|
class="content-navigation__link content-navigation__link--secondary"
|
||||||
@click="close"
|
@click="close"
|
||||||
>Support
|
>Support
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -81,142 +97,142 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TopicNavigation from '@/components/book-navigation/TopicNavigation';
|
import TopicNavigation from '@/components/book-navigation/TopicNavigation';
|
||||||
|
|
||||||
import sidebarMixin from '@/mixins/sidebar';
|
import sidebarMixin from '@/mixins/sidebar';
|
||||||
import meMixin from '@/mixins/me';
|
import meMixin from '@/mixins/me';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const Logo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Logo');
|
const Logo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Logo'));
|
||||||
|
|
||||||
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: {
|
||||||
isActive(linkName) {
|
isActive(linkName) {
|
||||||
return linkName === 'book' && this.$route.path.indexOf('module') > -1;
|
return linkName === 'book' && this.$route.path.indexOf('module') > -1;
|
||||||
},
|
},
|
||||||
isRoomUrl() {
|
isRoomUrl() {
|
||||||
return this.$route.path.indexOf('room') > -1;
|
return this.$route.path.indexOf('room') > -1;
|
||||||
},
|
},
|
||||||
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;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
padding: 0 24px;
|
|
||||||
@include navigation-link;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__primary,
|
|
||||||
&__secondary {
|
|
||||||
display: none;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
@include desktop {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__logo {
|
|
||||||
color: #17a887;
|
|
||||||
font-size: 36px;
|
|
||||||
font-weight: 800;
|
|
||||||
font-family: $sans-serif-font-family;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-self: center;
|
align-items: center;
|
||||||
|
|
||||||
/*
|
&__link {
|
||||||
* For IE10+
|
padding: 0 24px;
|
||||||
*/
|
@include navigation-link;
|
||||||
-ms-grid-column: 2;
|
|
||||||
-ms-grid-row-align: center;
|
|
||||||
-ms-grid-column-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__logo-icon {
|
|
||||||
width: auto;
|
|
||||||
height: 31px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
&--secondary {
|
|
||||||
@include regular-text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--active {
|
&__primary, &__secondary {
|
||||||
color: $color-brand;
|
display: none;
|
||||||
}
|
flex-direction: row;
|
||||||
}
|
|
||||||
|
|
||||||
$parent: &;
|
@include desktop {
|
||||||
|
display: flex;
|
||||||
&--sidebar {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
#{$parent}__primary,
|
|
||||||
#{$parent}__secondary {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#{$parent}__link {
|
|
||||||
@include heading-4;
|
|
||||||
line-height: 2.5em;
|
|
||||||
padding: 0;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.5 * $small-spacing;
|
|
||||||
|
|
||||||
&:only-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$parent}__item {
|
&__logo {
|
||||||
width: 100%;
|
color: #17A887;
|
||||||
//border-bottom: 1px solid $color-white;
|
font-size: 36px;
|
||||||
|
font-weight: 800;
|
||||||
|
font-family: $sans-serif-font-family;
|
||||||
|
display: flex;
|
||||||
|
justify-self: center;
|
||||||
|
|
||||||
/*&:nth-child(1) {*/
|
/*
|
||||||
/* order: 3;*/
|
* For IE10+
|
||||||
/* border-bottom: 0;*/
|
*/
|
||||||
/*}*/
|
-ms-grid-column: 2;
|
||||||
|
-ms-grid-row-align: center;
|
||||||
|
-ms-grid-column-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
/*&:nth-child(2) {*/
|
&__logo-icon {
|
||||||
/* order: 1;*/
|
width: auto;
|
||||||
/*}*/
|
height: 31px;
|
||||||
|
}
|
||||||
|
|
||||||
/*&:nth-child(3) {*/
|
&__link {
|
||||||
/* order: 2;*/
|
&--secondary {
|
||||||
/*}*/
|
@include regular-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
color: $color-brand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent: &;
|
||||||
|
|
||||||
|
&--sidebar {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
#{$parent}__primary, #{$parent}__secondary {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$parent}__link {
|
||||||
|
@include heading-4;
|
||||||
|
line-height: 2.5em;
|
||||||
|
padding: 0;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5*$small-spacing;
|
||||||
|
|
||||||
|
&:only-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$parent}__item {
|
||||||
|
width: 100%;
|
||||||
|
//border-bottom: 1px solid $color-white;
|
||||||
|
|
||||||
|
/*&:nth-child(1) {*/
|
||||||
|
/* order: 3;*/
|
||||||
|
/* border-bottom: 0;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*&:nth-child(2) {*/
|
||||||
|
/* order: 1;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*&:nth-child(3) {*/
|
||||||
|
/* order: 2;*/
|
||||||
|
/*}*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<transition name="slide">
|
<transition name="slide">
|
||||||
<div class="navigation-sidebar" v-if="sidebar.navigation" v-click-outside="close">
|
<div
|
||||||
<content-navigation :is-sidebar="true" class="navigation-sidebar__main" />
|
class="navigation-sidebar"
|
||||||
<div class="navigation-sidebar__close-button" @click="close">
|
v-if="sidebar.navigation"
|
||||||
|
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>
|
||||||
|
|
@ -10,94 +20,94 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ContentNavigation from '@/components/book-navigation/ContentNavigation';
|
import ContentNavigation from '@/components/book-navigation/ContentNavigation';
|
||||||
|
|
||||||
import sidebarMixin from '@/mixins/sidebar';
|
import sidebarMixin from '@/mixins/sidebar';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const Cross = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon');
|
const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [sidebarMixin],
|
mixins: [sidebarMixin],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ContentNavigation,
|
ContentNavigation,
|
||||||
Cross,
|
Cross
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
close() {
|
|
||||||
this.closeSidebar('navigation');
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
methods: {
|
||||||
|
close() {
|
||||||
|
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;
|
||||||
|
|
||||||
.navigation-sidebar {
|
.navigation-sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
|
|
||||||
@include desktop {
|
|
||||||
box-shadow: 0px 2px 9px rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
display: grid;
|
|
||||||
|
|
||||||
grid-template-columns: 1fr 50px;
|
|
||||||
grid-template-rows: 50px max-content auto 100px;
|
|
||||||
|
|
||||||
grid-template-areas: 'm m' 'm m' 's s' 's s';
|
|
||||||
|
|
||||||
&--with-subnavigation {
|
|
||||||
grid-template-areas: 'm m' 'm m' 'sub sub' 's s';
|
|
||||||
}
|
|
||||||
|
|
||||||
height: 100vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
@include desktop {
|
|
||||||
width: $desktop-width;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__main {
|
|
||||||
padding: $medium-spacing;
|
|
||||||
grid-area: m;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__main-link {
|
|
||||||
}
|
|
||||||
|
|
||||||
&__close-button {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 2;
|
|
||||||
align-self: center;
|
|
||||||
justify-self: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide {
|
|
||||||
&-enter-active,
|
|
||||||
&-leave-active {
|
|
||||||
transition: left 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter,
|
|
||||||
&-leave-to {
|
|
||||||
left: -100vw;
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
left: -$desktop-width;
|
box-shadow: 0px 2px 9px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
grid-template-columns: 1fr 50px;
|
||||||
|
grid-template-rows: 50px max-content auto 100px;
|
||||||
|
|
||||||
|
grid-template-areas: "m m" "m m" "s s" "s s";
|
||||||
|
|
||||||
|
&--with-subnavigation {
|
||||||
|
grid-template-areas: "m m" "m m" "sub sub" "s s";
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
width: $desktop-width;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__main {
|
||||||
|
padding: $medium-spacing;
|
||||||
|
grid-area: m;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__main-link {
|
||||||
|
}
|
||||||
|
|
||||||
|
&__close-button {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 2;
|
||||||
|
align-self: center;
|
||||||
|
justify-self: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide {
|
||||||
|
&-enter-active, &-leave-active {
|
||||||
|
transition: left 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-from, &-leave-to {
|
||||||
|
left: -100vw;
|
||||||
|
@include desktop {
|
||||||
|
left: -$desktop-width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,55 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ 'sub-navigation-item--active': show }" class="sub-navigation-item" v-click-outside="close">
|
<div
|
||||||
<div class="sub-navigation-item__title" @click="show = !show">
|
:class="{ 'sub-navigation-item--active': 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 class="sub-navigation-item__nav-items book-subnavigation" v-if="show">
|
<div
|
||||||
|
class="sub-navigation-item__nav-items book-subnavigation"
|
||||||
|
v-if="show"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const ChevronDown = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronDown');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const ChevronUp = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronUp');
|
const ChevronDown = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronDown'));
|
||||||
|
const ChevronUp = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronUp'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['title'],
|
props: ['title'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronUp,
|
ChevronUp
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
show: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
$route() {
|
|
||||||
this.show = false;
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
data() {
|
||||||
close() {
|
return {
|
||||||
this.show = false;
|
show: false
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.show = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
close() {
|
||||||
|
this.show = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<nav class="topic-navigation">
|
<nav class="topic-navigation">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'topic', params: { topicSlug: topic.slug } }"
|
:to="{name: 'topic', params: {topicSlug: topic.slug}}"
|
||||||
:class="{ 'topic-navigation__topic--active': topic.active, 'book-subnavigation__item--mobile': mobile }"
|
:class="{'topic-navigation__topic--active': topic.active, 'book-subnavigation__item--mobile': mobile}"
|
||||||
tag="div"
|
tag="div"
|
||||||
active-class="book-subnavigation__item--active"
|
active-class="book-subnavigation__item--active"
|
||||||
class="topic-navigation__topic book-subnavigation__item"
|
class="topic-navigation__topic book-subnavigation__item"
|
||||||
v-for="topic in topics"
|
v-for="topic in topics"
|
||||||
:key="topic.id"
|
:key="topic.id"
|
||||||
@click.native="closeSidebar('navigation')"
|
@click="closeSidebar('navigation')"
|
||||||
>
|
>
|
||||||
{{ topic.order }}.
|
{{ topic.order }}.
|
||||||
{{ topic.title }}
|
{{ topic.title }}
|
||||||
|
|
@ -17,49 +17,49 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ALL_TOPICS_QUERY from '@/graphql/gql/queries/allTopicsQuery.gql';
|
import ALL_TOPICS_QUERY from '@/graphql/gql/queries/allTopicsQuery.gql';
|
||||||
import sidebarMixin from '@/mixins/sidebar';
|
import sidebarMixin from '@/mixins/sidebar';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
mobile: {
|
mobile: {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mixins: [sidebarMixin],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
topics: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
topicId(id) {
|
|
||||||
return atob(id);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
apollo: {
|
|
||||||
topics: {
|
|
||||||
query: ALL_TOPICS_QUERY,
|
|
||||||
manual: true,
|
|
||||||
result({ data, loading }) {
|
|
||||||
if (!loading) {
|
|
||||||
this.topics = this.$getRidOfEdges(data).topics;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
mixins: [sidebarMixin],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
topics: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
topicId(id) {
|
||||||
|
return atob(id);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
apollo: {
|
||||||
|
topics: {
|
||||||
|
query: ALL_TOPICS_QUERY,
|
||||||
|
manual: true,
|
||||||
|
result({data, loading}) {
|
||||||
|
if (!loading) {
|
||||||
|
this.topics = this.$getRidOfEdges(data).topics;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '@/styles/_variables.scss';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.topic-navigation {
|
.topic-navigation {
|
||||||
&__topic {
|
&__topic {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue, { PropType } from 'vue';
|
import { PropType, defineComponent } from 'vue';
|
||||||
import Toggle from '@/components/ui/Toggle.vue';
|
import Toggle from '@/components/ui/Toggle.vue';
|
||||||
import ContentFormSection from '@/components/content-block-form/ContentFormSection.vue';
|
import ContentFormSection from '@/components/content-block-form/ContentFormSection.vue';
|
||||||
import InputWithLabel from '@/components/ui/InputWithLabel.vue';
|
import InputWithLabel from '@/components/ui/InputWithLabel.vue';
|
||||||
|
|
@ -130,7 +130,7 @@ interface ContentBlockFormData {
|
||||||
localContentBlock: any;
|
localContentBlock: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Vue.extend({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,14 @@
|
||||||
: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"
|
||||||
/>
|
/>
|
||||||
|
|
@ -39,321 +43,313 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ContentFormSection from '@/components/content-block-form/ContentFormSection';
|
import ContentFormSection from '@/components/content-block-form/ContentFormSection';
|
||||||
import ContentElementActions from '@/components/content-block-form/ContentElementActions';
|
import ContentElementActions from '@/components/content-block-form/ContentElementActions';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon');
|
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
|
||||||
const ContentBlockElementChooserWidget = () =>
|
const ContentBlockElementChooserWidget = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/ContentBlockElementChooserWidget'));
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/ContentBlockElementChooserWidget');
|
const LinkForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/LinkForm'));
|
||||||
const LinkForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/LinkForm');
|
const VideoForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/VideoForm'));
|
||||||
const VideoForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/VideoForm');
|
const ImageForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/ImageForm'));
|
||||||
const ImageForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/ImageForm');
|
const DocumentForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/DocumentForm'));
|
||||||
const DocumentForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/DocumentForm');
|
const AssignmentForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/AssignmentForm'));
|
||||||
const AssignmentForm = () =>
|
const TextForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/TipTap.vue'));
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/AssignmentForm');
|
const SubtitleForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/SubtitleForm'));
|
||||||
const TextForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/TipTap.vue');
|
// readonly blocks
|
||||||
const SubtitleForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/SubtitleForm');
|
const Assignment = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/assignment/Assignment'));
|
||||||
// readonly blocks
|
const SurveyBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/SurveyBlock'));
|
||||||
const Assignment = () =>
|
const Solution = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/Solution'));
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/assignment/Assignment');
|
const ImageBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/ImageBlock'));
|
||||||
const SurveyBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/SurveyBlock');
|
const Instruction = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/Instruction'));
|
||||||
const Solution = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/Solution');
|
const ModuleRoomSlug = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/ModuleRoomSlug'));
|
||||||
const ImageBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ImageBlock');
|
const CmsDocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/CmsDocumentBlock'));
|
||||||
const Instruction = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/Instruction');
|
const ThinglinkBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/ThinglinkBlock'));
|
||||||
const ModuleRoomSlug = () =>
|
const InfogramBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/InfogramBlock'));
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ModuleRoomSlug');
|
|
||||||
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 CHOOSER = 'content-block-element-chooser-widget';
|
const CHOOSER = 'content-block-element-chooser-widget';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
element: {
|
element: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
|
},
|
||||||
|
// is this element at the top level, or is it nested? we assume top level
|
||||||
|
topLevel: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
firstElement: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
lastElement: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// is this element at the top level, or is it nested? we assume top level
|
|
||||||
topLevel: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
firstElement: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
lastElement: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ContentElementActions,
|
ContentElementActions,
|
||||||
ContentFormSection,
|
ContentFormSection,
|
||||||
TrashIcon,
|
TrashIcon,
|
||||||
ContentBlockElementChooserWidget,
|
ContentBlockElementChooserWidget,
|
||||||
LinkForm,
|
LinkForm,
|
||||||
VideoForm,
|
VideoForm,
|
||||||
ImageForm,
|
ImageForm,
|
||||||
DocumentForm,
|
DocumentForm,
|
||||||
AssignmentForm,
|
AssignmentForm,
|
||||||
TextForm,
|
TextForm,
|
||||||
SubtitleForm,
|
SubtitleForm,
|
||||||
SurveyBlock,
|
SurveyBlock,
|
||||||
Solution,
|
Solution,
|
||||||
ImageBlock,
|
ImageBlock,
|
||||||
Instruction,
|
Instruction,
|
||||||
ModuleRoomSlug,
|
ModuleRoomSlug,
|
||||||
CmsDocumentBlock,
|
CmsDocumentBlock,
|
||||||
InfogramBlock,
|
InfogramBlock,
|
||||||
ThinglinkBlock,
|
ThinglinkBlock,
|
||||||
Assignment,
|
Assignment
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
actions() {
|
actions() {
|
||||||
return {
|
return {
|
||||||
up: !this.firstElement,
|
up: !this.firstElement,
|
||||||
down: !this.lastElement,
|
down: !this.lastElement,
|
||||||
extended: this.topLevel,
|
extended: this.topLevel,
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
isChooser() {
|
||||||
|
return this.component === CHOOSER;
|
||||||
|
},
|
||||||
|
type() {
|
||||||
|
return this.getType(this.element);
|
||||||
|
},
|
||||||
|
component() {
|
||||||
|
return this.type.component;
|
||||||
|
},
|
||||||
|
title() {
|
||||||
|
return this.type.title;
|
||||||
|
},
|
||||||
|
icon() {
|
||||||
|
return this.type.icon;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
isChooser() {
|
|
||||||
return this.component === CHOOSER;
|
|
||||||
},
|
|
||||||
type() {
|
|
||||||
return this.getType(this.element);
|
|
||||||
},
|
|
||||||
component() {
|
|
||||||
return this.type.component;
|
|
||||||
},
|
|
||||||
title() {
|
|
||||||
return this.type.title;
|
|
||||||
},
|
|
||||||
icon() {
|
|
||||||
return this.type.icon;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
getType(element) {
|
getType(element) {
|
||||||
switch (element.type) {
|
switch (element.type) {
|
||||||
case 'subtitle':
|
case 'subtitle':
|
||||||
return {
|
return {
|
||||||
component: 'subtitle-form',
|
component: 'subtitle-form',
|
||||||
title: 'Untertitel',
|
title: 'Untertitel',
|
||||||
icon: 'title-icon',
|
icon: 'title-icon',
|
||||||
};
|
};
|
||||||
case 'link_block':
|
case 'link_block':
|
||||||
return {
|
return {
|
||||||
component: 'link-form',
|
component: 'link-form',
|
||||||
title: 'Link',
|
title: 'Link',
|
||||||
icon: 'link-icon',
|
icon: 'link-icon',
|
||||||
};
|
};
|
||||||
case 'video_block':
|
case 'video_block':
|
||||||
return {
|
return {
|
||||||
component: 'video-form',
|
component: 'video-form',
|
||||||
title: 'Video',
|
title: 'Video',
|
||||||
icon: 'video-icon',
|
icon: 'video-icon',
|
||||||
};
|
};
|
||||||
case 'image_url_block':
|
case 'image_url_block':
|
||||||
return {
|
return {
|
||||||
component: 'image-form',
|
component: 'image-form',
|
||||||
title: 'Bild',
|
title: 'Bild',
|
||||||
icon: 'image-icon',
|
icon: 'image-icon',
|
||||||
};
|
};
|
||||||
case 'text_block':
|
case 'text_block':
|
||||||
return {
|
return {
|
||||||
component: 'text-form',
|
component: 'text-form',
|
||||||
title: 'Text',
|
title: 'Text',
|
||||||
icon: 'text-icon',
|
icon: 'text-icon',
|
||||||
};
|
};
|
||||||
case 'assignment':
|
case 'assignment':
|
||||||
return {
|
return {
|
||||||
component: element.id ? 'assignment' : 'assignment-form', // prevent editing of existing assignments
|
component: element.id ? 'assignment' : 'assignment-form', // prevent editing of existing assignments
|
||||||
title: 'Aufgabe & Ergebnis',
|
title: 'Aufgabe & Ergebnis',
|
||||||
icon: 'speech-bubble-icon',
|
icon: 'speech-bubble-icon',
|
||||||
};
|
};
|
||||||
case 'document_block':
|
case 'document_block':
|
||||||
return {
|
return {
|
||||||
component: 'document-form',
|
component: 'document-form',
|
||||||
title: 'Dokument',
|
title: 'Dokument',
|
||||||
icon: 'document-icon',
|
icon: 'document-icon',
|
||||||
};
|
};
|
||||||
case 'survey':
|
case 'survey':
|
||||||
return {
|
return {
|
||||||
component: 'survey-block',
|
component: 'survey-block',
|
||||||
title: 'Übung',
|
title: 'Übung',
|
||||||
};
|
};
|
||||||
case 'solution':
|
case 'solution':
|
||||||
return {
|
return {
|
||||||
component: 'solution',
|
component: 'solution',
|
||||||
title: 'Lösung',
|
title: 'Lösung',
|
||||||
};
|
};
|
||||||
case 'image_block':
|
case 'image_block':
|
||||||
return {
|
return {
|
||||||
component: 'image-block',
|
component: 'image-block',
|
||||||
title: 'Bild',
|
title: 'Bild',
|
||||||
};
|
};
|
||||||
case 'instruction':
|
case 'instruction':
|
||||||
return {
|
return {
|
||||||
component: 'instruction',
|
component: 'instruction',
|
||||||
title: 'Instruktion',
|
title: 'Instruktion',
|
||||||
};
|
};
|
||||||
case 'module_room_slug':
|
case 'module_room_slug':
|
||||||
return {
|
return {
|
||||||
component: 'module-room-slug',
|
component: 'module-room-slug',
|
||||||
title: 'Raum',
|
title: 'Raum',
|
||||||
};
|
};
|
||||||
case 'cms_document_block':
|
case 'cms_document_block':
|
||||||
return {
|
return {
|
||||||
component: 'cms-document-block',
|
component: 'cms-document-block',
|
||||||
title: 'Dokument',
|
title: 'Dokument',
|
||||||
};
|
};
|
||||||
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 {
|
||||||
component: CHOOSER,
|
component: CHOOSER,
|
||||||
title: '',
|
title: '',
|
||||||
icon: '',
|
icon: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
_updateProperty(value, key) {
|
_updateProperty(value, key) {
|
||||||
// const content = this.localContentBlock.contents[index];
|
// const content = this.localContentBlock.contents[index];
|
||||||
const content = this.element;
|
const content = this.element;
|
||||||
this.update({
|
this.update({
|
||||||
...content,
|
...content,
|
||||||
value: {
|
value: {
|
||||||
...content.value,
|
...content.value,
|
||||||
[key]: value,
|
[key]: value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
changeUrl(value) {
|
changeUrl(value) {
|
||||||
this._updateProperty(value, 'url');
|
this._updateProperty(value, 'url');
|
||||||
},
|
},
|
||||||
changeText(value) {
|
changeText(value) {
|
||||||
this._updateProperty(value, 'text');
|
this._updateProperty(value, 'text');
|
||||||
},
|
},
|
||||||
changeAssignmentTitle(value) {
|
changeAssignmentTitle(value) {
|
||||||
this._updateProperty(value, 'title');
|
this._updateProperty(value, 'title');
|
||||||
},
|
},
|
||||||
changeAssignmentAssignment(value) {
|
changeAssignmentAssignment(value) {
|
||||||
this._updateProperty(value, 'assignment');
|
this._updateProperty(value, 'assignment');
|
||||||
},
|
},
|
||||||
changeType({ type, convertToList }, value) {
|
changeType({type, convertToList}, value) {
|
||||||
let el = {
|
let el = {
|
||||||
type: type,
|
type: type,
|
||||||
value: Object.assign({}, value),
|
value: Object.assign({}, value),
|
||||||
};
|
};
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'subtitle':
|
case 'subtitle':
|
||||||
el = {
|
el = {
|
||||||
...el,
|
...el,
|
||||||
value: {
|
value: {
|
||||||
text: '',
|
text: '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 'text_block':
|
case 'text_block':
|
||||||
el = {
|
el = {
|
||||||
...el,
|
...el,
|
||||||
value: {
|
value: {
|
||||||
text: '',
|
text: '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 'link_block':
|
case 'link_block':
|
||||||
el = {
|
el = {
|
||||||
...el,
|
...el,
|
||||||
value: {
|
value: {
|
||||||
text: '',
|
text: '',
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'video_block':
|
|
||||||
el = {
|
|
||||||
...el,
|
|
||||||
value: {
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'document_block':
|
|
||||||
el = {
|
|
||||||
...el,
|
|
||||||
value: Object.assign(
|
|
||||||
{
|
|
||||||
url: '',
|
url: '',
|
||||||
},
|
},
|
||||||
value
|
};
|
||||||
),
|
break;
|
||||||
};
|
case 'video_block':
|
||||||
break;
|
el = {
|
||||||
case 'image_url_block':
|
...el,
|
||||||
el = {
|
value: {
|
||||||
...el,
|
url: '',
|
||||||
value: {
|
},
|
||||||
url: '',
|
};
|
||||||
},
|
break;
|
||||||
};
|
case 'document_block':
|
||||||
break;
|
el = {
|
||||||
}
|
...el,
|
||||||
|
value: Object.assign({
|
||||||
|
url: '',
|
||||||
|
}, value),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'image_url_block':
|
||||||
|
el = {
|
||||||
|
...el,
|
||||||
|
value: {
|
||||||
|
url: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (convertToList) {
|
if (convertToList) {
|
||||||
el = {
|
el = {
|
||||||
type: 'content_list_item',
|
type: 'content_list_item',
|
||||||
contents: [el],
|
contents: [el],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.update(el);
|
this.update(el);
|
||||||
|
},
|
||||||
|
update(element) {
|
||||||
|
this.$emit('update', element);
|
||||||
|
},
|
||||||
|
switchToDocument(value) {
|
||||||
|
this.changeType('document_block', value);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
update(element) {
|
};
|
||||||
this.$emit('update', element);
|
|
||||||
},
|
|
||||||
switchToDocument(value) {
|
|
||||||
this.changeType('document_block', value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.content-element {
|
.content-element {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
&__actions {
|
&__actions {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-self: flex-end;
|
justify-self: flex-end;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__section {
|
||||||
|
display: grid;
|
||||||
|
//grid-template-columns: 1fr 50px;
|
||||||
|
grid-auto-rows: auto;
|
||||||
|
/*width: 95%; // reserve space for scrollbar*/
|
||||||
|
}
|
||||||
|
|
||||||
|
&__chooser {
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__section {
|
|
||||||
display: grid;
|
|
||||||
//grid-template-columns: 1fr 50px;
|
|
||||||
grid-auto-rows: auto;
|
|
||||||
/*width: 95%; // reserve space for scrollbar*/
|
|
||||||
}
|
|
||||||
|
|
||||||
&__chooser {
|
|
||||||
grid-column: 1 / span 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
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';
|
||||||
|
|
@ -65,7 +65,7 @@ interface Data {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Vue.extend({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
actions: {
|
actions: {
|
||||||
type: Object as () => ActionOptions,
|
type: Object as () => ActionOptions,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
<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 class="content-form-section__icon" :is="icon" />
|
<component
|
||||||
<span class="content-form-section__title" data-cy="content-form-section-title">{{ title }}</span>
|
class="content-form-section__icon"
|
||||||
|
:is="icon"
|
||||||
|
/> <span
|
||||||
|
class="content-form-section__title"
|
||||||
|
data-cy="content-form-section-title"
|
||||||
|
>{{ title }}</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<content-element-actions
|
<content-element-actions
|
||||||
|
|
@ -22,73 +27,74 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import formElementIcons from '@/components/ui/form-element-icons.js';
|
import {defineComponent} from "vue";
|
||||||
import ContentElementActions from '@/components/content-block-form/ContentElementActions.vue';
|
import formElementIcons from '@/components/ui/form-element-icons.js';
|
||||||
import { ActionOptions } from '@/@types';
|
import ContentElementActions from '@/components/content-block-form/ContentElementActions.vue';
|
||||||
|
import {ActionOptions} from "@/@types";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: ''
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
type: Object as () => ActionOptions,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
icon: {
|
components: {
|
||||||
type: String,
|
ContentElementActions,
|
||||||
default: '',
|
...formElementIcons
|
||||||
},
|
}
|
||||||
actions: {
|
});
|
||||||
type: Object as () => ActionOptions,
|
|
||||||
default: () => {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
ContentElementActions,
|
|
||||||
...formElementIcons,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.content-form-section {
|
.content-form-section {
|
||||||
@include default-box-shadow;
|
@include default-box-shadow;
|
||||||
border-radius: $default-border-radius;
|
border-radius: $default-border-radius;
|
||||||
padding: $small-spacing $medium-spacing;
|
padding: $small-spacing $medium-spacing;
|
||||||
margin-bottom: $medium-spacing;
|
margin-bottom: $medium-spacing;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
grid-template-rows: auto;
|
grid-template-rows: auto;
|
||||||
grid-template-areas: 'h a' 'c c';
|
grid-template-areas: 'h a' 'c c';
|
||||||
align-items: center;
|
|
||||||
grid-row-gap: $medium-spacing;
|
|
||||||
|
|
||||||
&__heading {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-area: h;
|
grid-row-gap: $medium-spacing;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__actions {
|
&__heading {
|
||||||
grid-area: a;
|
display: flex;
|
||||||
justify-self: end;
|
align-items: center;
|
||||||
}
|
grid-area: h;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&__title {
|
&__actions {
|
||||||
@include heading-3;
|
grid-area: a;
|
||||||
margin-bottom: 0;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
&__title {
|
||||||
width: 28px;
|
@include heading-3;
|
||||||
height: 28px;
|
margin-bottom: 0;
|
||||||
margin-right: $small-spacing;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
&__icon {
|
||||||
grid-area: c;
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin-right: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
grid-area: c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -18,150 +18,167 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<add-content-element :index="-1" class="contents-form__add" @add-element="addElement" />
|
<add-content-element
|
||||||
<div class="contents-form__element" v-for="(element, index) in localContentBlock.contents" :key="index">
|
:index="-1"
|
||||||
<content-element :element="element" @update="update(index, $event)" @remove="remove(index)" />
|
class="contents-form__add"
|
||||||
|
@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 :index="index" class="contents-form__add" @add-element="addElement" />
|
<add-content-element
|
||||||
|
:index="index"
|
||||||
|
class="contents-form__add"
|
||||||
|
@add-element="addElement"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
:class="{ 'button--disabled': disableSave }"
|
:class="{'button--disabled': disableSave}"
|
||||||
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
|
||||||
<a class="button" @click="$emit('hide')">Abbrechen</a>
|
class="button"
|
||||||
|
@click="$emit('hide')"
|
||||||
|
>Abbrechen</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { meQuery } from '@/graphql/queries';
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
import {meQuery} from '@/graphql/queries';
|
||||||
|
|
||||||
const ModalInput = () => import(/* webpackChunkName: "content-forms" */ '@/components/ModalInput');
|
const ModalInput = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/ModalInput'));
|
||||||
const AddContentElement = () => import(/* webpackChunkName: "content-forms" */ '@/components/AddContentElement');
|
const AddContentElement = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/AddContentElement'));
|
||||||
const ContentElement = () =>
|
const ContentElement = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-block-form/ContentElement'));
|
||||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/ContentElement');
|
|
||||||
|
|
||||||
const Modal = () => import('@/components/Modal.vue');
|
const Modal = defineAsyncComponent(() => import('@/components/Modal'));
|
||||||
const Checkbox = () => import('@/components/ui/Checkbox.vue');
|
const Checkbox = defineAsyncComponent(() => import('@/components/ui/Checkbox'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
contentBlock: Object,
|
contentBlock: Object,
|
||||||
blockType: {
|
blockType: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'ContentBlock',
|
default: 'ContentBlock',
|
||||||
|
},
|
||||||
|
showTaskSelection: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disableSave: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
showTaskSelection: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
disableSave: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ContentElement,
|
ContentElement,
|
||||||
Modal,
|
Modal,
|
||||||
ModalInput,
|
ModalInput,
|
||||||
AddContentElement,
|
AddContentElement,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
},
|
},
|
||||||
|
|
||||||
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: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
apollo: {
|
||||||
|
me: meQuery,
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
titlePlaceholder() {
|
||||||
|
return this.blockType === 'RoomEntry' ? 'Titel für Raumeintrag erfassen' : 'Titel für Inhaltsblock erfassen';
|
||||||
|
},
|
||||||
|
taskSelection() {
|
||||||
|
return this.showTaskSelection && this.me.permissions.includes('users.can_manage_school_class_content');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setContentBlockType(checked) {
|
||||||
|
this.localContentBlock.isAssignment = checked;
|
||||||
|
},
|
||||||
|
update(index, element) {
|
||||||
|
this.localContentBlock.contents.splice(index, 1, element);
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
if (!this.disableSave) {
|
||||||
|
if (!this.localContentBlock.title) {
|
||||||
|
this.error = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.$emit('save', this.localContentBlock);
|
||||||
}
|
}
|
||||||
),
|
},
|
||||||
me: {},
|
updateTitle(title) {
|
||||||
};
|
this.localContentBlock.title = title;
|
||||||
},
|
this.error = false;
|
||||||
|
},
|
||||||
|
addElement(index) {
|
||||||
|
this.localContentBlock.contents.splice(index + 1, 0, {
|
||||||
|
hideAssignment: this.blockType !== 'ContentBlock',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
remove(index) {
|
||||||
|
this.localContentBlock.contents.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
apollo: {
|
|
||||||
me: meQuery,
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
titlePlaceholder() {
|
|
||||||
return this.blockType === 'RoomEntry' ? 'Titel für Raumeintrag erfassen' : 'Titel für Inhaltsblock erfassen';
|
|
||||||
},
|
},
|
||||||
taskSelection() {
|
};
|
||||||
return this.showTaskSelection && this.me.permissions.includes('users.can_manage_school_class_content');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setContentBlockType(checked) {
|
|
||||||
this.localContentBlock.isAssignment = checked;
|
|
||||||
},
|
|
||||||
update(index, element) {
|
|
||||||
this.localContentBlock.contents.splice(index, 1, element);
|
|
||||||
},
|
|
||||||
save() {
|
|
||||||
if (!this.disableSave) {
|
|
||||||
if (!this.localContentBlock.title) {
|
|
||||||
this.error = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.$emit('save', this.localContentBlock);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateTitle(title) {
|
|
||||||
this.localContentBlock.title = title;
|
|
||||||
this.error = false;
|
|
||||||
},
|
|
||||||
addElement(index) {
|
|
||||||
this.localContentBlock.contents.splice(index + 1, 0, {
|
|
||||||
hideAssignment: this.blockType !== 'ContentBlock',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
remove(index) {
|
|
||||||
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 {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__remove {
|
||||||
|
}
|
||||||
|
|
||||||
|
&__trash-icon {
|
||||||
|
}
|
||||||
|
|
||||||
|
&__add {
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__task {
|
||||||
|
margin: 15px 0 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__element-component {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__remove {
|
|
||||||
}
|
|
||||||
|
|
||||||
&__trash-icon {
|
|
||||||
}
|
|
||||||
|
|
||||||
&__add {
|
|
||||||
grid-column: 1 / span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__task {
|
|
||||||
margin: 15px 0 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,77 @@
|
||||||
<template>
|
<template>
|
||||||
<contents-form :content-block="contentBlock" :show-task-selection="true" @save="saveContentBlock" @hide="hideModal" />
|
<contents-form
|
||||||
|
:content-block="contentBlock"
|
||||||
|
:show-task-selection="true"
|
||||||
|
@save="saveContentBlock"
|
||||||
|
@hide="hideModal"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ContentsForm from '@/components/content-block-form/ContentsForm';
|
import ContentsForm from '@/components/content-block-form/ContentsForm';
|
||||||
|
|
||||||
import store from '@/store/index';
|
import {store} from '@/store';
|
||||||
|
|
||||||
import EDIT_CONTENT_BLOCK_MUTATION from 'gql/mutations/editContentBlock.gql';
|
import EDIT_CONTENT_BLOCK_MUTATION from 'gql/mutations/editContentBlock.gql';
|
||||||
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
|
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
|
||||||
import CONTENT_BLOCK_QUERY from '@/graphql/gql/queries/contentBlockQuery.gql';
|
import CONTENT_BLOCK_QUERY from '@/graphql/gql/queries/contentBlockQuery.gql';
|
||||||
import { setUserBlockType } from '@/helpers/content-block';
|
import {setUserBlockType} from '@/helpers/content-block';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ContentsForm,
|
ContentsForm,
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
contentBlock: {},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
// debugger;
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
hideModal() {
|
|
||||||
this.$store.dispatch('resetCurrentNoteBlock');
|
|
||||||
this.$store.dispatch('hideModal');
|
|
||||||
},
|
},
|
||||||
saveContentBlock(contentBlock) {
|
|
||||||
this.$apollo
|
data() {
|
||||||
.mutate({
|
return {
|
||||||
|
contentBlock: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
// debugger;
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
hideModal() {
|
||||||
|
this.$store.dispatch('resetCurrentNoteBlock');
|
||||||
|
this.$store.dispatch('hideModal');
|
||||||
|
},
|
||||||
|
saveContentBlock(contentBlock) {
|
||||||
|
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: {
|
||||||
contentBlock() {
|
contentBlock() {
|
||||||
return {
|
return {
|
||||||
query: CONTENT_BLOCK_QUERY,
|
query: CONTENT_BLOCK_QUERY,
|
||||||
variables: {
|
variables: {
|
||||||
id: store.state.currentNoteBlock,
|
id: store.state.currentNoteBlock,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="componentClass" :data-scrollto="component.id" data-cy="content-component">
|
<div
|
||||||
|
:class="componentClass"
|
||||||
|
:data-scrollto="component.id"
|
||||||
|
data-cy="content-component"
|
||||||
|
>
|
||||||
<bookmark-actions
|
<bookmark-actions
|
||||||
:bookmarked="bookmarked"
|
:bookmarked="bookmarked"
|
||||||
:note="note"
|
:note="note"
|
||||||
|
|
@ -9,105 +13,97 @@
|
||||||
@edit-note="editNote"
|
@edit-note="editNote"
|
||||||
@bookmark="bookmarkContent(component.id, !bookmarked)"
|
@bookmark="bookmarkContent(component.id, !bookmarked)"
|
||||||
/>
|
/>
|
||||||
<component v-bind="component" :parent="parent" :is="component.type" />
|
<component
|
||||||
|
v-bind="component"
|
||||||
|
:parent="parent"
|
||||||
|
:is="component.type"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { constructContentComponentBookmarkMutation } from '@/helpers/update-content-bookmark-mutation';
|
import {constructContentComponentBookmarkMutation} from '@/helpers/update-content-bookmark-mutation';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const TextBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/TextBlock');
|
const TextBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/TextBlock'));
|
||||||
const InstrumentWidget = () =>
|
const InstrumentWidget = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/InstrumentWidget'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/InstrumentWidget');
|
const ImageBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ImageBlock'));
|
||||||
const ImageBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ImageBlock');
|
const ImageUrlBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ImageUrlBlock'));
|
||||||
const ImageUrlBlock = () =>
|
const VideoBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/VideoBlock'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ImageUrlBlock');
|
const LinkBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/LinkBlock'));
|
||||||
const VideoBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/VideoBlock');
|
const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'));
|
||||||
const LinkBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/LinkBlock');
|
const CmsDocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/CmsDocumentBlock'));
|
||||||
const DocumentBlock = () =>
|
const InfogramBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/InfogramBlock'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/DocumentBlock');
|
const ThinglinkBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ThinglinkBlock'));
|
||||||
const CmsDocumentBlock = () =>
|
const GeniallyBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/GeniallyBlock'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/CmsDocumentBlock');
|
const SubtitleBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/SubtitleBlock'));
|
||||||
const InfogramBlock = () =>
|
const SectionTitleBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/SectionTitleBlock'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/InfogramBlock');
|
const ContentListBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ContentListBlock'));
|
||||||
const ThinglinkBlock = () =>
|
const ModuleRoomSlug = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ModuleRoomSlug'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ThinglinkBlock');
|
const Assignment = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/assignment/Assignment'));
|
||||||
const GeniallyBlock = () =>
|
const Survey = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/SurveyBlock'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/GeniallyBlock');
|
const Solution = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/Solution'));
|
||||||
const SubtitleBlock = () =>
|
const Instruction = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/Instruction'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/SubtitleBlock');
|
const BookmarkActions = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/notes/BookmarkActions'));
|
||||||
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 Solution = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Solution');
|
|
||||||
const Instruction = () =>
|
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Instruction');
|
|
||||||
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() {
|
||||||
|
|
@ -119,19 +115,18 @@ 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
|
? this.parent.__typename : 'ContentBlockNode';
|
||||||
: 'ContentBlockNode';
|
|
||||||
|
|
||||||
this.$store.dispatch('addNote', {
|
this.$store.dispatch('addNote', {
|
||||||
content: id,
|
content: id,
|
||||||
type,
|
type,
|
||||||
block: this.root,
|
block: this.root
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
editNote() {
|
editNote() {
|
||||||
|
|
@ -139,36 +134,37 @@ 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 {
|
||||||
|
margin-top: $section-spacing;
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--section_title {
|
||||||
|
margin-top: $section-spacing;
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--text_block {
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--document_block {
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--subtitle {
|
|
||||||
margin-top: $section-spacing;
|
|
||||||
margin-bottom: $large-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--section_title {
|
|
||||||
margin-top: $section-spacing;
|
|
||||||
margin-bottom: $large-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--text_block {
|
|
||||||
margin-bottom: $large-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--document_block {
|
|
||||||
margin-bottom: $large-spacing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,43 @@
|
||||||
<template>
|
<template>
|
||||||
<content-list :items="contentBlocks">
|
<content-list
|
||||||
|
:items="contentBlocks"
|
||||||
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<content-block :content-block="item" :parent="parent" />
|
<content-block
|
||||||
|
:content-block="item"
|
||||||
|
:parent="parent"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</content-list>
|
</content-list>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ContentList from '@/components/content-blocks/ContentList.vue';
|
import {defineAsyncComponent} from 'vue';
|
||||||
export default {
|
const ContentList = defineAsyncComponent(() => import('@/components/content-blocks/ContentList'));
|
||||||
name: 'ContentBlockList',
|
const ContentBlock = defineAsyncComponent(() => import('@/components/ContentBlock'));
|
||||||
props: ['contents', 'parent'],
|
|
||||||
|
|
||||||
components: {
|
export default {
|
||||||
ContentList,
|
name: 'ContentBlockList',
|
||||||
// https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
|
props: ['contents', 'parent'],
|
||||||
ContentBlock: () => import('@/components/ContentBlock.vue'),
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
components: {
|
||||||
contentBlocks() {
|
ContentList,
|
||||||
return this.contents.map((contentBlock) => {
|
ContentBlock
|
||||||
const contents = contentBlock.value ? [...contentBlock.value] : [];
|
|
||||||
return Object.assign({}, contentBlock, {
|
|
||||||
contents,
|
|
||||||
indent: true,
|
|
||||||
bookmarks: this.parent.bookmarks,
|
|
||||||
notes: this.parent.notes,
|
|
||||||
root: this.parent.id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
computed: {
|
||||||
@import '~styles/helpers';
|
contentBlocks() {
|
||||||
</style>
|
return this.contents.map(contentBlock => {
|
||||||
|
const contents = contentBlock.value ? [...contentBlock.value] : [];
|
||||||
|
return Object.assign({}, contentBlock, {
|
||||||
|
contents,
|
||||||
|
indent: true,
|
||||||
|
bookmarks: this.parent.bookmarks,
|
||||||
|
notes: this.parent.notes,
|
||||||
|
root: this.parent.id
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,71 +1,80 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="document-block">
|
<div class="document-block">
|
||||||
<document-icon class="document-block__icon" />
|
<document-icon class="document-block__icon" />
|
||||||
<a :href="value.url" class="document-block__link" target="_blank">{{ urlName }}</a>
|
<a
|
||||||
<a class="document-block__remove" v-if="showTrashIcon" @click="$emit('trash')">
|
:href="value.url"
|
||||||
|
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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon');
|
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
|
||||||
|
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: Object,
|
value: Object,
|
||||||
showTrashIcon: Boolean,
|
showTrashIcon: Boolean,
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
DocumentIcon,
|
|
||||||
TrashIcon,
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
urlName: function () {
|
|
||||||
if (this.value && this.value.url) {
|
|
||||||
const parts = this.value.url.split('/');
|
|
||||||
return parts[parts.length - 1];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
components: {
|
||||||
|
DocumentIcon,
|
||||||
|
TrashIcon,
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
urlName: function() {
|
||||||
|
if (this.value && this.value.url) {
|
||||||
|
const parts = this.value.url.split('/');
|
||||||
|
return parts[parts.length - 1];
|
||||||
|
}
|
||||||
|
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;
|
||||||
grid-template-columns: 50px 1fr 50px;
|
grid-template-columns: 50px 1fr 50px;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&__icon {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__remove {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__trash-icon {
|
&__icon {
|
||||||
width: 25px;
|
width: 30px;
|
||||||
height: 25px;
|
height: 30px;
|
||||||
fill: $color-silver-dark;
|
}
|
||||||
cursor: pointer;
|
|
||||||
justify-self: center;
|
&__link {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__remove {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__trash-icon {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
fill: $color-silver-dark;
|
||||||
|
cursor: pointer;
|
||||||
|
justify-self: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,57 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="instruction" v-if="me.isTeacher">
|
<div
|
||||||
|
class="instruction"
|
||||||
|
v-if="me.isTeacher"
|
||||||
|
>
|
||||||
<bulb-icon class="instruction__icon" />
|
<bulb-icon class="instruction__icon" />
|
||||||
<a :href="url" class="instruction__link">{{ text }}</a>
|
<a
|
||||||
|
:href="url"
|
||||||
|
class="instruction__link"
|
||||||
|
>{{ text }}</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import me from '@/mixins/me';
|
import me from '@/mixins/me';
|
||||||
const BulbIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/BulbIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const BulbIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/BulbIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['value'],
|
props: ['value'],
|
||||||
|
|
||||||
mixins: [me],
|
mixins: [me],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
BulbIcon,
|
BulbIcon
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
text() {
|
|
||||||
return this.value.text ? this.value.text : 'Anweisungen';
|
|
||||||
},
|
},
|
||||||
url() {
|
|
||||||
return this.value.document ? this.value.document.url : this.value.url;
|
computed: {
|
||||||
},
|
text() {
|
||||||
},
|
return this.value.text ? this.value.text : 'Anweisungen';
|
||||||
};
|
},
|
||||||
|
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;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
margin-right: $small-spacing;
|
margin-right: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
@include heading-3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
|
||||||
@include heading-3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,60 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ 'link-block--no-margin': noMargin }" class="link-block">
|
<div
|
||||||
|
:class="{ 'link-block--no-margin': noMargin}"
|
||||||
|
class="link-block"
|
||||||
|
>
|
||||||
<link-icon class="link-block__icon" />
|
<link-icon class="link-block__icon" />
|
||||||
<a :href="href" class="link-block__link" target="_blank">{{ value.text }}</a>
|
<a
|
||||||
|
:href="href"
|
||||||
|
class="link-block__link"
|
||||||
|
target="_blank"
|
||||||
|
>{{ value.text }}</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const LinkIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/LinkIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const LinkIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LinkIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: Object,
|
value: Object,
|
||||||
noMargin: {
|
noMargin: {
|
||||||
default: false,
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
LinkIcon,
|
LinkIcon
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
href() {
|
|
||||||
const url = this.value.url;
|
|
||||||
return url.startsWith('http') ? this.value.url : `http://${this.value.url}`;
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
computed: {
|
||||||
|
href() {
|
||||||
|
const url = this.value.url;
|
||||||
|
return url.startsWith('http') ? this.value.url : `http://${this.value.url}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.link-block {
|
.link-block {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 50px 1fr;
|
grid-template-columns: 50px 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&--no-margin {
|
&--no-margin {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,95 +1,114 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<div class="solution" data-cy="solution">
|
<div
|
||||||
<a class="solution__toggle" data-cy="show-solution" @click="toggle"
|
class="solution"
|
||||||
>Lösung
|
data-cy="solution"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="solution__toggle"
|
||||||
|
data-cy="show-solution"
|
||||||
|
@click="toggle"
|
||||||
|
>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 class="solution__hidden fade" v-if="visible">
|
<div
|
||||||
<p class="solution__text solution-text" data-cy="solution-text" v-html="sanitizedText" />
|
class="solution__hidden fade"
|
||||||
<cms-document-block :solution="true" class="solution__document" :value="value.document" v-if="value.document" />
|
v-if="visible"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { sanitizeAsHtml } from '@/helpers/text';
|
import {sanitizeAsHtml} from '@/helpers/text';
|
||||||
import CmsDocumentBlock from '@/components/content-blocks/CmsDocumentBlock';
|
import CmsDocumentBlock from '@/components/content-blocks/CmsDocumentBlock';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['value'],
|
props: ['value'],
|
||||||
components: { CmsDocumentBlock },
|
components: {CmsDocumentBlock},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
sanitizedText() {
|
|
||||||
return sanitizeAsHtml(this.value.text);
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
computed: {
|
||||||
toggle() {
|
sanitizedText() {
|
||||||
this.visible = !this.visible;
|
return sanitizeAsHtml(this.value.text);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
methods: {
|
||||||
|
toggle() {
|
||||||
|
this.visible = !this.visible;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.solution {
|
.solution {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-rows: auto;
|
grid-auto-rows: auto;
|
||||||
grid-row-gap: 15px;
|
grid-row-gap: 15px;
|
||||||
|
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
&__toggle {
|
&__toggle {
|
||||||
font-family: $sans-serif-font-family;
|
font-family: $sans-serif-font-family;
|
||||||
color: $color-silver-dark;
|
|
||||||
font-size: toRem(15px);
|
|
||||||
/*margin-bottom: 15px;*/
|
|
||||||
display: block;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: $font-weight-regular;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
font-size: toRem(18px);
|
|
||||||
color: $color-silver-dark;
|
|
||||||
|
|
||||||
:deep(p) {
|
|
||||||
font-size: toRem(18px);
|
|
||||||
color: $color-silver-dark;
|
color: $color-silver-dark;
|
||||||
|
font-size: toRem(15px);
|
||||||
|
/*margin-bottom: 15px;*/
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: $font-weight-regular;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(ul) {
|
&__text {
|
||||||
padding-left: $medium-spacing;
|
font-size: toRem(18px);
|
||||||
|
color: $color-silver-dark;
|
||||||
|
|
||||||
> li {
|
/deep/ p {
|
||||||
list-style: disc outside none;
|
font-size: toRem(18px);
|
||||||
color: $color-silver-dark;
|
color: $color-silver-dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/deep/ ul {
|
||||||
|
padding-left: $medium-spacing;
|
||||||
|
|
||||||
|
> li {
|
||||||
|
list-style: disc outside none;
|
||||||
|
color: $color-silver-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity 0.3s;
|
transition: opacity .3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter,
|
.fade-enter-from,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<div :data-scrollto="value.id" class="assignment">
|
<div
|
||||||
<p class="assignment__main-text" data-cy="assignment-main-text" v-html="assignment.assignment" />
|
:data-scrollto="value.id"
|
||||||
|
class="assignment"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="assignment__main-text"
|
||||||
|
data-cy="assignment-main-text"
|
||||||
|
v-html="assignment.assignment"
|
||||||
|
/>
|
||||||
|
|
||||||
<solution :value="solution" v-if="assignment.solution" />
|
<solution
|
||||||
|
:value="solution"
|
||||||
|
v-if="assignment.solution"
|
||||||
|
/>
|
||||||
|
|
||||||
<template v-if="isStudent">
|
<template v-if="isStudent">
|
||||||
<submission-form
|
<submission-form
|
||||||
|
|
@ -23,92 +33,101 @@
|
||||||
@spellcheck="spellcheck"
|
@spellcheck="spellcheck"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<spell-check :corrections="corrections" :text="submission.text" />
|
<spell-check
|
||||||
|
:corrections="corrections"
|
||||||
|
:text="submission.text"
|
||||||
|
/>
|
||||||
|
|
||||||
<p class="assignment__feedback" v-if="assignment.submission.submissionFeedback" v-html="feedbackText" />
|
<p
|
||||||
|
class="assignment__feedback"
|
||||||
|
v-if="assignment.submission.submissionFeedback"
|
||||||
|
v-html="feedbackText"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!isStudent">
|
<template v-if="!isStudent">
|
||||||
<router-link :to="{ name: 'submissions', params: { id: assignment.id } }" class="button button--primary">
|
<router-link
|
||||||
Zu den Ergebnissen
|
:to="{name: 'submissions', params: { id: assignment.id }}"
|
||||||
|
class="button button--primary"
|
||||||
|
>
|
||||||
|
Zu den
|
||||||
|
Ergebnissen
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import {mapActions, mapGetters} from 'vuex';
|
||||||
import ASSIGNMENT_QUERY from '@/graphql/gql/queries/assignmentQuery.gql';
|
import ASSIGNMENT_QUERY from '@/graphql/gql/queries/assignmentQuery.gql';
|
||||||
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
||||||
import UPDATE_ASSIGNMENT_MUTATION from '@/graphql/gql/mutations/updateAssignmentMutation.gql';
|
import UPDATE_ASSIGNMENT_MUTATION from '@/graphql/gql/mutations/updateAssignmentMutation.gql';
|
||||||
import UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS from '@/graphql/gql/mutations/updateAssignmentMutationWithSuccess.gql';
|
import UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS from '@/graphql/gql/mutations/updateAssignmentMutationWithSuccess.gql';
|
||||||
import SPELL_CHECK_MUTATION from '@/graphql/gql/mutations/spellCheck.gql';
|
import SPELL_CHECK_MUTATION from '@/graphql/gql/mutations/spellCheck.gql';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import { sanitize } from '@/helpers/text';
|
import {sanitize} from '@/helpers/text';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const SubmissionForm = () =>
|
const SubmissionForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/assignment/SubmissionForm'));
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/assignment/SubmissionForm');
|
const Solution = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/Solution'));
|
||||||
const Solution = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Solution');
|
const SpellCheck = defineAsyncComponent(() => 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'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Solution,
|
Solution,
|
||||||
SubmissionForm,
|
SubmissionForm,
|
||||||
SpellCheck,
|
SpellCheck,
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
assignment: {
|
|
||||||
submission: this.initialSubmission(),
|
|
||||||
},
|
|
||||||
me: {
|
|
||||||
permissions: [],
|
|
||||||
},
|
|
||||||
inputType: 'text',
|
|
||||||
unsaved: false,
|
|
||||||
saving: 0,
|
|
||||||
corrections: '',
|
|
||||||
spellcheckLoading: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(['scrollToAssignmentId']),
|
|
||||||
final() {
|
|
||||||
return !!this.submission && this.submission.final;
|
|
||||||
},
|
},
|
||||||
submission() {
|
|
||||||
return this.assignment.submission ? this.assignment.submission : {};
|
data() {
|
||||||
},
|
|
||||||
isStudent() {
|
|
||||||
return !this.me.permissions.includes('users.can_manage_school_class_content');
|
|
||||||
},
|
|
||||||
solution() {
|
|
||||||
return {
|
return {
|
||||||
text: this.assignment.solution,
|
assignment: {
|
||||||
|
submission: this.initialSubmission(),
|
||||||
|
},
|
||||||
|
me: {
|
||||||
|
permissions: [],
|
||||||
|
},
|
||||||
|
inputType: 'text',
|
||||||
|
unsaved: false,
|
||||||
|
saving: 0,
|
||||||
|
corrections: '',
|
||||||
|
spellcheckLoading: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
id() {
|
|
||||||
return this.assignment.id ? this.assignment.id.replace(/=/g, '') : '';
|
|
||||||
},
|
|
||||||
feedbackText() {
|
|
||||||
let feedback = this.assignment.submission.submissionFeedback;
|
|
||||||
let sanitizedFeedbackText = sanitize(feedback.text);
|
|
||||||
return `<span class="inline-title">Feedback von ${feedback.teacher.firstName} ${feedback.teacher.lastName}:</span> ${sanitizedFeedbackText}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
computed: {
|
||||||
...mapActions(['scrollToAssignmentReady']),
|
...mapGetters(['scrollToAssignmentId']),
|
||||||
_save: debounce(function (submission) {
|
final() {
|
||||||
this.saving++;
|
return !!this.submission && this.submission.final;
|
||||||
this.$apollo
|
},
|
||||||
.mutate({
|
submission() {
|
||||||
|
return this.assignment.submission ? this.assignment.submission : {};
|
||||||
|
},
|
||||||
|
isStudent() {
|
||||||
|
return !this.me.permissions.includes('users.can_manage_school_class_content');
|
||||||
|
},
|
||||||
|
solution() {
|
||||||
|
return {
|
||||||
|
text: this.assignment.solution,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
id() {
|
||||||
|
return this.assignment.id ? this.assignment.id.replace(/=/g, '') : '';
|
||||||
|
},
|
||||||
|
feedbackText() {
|
||||||
|
let feedback = this.assignment.submission.submissionFeedback;
|
||||||
|
let sanitizedFeedbackText = sanitize(feedback.text);
|
||||||
|
return `<span class="inline-title">Feedback von ${feedback.teacher.firstName} ${feedback.teacher.lastName}:</span> ${sanitizedFeedbackText}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
...mapActions(['scrollToAssignmentReady']),
|
||||||
|
_save: debounce(function (submission) {
|
||||||
|
this.saving++;
|
||||||
|
this.$apollo.mutate({
|
||||||
mutation: UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS,
|
mutation: UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
|
|
@ -119,14 +138,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
update(
|
update(store, {data: {updateAssignment: {successful, updatedAssignment}}}) {
|
||||||
store,
|
|
||||||
{
|
|
||||||
data: {
|
|
||||||
updateAssignment: { successful, updatedAssignment },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
if (successful) {
|
if (successful) {
|
||||||
const query = ASSIGNMENT_QUERY;
|
const query = ASSIGNMENT_QUERY;
|
||||||
|
|
@ -137,82 +149,80 @@ export default {
|
||||||
submission,
|
submission,
|
||||||
});
|
});
|
||||||
const data = {
|
const data = {
|
||||||
assignment,
|
assignment
|
||||||
};
|
};
|
||||||
store.writeQuery({ query, variables, data });
|
store.writeQuery({query, variables, data});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 500),
|
}, 500),
|
||||||
saveInput: function (answer) {
|
saveInput: function (answer) {
|
||||||
// reset corrections on input
|
// reset corrections on input
|
||||||
this.corrections = '';
|
this.corrections = '';
|
||||||
this.unsaved = true;
|
this.unsaved = true;
|
||||||
/*
|
/*
|
||||||
We update the assignment on this component, so the changes are reflected on it. The server does not return
|
We update the assignment on this component, so the changes are reflected on it. The server does not return
|
||||||
the updated entity, to prevent the UI to update when the user is entering his input
|
the updated entity, to prevent the UI to update when the user is entering his input
|
||||||
*/
|
*/
|
||||||
this.assignment.submission.text = answer;
|
this.assignment.submission.text = answer;
|
||||||
this._save(this.assignment.submission);
|
this._save(this.assignment.submission);
|
||||||
},
|
},
|
||||||
changeDocumentUrl(documentUrl) {
|
changeDocumentUrl(documentUrl) {
|
||||||
this.assignment.submission.document = documentUrl;
|
this.assignment.submission.document = documentUrl;
|
||||||
this._save(this.assignment.submission);
|
this._save(this.assignment.submission);
|
||||||
},
|
},
|
||||||
turnIn() {
|
turnIn() {
|
||||||
// reset corrections on turn in
|
// reset corrections on turn in
|
||||||
this.corrections = '';
|
this.corrections = '';
|
||||||
this.$apollo.mutate({
|
this.$apollo.mutate({
|
||||||
mutation: UPDATE_ASSIGNMENT_MUTATION,
|
mutation: UPDATE_ASSIGNMENT_MUTATION,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
assignment: {
|
assignment: {
|
||||||
id: this.assignment.id,
|
id: this.assignment.id,
|
||||||
answer: this.assignment.submission.text,
|
answer: this.assignment.submission.text,
|
||||||
document: this.assignment.submission.document,
|
document: this.assignment.submission.document,
|
||||||
final: true,
|
final: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
},
|
||||||
},
|
reopen() {
|
||||||
reopen() {
|
this.$apollo.mutate({
|
||||||
this.$apollo.mutate({
|
mutation: UPDATE_ASSIGNMENT_MUTATION,
|
||||||
mutation: UPDATE_ASSIGNMENT_MUTATION,
|
variables: {
|
||||||
variables: {
|
input: {
|
||||||
input: {
|
assignment: {
|
||||||
assignment: {
|
id: this.assignment.id,
|
||||||
id: this.assignment.id,
|
answer: this.assignment.submission.text,
|
||||||
answer: this.assignment.submission.text,
|
document: this.assignment.submission.document,
|
||||||
document: this.assignment.submission.document,
|
final: false,
|
||||||
final: false,
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
},
|
||||||
},
|
initialSubmission() {
|
||||||
initialSubmission() {
|
return {
|
||||||
return {
|
text: '',
|
||||||
text: '',
|
document: '',
|
||||||
document: '',
|
final: false,
|
||||||
final: false,
|
};
|
||||||
};
|
},
|
||||||
},
|
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: {
|
||||||
|
|
@ -220,96 +230,90 @@ export default {
|
||||||
text: this.assignment.submission.text,
|
text: this.assignment.submission.text,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
update(
|
update(store, {data: {spellCheck: {results}}}) {
|
||||||
store,
|
|
||||||
{
|
|
||||||
data: {
|
|
||||||
spellCheck: { results },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
self.corrections = results;
|
self.corrections = results;
|
||||||
},
|
},
|
||||||
})
|
}).then(() => {
|
||||||
.then(() => {
|
|
||||||
this.spellcheckLoading = false;
|
this.spellcheckLoading = false;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
apollo: {
|
apollo: {
|
||||||
assignment: {
|
assignment: {
|
||||||
query: ASSIGNMENT_QUERY,
|
query: ASSIGNMENT_QUERY,
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
id: this.value.id,
|
id: this.value.id,
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
result(response) {
|
||||||
|
const data = response.data;
|
||||||
|
this.assignment = cloneDeep(data.assignment);
|
||||||
|
this.assignment.submission = Object.assign(this.initialSubmission(), this.assignment.submission);
|
||||||
|
if (this.assignment.id === this.scrollToAssignmentId && 'stale' in response) {
|
||||||
|
this.$nextTick(() => this.scrollToAssignmentReady(true));
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
result(response) {
|
me: {
|
||||||
const data = response.data;
|
query: ME_QUERY,
|
||||||
this.assignment = cloneDeep(data.assignment);
|
|
||||||
this.assignment.submission = Object.assign(this.initialSubmission(), this.assignment.submission);
|
|
||||||
if (this.assignment.id === this.scrollToAssignmentId && 'stale' in response) {
|
|
||||||
this.$nextTick(() => this.scrollToAssignmentReady(true));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
me: {
|
};
|
||||||
query: ME_QUERY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</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';
|
||||||
@import '@/styles/_mixins.scss';
|
@import '@/styles/_mixins.scss';
|
||||||
|
|
||||||
.assignment {
|
.assignment {
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
font-size: toRem(17px);
|
font-size: toRem(17px);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
|
||||||
|
|
||||||
&__main-text {
|
|
||||||
:deep(ul) {
|
|
||||||
@include list-parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(li) {
|
&__main-text {
|
||||||
@include list-child;
|
/deep/ ul{
|
||||||
|
@include list-parent
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ li {
|
||||||
|
@include list-child;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&__toggle-input-container {
|
&__toggle-input-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
|
||||||
|
|
||||||
&__toggle-input {
|
|
||||||
border: 0;
|
|
||||||
font-family: $sans-serif-font-family;
|
|
||||||
background: transparent;
|
|
||||||
font-size: toRem(14px);
|
|
||||||
padding: 5px 0;
|
|
||||||
margin-right: 15px;
|
|
||||||
outline: 0;
|
|
||||||
color: $color-silver-dark;
|
|
||||||
cursor: pointer;
|
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
|
|
||||||
&--active {
|
|
||||||
border-bottom-color: $color-charcoal-dark;
|
|
||||||
color: $color-charcoal-dark;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__toggle-input {
|
||||||
|
border: 0;
|
||||||
|
font-family: $sans-serif-font-family;
|
||||||
|
background: transparent;
|
||||||
|
font-size: toRem(14px);
|
||||||
|
padding: 5px 0;
|
||||||
|
margin-right: 15px;
|
||||||
|
outline: 0;
|
||||||
|
color: $color-silver-dark;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
border-bottom-color: $color-charcoal-dark;
|
||||||
|
color: $color-charcoal-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__feedback {
|
||||||
|
@include regular-text;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__feedback {
|
|
||||||
@include regular-text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,99 +1,109 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="final-submission" data-cy="final-submission">
|
<div
|
||||||
<document-block :value="{ url: userInput.document }" class="final-submission__document" v-if="userInput.document" />
|
class="final-submission"
|
||||||
|
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 class="final-submission__reopen" data-cy="final-submission-reopen" v-if="showReopen" @click="$emit('reopen')"
|
<a
|
||||||
>Bearbeiten</a
|
class="final-submission__reopen"
|
||||||
>
|
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 {defineAsyncComponent} from 'vue';
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/DocumentBlock');
|
const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'));
|
||||||
|
|
||||||
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon');
|
const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/InfoIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
userInput: {
|
userInput: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({})
|
||||||
|
},
|
||||||
|
showReopen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
sharedMsg: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
},
|
},
|
||||||
showReopen: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
sharedMsg: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
InfoIcon,
|
InfoIcon,
|
||||||
DocumentBlock,
|
DocumentBlock,
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
text() {
|
|
||||||
return newLineToParagraph(this.userInput.text);
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
computed: {
|
||||||
|
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 {
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
@include input-box-shadow;
|
@include input-box-shadow;
|
||||||
border-radius: $input-border-radius;
|
border-radius: $input-border-radius;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
font-size: toRem(17px);
|
font-size: toRem(17px);
|
||||||
font-family: $sans-serif-font-family;
|
font-family: $sans-serif-font-family;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
font-weight: $font-weight-regular;
|
font-weight: $font-weight-regular;
|
||||||
|
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__document {
|
||||||
|
margin-bottom: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__explanation {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__explanation-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
fill: $color-brand;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__explanation-text {
|
||||||
|
color: $color-brand;
|
||||||
|
font-family: $sans-serif-font-family;
|
||||||
|
font-weight: $font-weight-regular;
|
||||||
|
margin-right: $medium-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__reopen {
|
||||||
|
@include small-text;
|
||||||
|
cursor: pointer;
|
||||||
|
color: $color-charcoal-light;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__document {
|
|
||||||
margin-bottom: $small-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__explanation {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__explanation-icon {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
fill: $color-brand;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__explanation-text {
|
|
||||||
color: $color-brand;
|
|
||||||
font-family: $sans-serif-font-family;
|
|
||||||
font-weight: $font-weight-regular;
|
|
||||||
margin-right: $medium-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__reopen {
|
|
||||||
@include small-text;
|
|
||||||
cursor: pointer;
|
|
||||||
color: $color-charcoal-light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="submission-form-container__actions" v-if="!isFinalOrReadOnly">
|
<div
|
||||||
|
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"
|
||||||
|
|
@ -26,7 +29,11 @@
|
||||||
>
|
>
|
||||||
{{ spellcheckText }}
|
{{ spellcheckText }}
|
||||||
</button>
|
</button>
|
||||||
<file-upload :document="userInput.document" v-if="allowsDocuments" @change-document-url="changeDocumentUrl" />
|
<file-upload
|
||||||
|
:document="userInput.document"
|
||||||
|
v-if="allowsDocuments"
|
||||||
|
@change-document-url="changeDocumentUrl"
|
||||||
|
/>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -41,112 +48,116 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const SubmissionInput = () => import('@/components/content-blocks/assignment/SubmissionInput.vue');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const FinalSubmission = () => import('@/components/content-blocks/assignment/FinalSubmission.vue');
|
const SubmissionInput = defineAsyncComponent(() => import('@/components/content-blocks/assignment/SubmissionInput'));
|
||||||
const FileUpload = () => import('@/components/ui/file-upload/FileUpload.vue');
|
const FinalSubmission = defineAsyncComponent(() => import('@/components/content-blocks/assignment/FinalSubmission'));
|
||||||
|
const FileUpload = defineAsyncComponent(() => import('@/components/ui/file-upload/FileUpload'));
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
userInput: Object,
|
|
||||||
saved: Boolean,
|
|
||||||
placeholder: String,
|
|
||||||
action: String,
|
|
||||||
reopen: Function,
|
|
||||||
document: String,
|
|
||||||
readOnly: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
spellcheck: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
spellcheckLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
sharedMsg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
export default {
|
||||||
FileUpload,
|
props: {
|
||||||
SubmissionInput,
|
userInput: Object,
|
||||||
FinalSubmission,
|
saved: Boolean,
|
||||||
},
|
placeholder: String,
|
||||||
|
action: String,
|
||||||
|
reopen: Function,
|
||||||
|
document: String,
|
||||||
|
readOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
spellcheck: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
spellcheckLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
sharedMsg: String,
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
components: {
|
||||||
final() {
|
FileUpload,
|
||||||
return !!this.userInput && this.userInput.final;
|
SubmissionInput,
|
||||||
|
FinalSubmission,
|
||||||
},
|
},
|
||||||
isFinalOrReadOnly() {
|
|
||||||
return this.final || this.readOnly;
|
|
||||||
},
|
|
||||||
allowsDocuments() {
|
|
||||||
return 'document' in this.userInput;
|
|
||||||
},
|
|
||||||
showSpellcheckButton() {
|
|
||||||
return this.spellcheck && process.env.VUE_APP_ENABLE_SPELLCHECK;
|
|
||||||
},
|
|
||||||
spellcheckText() {
|
|
||||||
if (!this.spellcheckLoading) {
|
|
||||||
return 'Rechtschreibung prüfen';
|
|
||||||
} else {
|
|
||||||
return 'Wird geprüft...';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
computed: {
|
||||||
reopenSubmission() {
|
final() {
|
||||||
this.$emit('reopen');
|
return !!this.userInput && this.userInput.final;
|
||||||
|
},
|
||||||
|
isFinalOrReadOnly() {
|
||||||
|
return this.final || this.readOnly;
|
||||||
|
},
|
||||||
|
allowsDocuments() {
|
||||||
|
return 'document' in this.userInput;
|
||||||
|
},
|
||||||
|
showSpellcheckButton() {
|
||||||
|
return this.spellcheck && process.env.VUE_APP_ENABLE_SPELLCHECK;
|
||||||
|
},
|
||||||
|
spellcheckText() {
|
||||||
|
if (!this.spellcheckLoading) {
|
||||||
|
return 'Rechtschreibung prüfen';
|
||||||
|
} else {
|
||||||
|
return 'Wird geprüft...';
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
saveInput(input) {
|
|
||||||
this.$emit('saveInput', input);
|
methods: {
|
||||||
|
reopenSubmission() {
|
||||||
|
this.$emit('reopen');
|
||||||
|
},
|
||||||
|
saveInput(input) {
|
||||||
|
this.$emit('saveInput', input);
|
||||||
|
},
|
||||||
|
changeDocumentUrl(documentUrl) {
|
||||||
|
this.$emit('changeDocumentUrl', documentUrl);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
changeDocumentUrl(documentUrl) {
|
|
||||||
this.$emit('changeDocumentUrl', documentUrl);
|
};
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.submission-form-container {
|
.submission-form-container {
|
||||||
@include form-with-border;
|
@include form-with-border;
|
||||||
|
|
||||||
margin-bottom: $medium-spacing;
|
margin-bottom: $medium-spacing;
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
@include desktop {
|
@include desktop {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__inputs {
|
&__inputs {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__submit {
|
&__submit {
|
||||||
margin-right: $medium-spacing;
|
margin-right: $medium-spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__actions {
|
&__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__document {
|
&__document {
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__spellcheck {
|
||||||
|
/* so the button does not change size when changing the text */
|
||||||
|
width: 235px;
|
||||||
|
text-align: center;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__spellcheck {
|
|
||||||
/* so the button does not change size when changing the text */
|
|
||||||
width: 235px;
|
|
||||||
text-align: center;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -4,68 +4,75 @@
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:value="inputText"
|
:value="inputText"
|
||||||
:class="{ 'submission-form__textarea--readonly': readonly }"
|
:class="{'submission-form__textarea--readonly': readonly}"
|
||||||
data-cy="submission-textarea"
|
data-cy="submission-textarea"
|
||||||
rows="1"
|
rows="1"
|
||||||
class="submission-form__textarea"
|
class="submission-form__textarea"
|
||||||
v-auto-grow
|
v-auto-grow
|
||||||
@input="$emit('input', $event.target.value)"
|
@input="$emit('input', $event.target.value)"
|
||||||
/>
|
/>
|
||||||
<div class="submission-form__save-status submission-form__save-status--saved" v-if="saved">
|
<div
|
||||||
|
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 class="submission-form__save-status submission-form__save-status--unsaved" v-if="!saved">
|
<div
|
||||||
|
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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const TickCircleIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TickCircleIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const LoadingIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/LoadingIcon');
|
const TickCircleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TickCircleIcon'));
|
||||||
|
const LoadingIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LoadingIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
inputText: String,
|
inputText: String,
|
||||||
saved: Boolean,
|
saved: Boolean,
|
||||||
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;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
&__textarea {
|
&__textarea {
|
||||||
@include borderless-textarea;
|
@include borderless-textarea;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__save-status {
|
||||||
|
position: relative;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__save-status-icon {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
fill: $color-silver-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__saving-icon {
|
||||||
|
@include spin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__save-status {
|
|
||||||
position: relative;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__save-status-icon {
|
|
||||||
width: 22px;
|
|
||||||
height: 22px;
|
|
||||||
fill: $color-silver-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__saving-icon {
|
|
||||||
@include spin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -20,43 +20,44 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/InfoIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
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;
|
||||||
grid-auto-rows: auto;
|
grid-auto-rows: auto;
|
||||||
grid-row-gap: 13px;
|
grid-row-gap: 13px;
|
||||||
grid-template-columns: 40px 1fr;
|
grid-template-columns: 40px 1fr;
|
||||||
grid-column-gap: 16px;
|
grid-column-gap: 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
width: $modal-input-width;
|
width: $modal-input-width;
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exercise-text {
|
||||||
|
width: $modal-input-width;
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__help-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__help-description {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__exercise-text {
|
|
||||||
width: $modal-input-width;
|
|
||||||
grid-column: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__help-icon {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__help-description {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,119 +1,133 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="document-form" ref="documentform">
|
<div
|
||||||
<div v-if="!value.url" ref="uploadcare-panel" />
|
class="document-form"
|
||||||
<div class="document-form__spinner" v-if="loading">
|
ref="documentform"
|
||||||
|
>
|
||||||
|
<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 class="document-form__uploaded" v-if="value.url">
|
<div
|
||||||
|
class="document-form__uploaded"
|
||||||
|
v-if="value.url"
|
||||||
|
>
|
||||||
<document-icon class="document-form__icon" />
|
<document-icon class="document-form__icon" />
|
||||||
<a :href="previewUrl" class="document-form__link" target="_blank">{{ previewLink }}</a>
|
<a
|
||||||
|
:href="previewUrl"
|
||||||
|
class="document-form__link"
|
||||||
|
target="_blank"
|
||||||
|
>{{ previewLink }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { uploadcare } from '@/helpers/uploadcare';
|
import {uploadcare} from '@/helpers/uploadcare';
|
||||||
import LoadingIcon from '@/components/icons/LoadingIcon';
|
import LoadingIcon from '@/components/icons/LoadingIcon';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon');
|
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['value', 'index'],
|
props: ['value', 'index'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
LoadingIcon,
|
LoadingIcon,
|
||||||
DocumentIcon,
|
DocumentIcon,
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
previewUrl() {
|
|
||||||
if (this.value && this.value.url) {
|
|
||||||
return this.value.url;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
previewLink() {
|
|
||||||
if (this.value && this.value.url) {
|
|
||||||
const parts = this.value.url.split('/');
|
|
||||||
return parts[parts.length - 1];
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
data() {
|
||||||
uploadcare(
|
return {
|
||||||
this,
|
loading: false,
|
||||||
(url) => {
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
previewUrl() {
|
||||||
|
if (this.value && this.value.url) {
|
||||||
|
return this.value.url;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
previewLink() {
|
||||||
|
if (this.value && this.value.url) {
|
||||||
|
const parts = this.value.url.split('/');
|
||||||
|
return parts[parts.length - 1];
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
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 {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__spinner {
|
|
||||||
width: 100%;
|
|
||||||
height: 150px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__loading-icon {
|
|
||||||
@include spin;
|
|
||||||
fill: $color-silver-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__icon {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
margin-right: $small-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__file-input {
|
|
||||||
width: 0.1px;
|
|
||||||
height: 0.1px;
|
|
||||||
overflow: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
|
||||||
z-index: -1;
|
|
||||||
|
|
||||||
& + label {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: $color-silver-light;
|
|
||||||
height: 150px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-family: $sans-serif-font-family;
|
}
|
||||||
font-weight: $font-weight-regular;
|
|
||||||
|
&__link {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__spinner {
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__loading-icon {
|
||||||
|
@include spin;
|
||||||
|
fill: $color-silver-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
margin-right: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__file-input {
|
||||||
|
width: 0.1px;
|
||||||
|
height: 0.1px;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
|
||||||
|
& + label {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: $color-silver-light;
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: $sans-serif-font-family;
|
||||||
|
font-weight: $font-weight-regular;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,130 +1,147 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="tip-tap">
|
<div class="tip-tap">
|
||||||
<editor-content class="tip-tap__editor-wrapper" :editor="editor" />
|
<editor-content
|
||||||
|
class="tip-tap__editor-wrapper"
|
||||||
|
:editor="editor"
|
||||||
|
/>
|
||||||
|
|
||||||
<toggle :bordered="false" :checked="isList" label="Als Liste formatieren" @input="toggleList" />
|
<toggle
|
||||||
|
: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 {PropType, defineComponent} from 'vue';
|
||||||
import { Editor, EditorContent } from '@tiptap/vue-2';
|
import {Editor, EditorContent} from "@tiptap/vue-3";
|
||||||
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;
|
||||||
}
|
}
|
||||||
interface Value {
|
interface Value {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Vue.extend({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Object as PropType<Value>,
|
type: Object as PropType<Value>,
|
||||||
validator(value: Value) {
|
validator: (value: Value) => {
|
||||||
return Object.prototype.hasOwnProperty.call(value, 'text');
|
return Object.prototype.hasOwnProperty.call(value, 'text');
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
Toggle,
|
|
||||||
EditorContent,
|
|
||||||
},
|
|
||||||
|
|
||||||
data(): Data {
|
|
||||||
return {
|
|
||||||
editor: undefined,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
isList(): boolean {
|
|
||||||
return this.editor?.isActive('bulletList') || false;
|
|
||||||
},
|
|
||||||
text(): string {
|
|
||||||
return this.value.text;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value({ text }: Value) {
|
|
||||||
const editor = this.editor as Editor; // editor is always initialized on mount, cast it
|
|
||||||
const isSame = editor.getHTML() === text;
|
|
||||||
|
|
||||||
if (isSame) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.commands.setContent(text, false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.editor = new Editor({
|
|
||||||
editorProps: {
|
|
||||||
attributes: {
|
|
||||||
class: 'tip-tap__editor',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
content: this.text,
|
|
||||||
extensions: [Document, Paragraph, Text, BulletList, ListItem],
|
|
||||||
onUpdate: () => {
|
|
||||||
const text = (this.editor as Editor).getHTML();
|
|
||||||
this.$emit('input', text);
|
|
||||||
this.$emit('change-text', text);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.editor?.destroy();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggleList() {
|
|
||||||
const editor = this.editor as Editor;
|
|
||||||
editor.chain().selectAll().toggleBulletList().run();
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
components: {
|
||||||
|
Toggle,
|
||||||
|
EditorContent
|
||||||
|
},
|
||||||
|
|
||||||
|
data(): Data {
|
||||||
|
return {
|
||||||
|
editor: undefined,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isList(): boolean {
|
||||||
|
return this.editor?.isActive('bulletList') || false;
|
||||||
|
},
|
||||||
|
text(): string {
|
||||||
|
return this.value?.text || '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
value({text}: Value) {
|
||||||
|
const editor = this.editor as Editor; // editor is always initialized on mount, cast it
|
||||||
|
const isSame = editor.getHTML() === text;
|
||||||
|
|
||||||
|
if (isSame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.commands.setContent(text, false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.editor = new Editor({
|
||||||
|
editorProps: {
|
||||||
|
attributes: {
|
||||||
|
class: 'tip-tap__editor'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content: this.text,
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
BulletList,
|
||||||
|
ListItem
|
||||||
|
],
|
||||||
|
onUpdate: () => {
|
||||||
|
const text=(this.editor as Editor).getHTML();
|
||||||
|
this.$emit('input', text);
|
||||||
|
this.$emit('change-text', text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeUnmount() {
|
||||||
|
this.editor?.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleList() {
|
||||||
|
const editor = this.editor as Editor;
|
||||||
|
editor.chain().selectAll().toggleBulletList().run();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.tip-tap {
|
|
||||||
&__editor-wrapper {
|
|
||||||
margin-bottom: $medium-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.tip-tap__editor) {
|
.tip-tap {
|
||||||
@include inputstyle;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(ul) {
|
&__editor-wrapper {
|
||||||
padding-left: $medium-spacing;
|
margin-bottom: $medium-spacing;
|
||||||
list-style: initial;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
:deep(li) {
|
/deep/ &__editor {
|
||||||
@include inputfont;
|
@include inputstyle;
|
||||||
}
|
flex-direction: column;
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(div) {
|
/deep/ ul {
|
||||||
@include inputfont;
|
padding-left: $medium-spacing;
|
||||||
}
|
list-style: initial;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(p) {
|
/deep/ li {
|
||||||
@include inputfont;
|
@include inputfont;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ div {
|
||||||
|
@include inputfont;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ p {
|
||||||
|
@include inputfont;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="video-form" v-if="!isVimeo && !isYoutube && !isSrf">
|
<div
|
||||||
|
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
|
Sie können Videos auf <a
|
||||||
<a class="video-form__platform-link help-text__link" href="https://youtube.com/" target="_blank">Youtube</a>
|
class="video-form__platform-link help-text__link"
|
||||||
oder <a class="video-form__platform-link help-text__link" href="https://vimeo.com/" target="_blank">Vimeo</a>
|
href="https://youtube.com/"
|
||||||
|
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>
|
||||||
|
|
||||||
|
|
@ -14,7 +24,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">
|
||||||
|
|
@ -30,64 +40,67 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import YoutubeEmbed from '@/components/videos/YoutubeEmbed';
|
import YoutubeEmbed from '@/components/videos/YoutubeEmbed';
|
||||||
import VimeoEmbed from '@/components/videos/VimeoEmbed';
|
import VimeoEmbed from '@/components/videos/VimeoEmbed';
|
||||||
import SrfEmbed from '@/components/videos/SrfEmbed';
|
import SrfEmbed from '@/components/videos/SrfEmbed';
|
||||||
import { isVimeoUrl, isYoutubeUrl, isSrfUrl } from '@/helpers/video';
|
import {isVimeoUrl, isYoutubeUrl, isSrfUrl} from '@/helpers/video';
|
||||||
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/InfoIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['value', 'index'],
|
props: ['value', 'index'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
InfoIcon,
|
InfoIcon,
|
||||||
YoutubeEmbed,
|
YoutubeEmbed,
|
||||||
VimeoEmbed,
|
VimeoEmbed,
|
||||||
SrfEmbed,
|
SrfEmbed
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isYoutube() {
|
isYoutube() {
|
||||||
return isYoutubeUrl(this.value.url);
|
return isYoutubeUrl(this.value.url);
|
||||||
},
|
},
|
||||||
isVimeo() {
|
isVimeo() {
|
||||||
return isVimeoUrl(this.value.url);
|
return isVimeoUrl(this.value.url);
|
||||||
},
|
},
|
||||||
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;
|
||||||
grid-auto-rows: auto;
|
grid-auto-rows: auto;
|
||||||
grid-template-columns: 40px 1fr;
|
grid-template-columns: 40px 1fr;
|
||||||
grid-column-gap: 16px;
|
grid-column-gap: 16px;
|
||||||
grid-row-gap: 20px;
|
grid-row-gap: 20px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&__help-icon {
|
&__help-icon {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&__help-description {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&__platform-link {
|
||||||
|
font-family: $sans-serif-font-family;
|
||||||
|
text-decoration: underline;
|
||||||
|
font-weight: $font-weight-regular;
|
||||||
|
font-size: toRem(17px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__video-link {
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
width: $modal-input-width
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__help-description {
|
|
||||||
}
|
|
||||||
|
|
||||||
&__platform-link {
|
|
||||||
font-family: $sans-serif-font-family;
|
|
||||||
text-decoration: underline;
|
|
||||||
font-weight: $font-weight-regular;
|
|
||||||
font-size: toRem(17px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__video-link {
|
|
||||||
grid-column: 1 / span 2;
|
|
||||||
width: $modal-input-width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,164 +1,179 @@
|
||||||
<template>
|
<template>
|
||||||
<a :class="typeClass" class="filter-entry" data-cy="filter-entry" :style="categoryStyle">
|
<a
|
||||||
|
:class="typeClass"
|
||||||
|
class="filter-entry"
|
||||||
|
data-cy="filter-entry"
|
||||||
|
:style="categoryStyle"
|
||||||
|
@click="$emit('filter')"
|
||||||
|
>
|
||||||
<span class="filter-entry__text">{{ text }}</span>
|
<span class="filter-entry__text">{{ text }}</span>
|
||||||
<span :style="activeStyle" class="filter-entry__icon-wrapper">
|
<span
|
||||||
<chevron-right :style="{ fill: category.foreground }" class="filter-entry__icon" />
|
:style="activeStyle"
|
||||||
|
class="filter-entry__icon-wrapper"
|
||||||
|
>
|
||||||
|
<chevron-right
|
||||||
|
:style="{fill: category.foreground}"
|
||||||
|
class="filter-entry__icon"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
|
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const ChevronRight = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight'));
|
||||||
|
|
||||||
const ChevronRight = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronRight');
|
export default {
|
||||||
|
props: {
|
||||||
export default {
|
text: {
|
||||||
props: {
|
type: String,
|
||||||
text: {
|
required: true,
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
id: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
isCategory: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
category: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
ChevronRight,
|
|
||||||
},
|
|
||||||
|
|
||||||
apollo: {
|
|
||||||
instrumentFilter: {
|
|
||||||
query: INSTRUMENT_FILTER_QUERY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
instrumentFilter: {
|
|
||||||
currentFilter: '',
|
|
||||||
},
|
},
|
||||||
};
|
id: {
|
||||||
},
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
isCategory: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
components: {
|
||||||
isActive() {
|
ChevronRight,
|
||||||
if (!this.instrumentFilter.currentFilter) {
|
|
||||||
return this.id === '';
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const [_, identifier] = this.instrumentFilter.currentFilter.split(':');
|
|
||||||
return this.id === identifier;
|
|
||||||
},
|
},
|
||||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
|
||||||
activeStyle() {
|
emits: ['filter'],
|
||||||
if (this.isActive) {
|
|
||||||
return {
|
apollo: {
|
||||||
backgroundColor: this.category.background,
|
instrumentFilter: {
|
||||||
};
|
query: INSTRUMENT_FILTER_QUERY,
|
||||||
}
|
},
|
||||||
return {};
|
|
||||||
},
|
},
|
||||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
|
||||||
categoryStyle() {
|
data() {
|
||||||
if (this.isCategory) {
|
|
||||||
return {
|
|
||||||
color: this.category.foreground,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
|
||||||
typeClass() {
|
|
||||||
return {
|
return {
|
||||||
'filter-entry--active': this.isActive,
|
instrumentFilter: {
|
||||||
'filter-entry--category': this.isCategory,
|
currentFilter: '',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
computed: {
|
||||||
|
isActive() {
|
||||||
|
if (!this.instrumentFilter.currentFilter) {
|
||||||
|
return this.id === '';
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const [_, identifier] = this.instrumentFilter.currentFilter.split(':');
|
||||||
|
return this.id === identifier;
|
||||||
|
},
|
||||||
|
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||||
|
activeStyle() {
|
||||||
|
if (this.isActive) {
|
||||||
|
return {
|
||||||
|
backgroundColor: this.category.background,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||||
|
categoryStyle() {
|
||||||
|
if (this.isCategory) {
|
||||||
|
return {
|
||||||
|
color: this.category.foreground,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||||
|
typeClass() {
|
||||||
|
return {
|
||||||
|
'filter-entry--active': this.isActive,
|
||||||
|
'filter-entry--category': this.isCategory,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.filter-entry {
|
.filter-entry {
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
@include sub-heading;
|
|
||||||
line-height: 1.5;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__icon-wrapper {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
cursor: pointer;
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__icon {
|
&__text {
|
||||||
width: 10px;
|
@include sub-heading;
|
||||||
height: 10px;
|
line-height: 1.5;
|
||||||
}
|
color: inherit;
|
||||||
|
|
||||||
$root: &;
|
|
||||||
|
|
||||||
@mixin filter-block($color) {
|
|
||||||
&#{$root}--category {
|
|
||||||
color: $color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&#{$root}--active {
|
&__icon-wrapper {
|
||||||
#{$root}__icon-wrapper {
|
display: flex;
|
||||||
background-color: $color;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
$root: &;
|
||||||
|
|
||||||
|
@mixin filter-block($color) {
|
||||||
|
&#{$root}--category {
|
||||||
|
color: $color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&#{$root}--active {
|
||||||
|
#{$root}__icon-wrapper {
|
||||||
|
background-color: $color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$root}__icon {
|
||||||
|
fill: $color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$root}__icon {
|
&--language-communication {
|
||||||
fill: $color;
|
@include filter-block($color-accent-1-dark);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--language-communication {
|
|
||||||
@include filter-block($color-accent-1-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
&--society {
|
|
||||||
@include filter-block($color-accent-2-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
&--interdisciplinary {
|
|
||||||
@include filter-block($color-accent-4-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
&--active {
|
|
||||||
#{$root}__text {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$root}__icon-wrapper {
|
&--society {
|
||||||
background-color: black;
|
@include filter-block($color-accent-2-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$root}__icon {
|
&--interdisciplinary {
|
||||||
fill: white;
|
@include filter-block($color-accent-4-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
#{$root}__text {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$root}__icon-wrapper {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$root}__icon {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<filter-entry
|
<filter-entry
|
||||||
:text="title"
|
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
|
:text="title"
|
||||||
|
:type="category"
|
||||||
:category="category"
|
:category="category"
|
||||||
:is-category="true"
|
:is-category="true"
|
||||||
:id="category.id"
|
:id="category.id"
|
||||||
@click.native="setCategoryFilter(category.id)"
|
@filter="setCategoryFilter(category.id)"
|
||||||
/>
|
/>
|
||||||
<div class="filter-group__children">
|
<div class="filter-group__children">
|
||||||
<filter-entry
|
<filter-entry
|
||||||
|
|
@ -16,83 +17,84 @@
|
||||||
v-for="type in types"
|
v-for="type in types"
|
||||||
:id="type.id"
|
:id="type.id"
|
||||||
:key="type.id"
|
:key="type.id"
|
||||||
@click.native="setFilter(`type:${type.id}`)"
|
@filter="setFilter(`type:${type.id}`)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import FilterEntry from '@/components/instruments/FilterEntry';
|
import {defineComponent} from 'vue';
|
||||||
|
import FilterEntry from '@/components/instruments/FilterEntry';
|
||||||
|
|
||||||
import SET_FILTER_MUTATION from 'gql/local/mutations/setInstrumentFilter.gql';
|
import SET_FILTER_MUTATION from 'gql/local/mutations/setInstrumentFilter.gql';
|
||||||
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
|
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
|
||||||
types: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
category: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
FilterEntry,
|
|
||||||
},
|
|
||||||
|
|
||||||
apollo: {
|
|
||||||
instrumentFilter: {
|
|
||||||
query: INSTRUMENT_FILTER_QUERY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
instrumentFilter: {
|
|
||||||
currentFilter: '',
|
|
||||||
},
|
},
|
||||||
};
|
types: {
|
||||||
},
|
type: Array,
|
||||||
inheritAttrs: false,
|
default: () => [],
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
FilterEntry,
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
apollo: {
|
||||||
setCategoryFilter(category) {
|
instrumentFilter: {
|
||||||
if (category) {
|
query: INSTRUMENT_FILTER_QUERY
|
||||||
this.setFilter(`category:${category}`);
|
|
||||||
} else {
|
|
||||||
this.setFilter(``);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setFilter(filter) {
|
|
||||||
this.$apollo.mutate({
|
data() {
|
||||||
mutation: SET_FILTER_MUTATION,
|
return {
|
||||||
variables: {
|
instrumentFilter: {
|
||||||
filter,
|
currentFilter: ''
|
||||||
},
|
}
|
||||||
});
|
};
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setCategoryFilter(category) {
|
||||||
|
if (category) {
|
||||||
|
this.setFilter(`category:${category}`);
|
||||||
|
} else {
|
||||||
|
this.setFilter(``);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setFilter(filter) {
|
||||||
|
this.$apollo.mutate({
|
||||||
|
mutation: SET_FILTER_MUTATION,
|
||||||
|
variables: {
|
||||||
|
filter
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.filter-group {
|
.filter-group {
|
||||||
border-bottom: 1px solid $color-silver;
|
border-bottom: 1px solid $color-silver;
|
||||||
padding: $medium-spacing 0;
|
padding: $medium-spacing 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
&__children {
|
&__children {
|
||||||
padding-left: $medium-spacing;
|
padding-left: $medium-spacing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
const Modal = () => import(/* webpackChunkName: "modals" */ '@/components/Modal');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const FullscreenImage = () => import(/* webpackChunkName: "modals" */ '@/components/FullscreenImage');
|
const Modal = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/Modal'));
|
||||||
const FullscreenInfographic = () => import(/* webpackChunkName: "modals" */ '@/components/FullscreenInfographic');
|
const FullscreenImage = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/FullscreenImage'));
|
||||||
const FullscreenVideo = () => import(/* webpackChunkName: "modals" */ '@/components/FullscreenVideo');
|
const FullscreenInfographic = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/FullscreenInfographic'));
|
||||||
const DeactivatePerson = () => import(/* webpackChunkName: "modals" */ '@/components/profile/DeactivatePerson');
|
const FullscreenVideo = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/FullscreenVideo'));
|
||||||
const SnapshotCreated = () => import(/* webpackChunkName: "modals" */ '@/components/modules/SnapshotCreated');
|
const DeactivatePerson = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/profile/DeactivatePerson'));
|
||||||
const ChangeVisibility = () => import(/* webpackChunkName: "modals" */ '@/components/rooms/ChangeVisibility');
|
const SnapshotCreated = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/modules/SnapshotCreated'));
|
||||||
const Confirm = () => import(/* webpackChunkName: "modals" */ '@/components/modals/Confirm');
|
const ChangeVisibility = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/rooms/ChangeVisibility'));
|
||||||
|
const Confirm = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/modals/Confirm'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Modal,
|
Modal,
|
||||||
|
|
|
||||||
|
|
@ -5,28 +5,8 @@
|
||||||
:slug="module.topic.slug"
|
:slug="module.topic.slug"
|
||||||
class="module-navigation__topic-link"
|
class="module-navigation__topic-link"
|
||||||
type="topic"
|
type="topic"
|
||||||
|
v-if="module.topic"
|
||||||
/>
|
/>
|
||||||
<div class="module-navigation__module-content" v-if="false">
|
|
||||||
<!-- Do not display this for now, might be used later again though -->
|
|
||||||
<router-link :to="moduleContentLink" tag="h3" class="module-navigation__heading">
|
|
||||||
Inhalte: {{ module.metaTitle }}
|
|
||||||
</router-link>
|
|
||||||
<div class="module-navigation__anchors" v-if="onModulePage">
|
|
||||||
<a href="#" class="module-navigation__anchor" v-scroll-to="'#meta-title'">Einleitung</a>
|
|
||||||
<a href="#" class="module-navigation__anchor" v-scroll-to="'#objectives'">Lernziele</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="module-navigation__anchor"
|
|
||||||
v-for="(chapter, index) in module.chapters"
|
|
||||||
:key="chapter.id"
|
|
||||||
v-scroll-to="chapterId(index)"
|
|
||||||
>{{ chapter.title }}</a
|
|
||||||
>
|
|
||||||
<a href="#" class="module-navigation__anchor" v-scroll-to="'#objectives-confirm'">Lernzielkontrolle</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="module-navigation__toggle-menu"
|
class="module-navigation__toggle-menu"
|
||||||
data-cy="module-teacher-menu"
|
data-cy="module-teacher-menu"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link :to="moduleLink" :class="['module-teaser', { 'module-teaser--small': !teaser }]" tag="div">
|
<router-link
|
||||||
<div :style="{ backgroundImage: 'url(' + heroImage + ')' }" class="module-teaser__image" />
|
:to="moduleLink"
|
||||||
|
:class="['module-teaser', {'module-teaser--small': !teaser}]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:style="{backgroundImage: 'url('+heroImage+')'}"
|
||||||
|
class="module-teaser__image"
|
||||||
|
/>
|
||||||
<div class="module-teaser__body">
|
<div class="module-teaser__body">
|
||||||
<h3 class="module-teaser__meta-title">
|
<h3 class="module-teaser__meta-title">
|
||||||
{{ metaTitle }}
|
{{ metaTitle }}
|
||||||
|
|
@ -16,68 +22,71 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['metaTitle', 'title', 'teaser', 'id', 'slug', 'heroImage'],
|
props: ['metaTitle', 'title', 'teaser', 'id', 'slug', 'heroImage'],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
moduleLink() {
|
moduleLink() {
|
||||||
return {
|
if (this.slug) {
|
||||||
name: 'module',
|
return {
|
||||||
params: {
|
name: 'module',
|
||||||
slug: this.slug,
|
params: {
|
||||||
},
|
slug: this.slug
|
||||||
};
|
}
|
||||||
},
|
};
|
||||||
},
|
} else {
|
||||||
};
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '@/styles/_variables.scss';
|
@import "~styles/helpers";
|
||||||
@import '@/styles/_mixins.scss';
|
|
||||||
|
|
||||||
.module-teaser {
|
.module-teaser {
|
||||||
box-shadow: 0 3px 9px 0 rgba(0, 0, 0, 0.12);
|
box-shadow: 0 3px 9px 0 rgba(0, 0, 0, 0.12);
|
||||||
border: 1px solid #e2e2e2;
|
border: 1px solid #E2E2E2;
|
||||||
height: 330px;
|
height: 330px;
|
||||||
max-width: 380px;
|
max-width: 380px;
|
||||||
width: 100%;
|
|
||||||
border-radius: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&--small {
|
|
||||||
height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__image {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 150px;
|
border-radius: 12px;
|
||||||
height: 150px;
|
overflow: hidden;
|
||||||
background-position: center;
|
box-sizing: border-box;
|
||||||
background-size: 100% auto;
|
cursor: pointer;
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__body {
|
&--small {
|
||||||
padding: 20px;
|
height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__meta-title {
|
&__image {
|
||||||
color: $color-silver-dark;
|
width: 100%;
|
||||||
margin-bottom: $large-spacing;
|
max-height: 150px;
|
||||||
@include regular-text;
|
height: 150px;
|
||||||
}
|
background-position: center;
|
||||||
|
background-size: 100% auto;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
&__title {
|
&__body {
|
||||||
@include heading-3;
|
padding: 20px;
|
||||||
margin-bottom: 5px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&__description {
|
&__meta-title {
|
||||||
line-height: $default-line-height;
|
color: $color-silver-dark;
|
||||||
font-size: 1.2rem;
|
margin-bottom: $large-spacing;
|
||||||
|
@include regular-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
@include heading-3;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
line-height: $default-line-height;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="bookmark-actions" v-if="!editMode">
|
<div
|
||||||
|
class="bookmark-actions"
|
||||||
|
v-if="!editMode"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
:class="{ 'bookmark-actions__action--bookmarked': bookmarked }"
|
:class="{'bookmark-actions__action--bookmarked': bookmarked}"
|
||||||
class="bookmark-actions__action bookmark-actions__bookmark"
|
class="bookmark-actions__action bookmark-actions__bookmark"
|
||||||
data-cy="bookmark-action"
|
data-cy="bookmark-action"
|
||||||
@click="$emit('bookmark')"
|
@click="$emit('bookmark')"
|
||||||
|
|
@ -29,73 +32,74 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const BookmarkIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/BookmarkIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const AddNoteIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddNoteIcon');
|
const BookmarkIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/BookmarkIcon'));
|
||||||
const NoteIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/NoteIcon');
|
const AddNoteIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddNoteIcon'));
|
||||||
|
const NoteIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/NoteIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
bookmarked: {
|
bookmarked: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: [Object, Boolean],
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
editMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
note: {
|
components: {
|
||||||
type: [Object, Boolean],
|
BookmarkIcon,
|
||||||
default: false,
|
AddNoteIcon,
|
||||||
|
NoteIcon
|
||||||
},
|
},
|
||||||
editMode: {
|
};
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
BookmarkIcon,
|
|
||||||
AddNoteIcon,
|
|
||||||
NoteIcon,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.bookmark-actions {
|
.bookmark-actions {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
|
|
||||||
padding: 0 2 * $large-spacing;
|
padding: 0 2*$large-spacing;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -5 * $large-spacing;
|
right: -5*$large-spacing;
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
|
|
||||||
&__action {
|
&__action {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.3s;
|
transition: opacity 0.3s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 26px;
|
width: 26px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
&--bookmarked,
|
&--bookmarked, &--noted {
|
||||||
&--noted {
|
opacity: 1;
|
||||||
opacity: 1;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent: &;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
#{$parent}__action {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$parent: &;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
#{$parent}__action {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -6,36 +6,40 @@
|
||||||
placeholder="Lernziel erfassen..."
|
placeholder="Lernziel erfassen..."
|
||||||
@input="$emit('input', $event)"
|
@input="$emit('input', $event)"
|
||||||
/>
|
/>
|
||||||
<a class="icon-button" @click="$emit('delete')">
|
<a
|
||||||
|
class="icon-button"
|
||||||
|
@click="$emit('delete')"
|
||||||
|
>
|
||||||
<trash-icon class="icon-button__icon icon-button__icon--subtle" />
|
<trash-icon class="icon-button__icon icon-button__icon--subtle" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ModalInput from '@/components/ModalInput';
|
import ModalInput from '@/components/ModalInput';
|
||||||
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['objective'],
|
props: ['objective'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ModalInput,
|
ModalInput,
|
||||||
TrashIcon,
|
TrashIcon
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '@/styles/_variables.scss';
|
@import "@/styles/_variables.scss";
|
||||||
|
|
||||||
.objective-form {
|
.objective-form {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 50px;
|
grid-template-columns: 1fr 50px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
&__input {
|
&__input {
|
||||||
width: $modal-input-width;
|
width: $modal-input-width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
<div class="page-form-input">
|
<div class="page-form-input">
|
||||||
<label :for="id" class="page-form-input__label">{{ label }}</label>
|
<label :for="id" class="page-form-input__label">{{ label }}</label>
|
||||||
<component
|
<component
|
||||||
|
:value="value"
|
||||||
:class="classes"
|
:class="classes"
|
||||||
:value.prop="value"
|
|
||||||
:data-cy="cyId"
|
:data-cy="cyId"
|
||||||
:is="type"
|
:is="type"
|
||||||
:id="id"
|
:id="id"
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,49 @@
|
||||||
<template>
|
<template>
|
||||||
<a class="add-project-entry" @click="addProjectEntry">
|
<a
|
||||||
|
class="add-project-entry"
|
||||||
|
@click="addProjectEntry"
|
||||||
|
>
|
||||||
<plus-icon class="add-project-entry__icon" />
|
<plus-icon class="add-project-entry__icon" />
|
||||||
<span class="add-project-entry__text">Beitrag erfassen</span>
|
<span class="add-project-entry__text">Beitrag erfassen</span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const PlusIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PlusIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
export default {
|
const PlusIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PlusIcon'));
|
||||||
props: ['project'],
|
export default {
|
||||||
components: { PlusIcon },
|
props: ['project'],
|
||||||
|
components: {PlusIcon},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
addProjectEntry() {
|
addProjectEntry() {
|
||||||
this.$store.dispatch('addProjectEntry', this.project);
|
this.$store.dispatch('addProjectEntry', this.project);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.add-project-entry {
|
.add-project-entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 2px solid $color-brand;
|
border: 2px solid $color-brand;
|
||||||
border-radius: $default-border-radius;
|
border-radius: $default-border-radius;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
fill: $color-brand;
|
fill: $color-brand;
|
||||||
margin-right: $small-spacing;
|
margin-right: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
@include navigation-link;
|
||||||
|
color: $color-brand;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
|
||||||
@include navigation-link;
|
|
||||||
color: $color-brand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="portfolio-onboarding">
|
<div class="portfolio-onboarding">
|
||||||
<h1 class="portfolio-onboarding__heading" data-cy="page-title">Portfolio</h1>
|
<h1
|
||||||
<portfolio-illustration data-cy="portfolio-onboarding-illustration" class="portfolio-onboarding__illustration" />
|
class="portfolio-onboarding__heading"
|
||||||
<h2 class="portfolio-onboarding__subheading" data-cy="portfolio-onboarding-subtitle">Woran denken Sie gerade?</h2>
|
data-cy="page-title"
|
||||||
<p class="portfolio-onboarding__text" data-cy="portfolio-onboarding-text">
|
>
|
||||||
|
Portfolio
|
||||||
|
</h1>
|
||||||
|
<portfolio-illustration
|
||||||
|
data-cy="portfolio-onboarding-illustration"
|
||||||
|
class="portfolio-onboarding__illustration"
|
||||||
|
/>
|
||||||
|
<h2
|
||||||
|
class="portfolio-onboarding__subheading"
|
||||||
|
data-cy="portfolio-onboarding-subtitle"
|
||||||
|
>
|
||||||
|
Woran denken Sie gerade?
|
||||||
|
</h2>
|
||||||
|
<p
|
||||||
|
class="portfolio-onboarding__text"
|
||||||
|
data-cy="portfolio-onboarding-text"
|
||||||
|
>
|
||||||
Hier können Sie Projekte erstellen, um Ihre Gedanken festzuhalten oder Ihre Arbeit zu dokumentieren.
|
Hier können Sie Projekte erstellen, um Ihre Gedanken festzuhalten oder Ihre Arbeit zu dokumentieren.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -12,33 +28,34 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const PortfolioIllustration = () => import('@/components/illustrations/PortfolioIllustration.vue');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const CreateProjectButton = () => import('@/components/portfolio/CreateProjectButton.vue');
|
const PortfolioIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/PortfolioIllustration'));
|
||||||
export default {
|
const CreateProjectButton = defineAsyncComponent(() => import('@/components/portfolio/CreateProjectButton'));
|
||||||
components: { CreateProjectButton, PortfolioIllustration },
|
export default {
|
||||||
};
|
components: {CreateProjectButton, PortfolioIllustration},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.portfolio-onboarding {
|
.portfolio-onboarding {
|
||||||
@include onboarding-page;
|
@include onboarding-page;
|
||||||
|
|
||||||
&__heading {
|
&__heading {
|
||||||
@include heading-1;
|
@include heading-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subheading {
|
||||||
|
@include heading-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__illustration {
|
||||||
|
@include onboarding-illustration;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
@include onboarding-text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__subheading {
|
|
||||||
@include heading-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__illustration {
|
|
||||||
@include onboarding-illustration;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
@include onboarding-text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,48 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="project-actions" data-cy="project-actions">
|
<div
|
||||||
<a class="project-actions__more-link" @click.stop="toggleMenu">
|
class="project-actions"
|
||||||
|
data-cy="project-actions"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="project-actions__more-link"
|
||||||
|
@click.stop="toggleMenu"
|
||||||
|
>
|
||||||
<ellipses />
|
<ellipses />
|
||||||
</a>
|
</a>
|
||||||
<widget-popover class="project-actions__popover" v-if="showMenu" @hide-me="showMenu = false">
|
<widget-popover
|
||||||
|
class="project-actions__popover"
|
||||||
|
v-if="showMenu"
|
||||||
|
@hide-me="showMenu = false"
|
||||||
|
>
|
||||||
<li class="popover-links__link">
|
<li class="popover-links__link">
|
||||||
<a data-cy="delete-project" @click="deleteProject(slug)">Projekt löschen</a>
|
<a
|
||||||
|
data-cy="delete-project"
|
||||||
|
@click="deleteProject(slug)"
|
||||||
|
>Projekt löschen</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="popover-links__link">
|
<li class="popover-links__link">
|
||||||
<a data-cy="edit-project" @click="editProject(slug)">Projekt bearbeiten</a>
|
<a
|
||||||
|
data-cy="edit-project"
|
||||||
|
@click="editProject(slug)"
|
||||||
|
>Projekt bearbeiten</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="popover-links__link" v-if="!final && shareButtons">
|
<li
|
||||||
<a data-cy="share-project" @click="updateProjectShareState(slug, true)">Projekt teilen</a>
|
class="popover-links__link"
|
||||||
|
v-if="!final && shareButtons"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
data-cy="share-project"
|
||||||
|
@click="updateProjectShareState(slug, true)"
|
||||||
|
>Projekt teilen</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="popover-links__link" v-if="final && shareButtons">
|
<li
|
||||||
<a data-cy="unshare-project" @click="updateProjectShareState(slug, false)">Projekt nicht mehr teilen</a>
|
class="popover-links__link"
|
||||||
|
v-if="final && shareButtons"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
data-cy="unshare-project"
|
||||||
|
@click="updateProjectShareState(slug, false)"
|
||||||
|
>Projekt nicht mehr teilen</a>
|
||||||
</li>
|
</li>
|
||||||
</widget-popover>
|
</widget-popover>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -27,8 +55,9 @@ import DELETE_PROJECT_MUTATION from '@/graphql/gql/mutations/deleteProject.gql';
|
||||||
import PROJECTS_QUERY from '@/graphql/gql/queries/allProjects.gql';
|
import PROJECTS_QUERY from '@/graphql/gql/queries/allProjects.gql';
|
||||||
|
|
||||||
import updateProjectShareState from '@/mixins/update-project-share-state';
|
import updateProjectShareState from '@/mixins/update-project-share-state';
|
||||||
import { removeAtIndex } from '@/graphql/immutable-operations.ts';
|
import {removeAtIndex} from '@/graphql/immutable-operations.ts';
|
||||||
const Ellipses = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Ellipses.vue');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const Ellipses = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Ellipses.vue'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -64,50 +93,42 @@ export default {
|
||||||
this.showMenu = !this.showMenu;
|
this.showMenu = !this.showMenu;
|
||||||
},
|
},
|
||||||
editProject(slug) {
|
editProject(slug) {
|
||||||
this.$router.push({ name: 'edit-project', params: { slug } });
|
this.$router.push({name: 'edit-project', params: {slug}});
|
||||||
},
|
},
|
||||||
deleteProject(slug) {
|
deleteProject(slug) {
|
||||||
this.$apollo
|
this.$apollo.mutate({
|
||||||
.mutate({
|
mutation: DELETE_PROJECT_MUTATION,
|
||||||
mutation: DELETE_PROJECT_MUTATION,
|
variables: {
|
||||||
variables: {
|
input: {
|
||||||
input: {
|
slug,
|
||||||
slug,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
update(
|
},
|
||||||
store,
|
update(store, {data: {deleteProject: {success}}}) {
|
||||||
{
|
if (success) {
|
||||||
data: {
|
const {projects: prevProjects} = store.readQuery({query: PROJECTS_QUERY});
|
||||||
deleteProject: { success },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
if (success) {
|
|
||||||
const { projects: prevProjects } = store.readQuery({ query: PROJECTS_QUERY });
|
|
||||||
|
|
||||||
if (prevProjects) {
|
|
||||||
let index = prevProjects.findIndex((project) => project.slug === slug);
|
|
||||||
const projects = removeAtIndex(prevProjects, index);
|
|
||||||
|
|
||||||
const data = {
|
if (prevProjects) {
|
||||||
projects,
|
let index = prevProjects.findIndex(project => project.slug === slug);
|
||||||
};
|
const projects = removeAtIndex(prevProjects, index);
|
||||||
store.writeQuery({ query: PROJECTS_QUERY, data });
|
|
||||||
}
|
const data = {
|
||||||
|
projects
|
||||||
|
};
|
||||||
|
store.writeQuery({query: PROJECTS_QUERY, data});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
},
|
||||||
.then(() => {
|
}).then(() => {
|
||||||
this.$router.push('/portfolio');
|
this.$router.push('/portfolio');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/_helpers.scss';
|
@import "~styles/_helpers.scss";
|
||||||
|
|
||||||
.project-actions {
|
.project-actions {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,44 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="project-entry" data-cy="project-entry">
|
<div
|
||||||
<more-options-widget class="project-entry__more" data-cy="project-entry-more" v-if="!readOnly">
|
class="project-entry"
|
||||||
|
data-cy="project-entry"
|
||||||
|
>
|
||||||
|
<more-options-widget
|
||||||
|
class="project-entry__more"
|
||||||
|
data-cy="project-entry-more"
|
||||||
|
v-if="!readOnly"
|
||||||
|
>
|
||||||
<li class="popover-links__link">
|
<li class="popover-links__link">
|
||||||
<a data-cy="edit-project-entry" @click="editProjectEntry()">Eintrag bearbeiten</a>
|
<a
|
||||||
|
data-cy="edit-project-entry"
|
||||||
|
@click="editProjectEntry()"
|
||||||
|
>Eintrag bearbeiten</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="popover-links__link">
|
<li class="popover-links__link">
|
||||||
<a data-cy="delete-project-entry" @click="deleteProjectEntry()">Eintrag löschen</a>
|
<a
|
||||||
|
data-cy="delete-project-entry"
|
||||||
|
@click="deleteProjectEntry()"
|
||||||
|
>Eintrag löschen</a>
|
||||||
</li>
|
</li>
|
||||||
</more-options-widget>
|
</more-options-widget>
|
||||||
|
|
||||||
<h3 class="project-entry__heading" data-cy="project-entry-date">
|
<h3
|
||||||
|
class="project-entry__heading"
|
||||||
|
data-cy="project-entry-date"
|
||||||
|
>
|
||||||
{{ createdDateTime }}
|
{{ createdDateTime }}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="project-entry__paragraph" data-cy="project-entry-activity">
|
<p
|
||||||
|
class="project-entry__paragraph"
|
||||||
|
data-cy="project-entry-activity"
|
||||||
|
>
|
||||||
{{ description }}
|
{{ description }}
|
||||||
</p>
|
</p>
|
||||||
<p class="project-entry__paragraph" v-if="documentUrl">
|
<p
|
||||||
<document-block :value="{ url: documentUrl }" />
|
class="project-entry__paragraph"
|
||||||
|
v-if="documentUrl"
|
||||||
|
>
|
||||||
|
<document-block :value="{url: documentUrl}" />
|
||||||
</p>
|
</p>
|
||||||
<div class="project-entry__date">
|
<div class="project-entry__date">
|
||||||
{{ createdDate }}
|
{{ createdDate }}
|
||||||
|
|
@ -25,116 +47,110 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
||||||
|
|
||||||
import DELETE_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/deleteProjectEntry.gql';
|
import DELETE_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/deleteProjectEntry.gql';
|
||||||
import PROJECT_QUERY from '@/graphql/gql/queries/projectQuery.gql';
|
import PROJECT_QUERY from '@/graphql/gql/queries/projectQuery.gql';
|
||||||
import { dateFilter, dateTimeFilter } from '@/filters/date-filter';
|
import {dateFilter, dateTimeFilter} from '@/filters/date-filter';
|
||||||
import { removeAtIndex } from '@/graphql/immutable-operations.ts';
|
import {removeAtIndex} from '@/graphql/immutable-operations.ts';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'));
|
||||||
|
|
||||||
const DocumentBlock = () =>
|
|
||||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/DocumentBlock');
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['description', 'documentUrl', 'created', 'id', 'readOnly'],
|
props: ['description', 'documentUrl', 'created', 'id', 'readOnly'],
|
||||||
components: {
|
components: {
|
||||||
DocumentBlock,
|
DocumentBlock,
|
||||||
MoreOptionsWidget,
|
MoreOptionsWidget,
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
createdDate() {
|
|
||||||
return dateFilter(this.created);
|
|
||||||
},
|
},
|
||||||
createdDateTime() {
|
|
||||||
return dateTimeFilter(this.created);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
computed: {
|
||||||
editProjectEntry() {
|
createdDate() {
|
||||||
this.$store.dispatch('editProjectEntry', this.id);
|
return dateFilter(this.created);
|
||||||
|
},
|
||||||
|
createdDateTime() {
|
||||||
|
return dateTimeFilter(this.created);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
deleteProjectEntry() {
|
|
||||||
const projectEntry = this; // otherwise we run into scope errors
|
methods: {
|
||||||
this.$apollo.mutate({
|
editProjectEntry() {
|
||||||
mutation: DELETE_PROJECT_ENTRY_MUTATION,
|
this.$store.dispatch('editProjectEntry', this.id);
|
||||||
variables: {
|
},
|
||||||
input: {
|
deleteProjectEntry() {
|
||||||
id: this.id,
|
const projectEntry = this; // otherwise we run into scope errors
|
||||||
},
|
this.$apollo.mutate({
|
||||||
},
|
mutation: DELETE_PROJECT_ENTRY_MUTATION,
|
||||||
update(
|
variables: {
|
||||||
store,
|
input: {
|
||||||
{
|
id: this.id,
|
||||||
data: {
|
|
||||||
deleteProjectEntry: { success },
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
) {
|
update(store, {data: {deleteProjectEntry: {success}}}) {
|
||||||
if (success) {
|
if (success) {
|
||||||
const query = PROJECT_QUERY;
|
const query = PROJECT_QUERY;
|
||||||
const variables = {
|
const variables = {
|
||||||
slug: projectEntry.$route.params.slug,
|
slug: projectEntry.$route.params.slug,
|
||||||
};
|
|
||||||
const { project } = store.readQuery({ query, variables });
|
|
||||||
if (project) {
|
|
||||||
const index = project.entries.findIndex((entry) => entry.id === projectEntry.id);
|
|
||||||
const entries = removeAtIndex(project.entries, index);
|
|
||||||
const data = {
|
|
||||||
project: {
|
|
||||||
...project,
|
|
||||||
entries,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
store.writeQuery({ query, variables, data });
|
const {project} = store.readQuery({query, variables});
|
||||||
|
if (project) {
|
||||||
|
const index = project.entries.findIndex(entry => entry.id === projectEntry.id);
|
||||||
|
const entries = removeAtIndex(project.entries, index);
|
||||||
|
const data = {
|
||||||
|
project: {
|
||||||
|
...project,
|
||||||
|
entries,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
store.writeQuery({query, variables, data});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
});
|
||||||
});
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.project-entry {
|
.project-entry {
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
border-radius: $default-border-radius;
|
border-radius: $default-border-radius;
|
||||||
padding: 30px 20px;
|
padding: 30px 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&__heading {
|
&__heading {
|
||||||
font-size: toRem(22px);
|
font-size: toRem(22px);
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
|
||||||
|
|
||||||
&__paragraph {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
white-space: pre-line;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__date {
|
|
||||||
font-family: $sans-serif-font-family;
|
|
||||||
color: $color-silver-dark;
|
|
||||||
font-size: toRem(17px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
cursor: pointer;
|
|
||||||
@include heading-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__more {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
display: none;
|
|
||||||
@include desktop {
|
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__paragraph {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__date {
|
||||||
|
font-family: $sans-serif-font-family;
|
||||||
|
color: $color-silver-dark;
|
||||||
|
font-size: toRem(17px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
cursor: pointer;
|
||||||
|
@include heading-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__more {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
display: none;
|
||||||
|
@include desktop {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<modal :hide-header="false">
|
<modal :hide-header="false">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h2 class="project-entry-modal__heading" data-cy="modal-title">Beitrag erfassen</h2>
|
<h2
|
||||||
|
class="project-entry-modal__heading"
|
||||||
|
data-cy="modal-title"
|
||||||
|
>
|
||||||
|
Beitrag erfassen
|
||||||
|
</h2>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="project-entry-modal">
|
<div class="project-entry-modal">
|
||||||
|
|
@ -18,7 +23,7 @@
|
||||||
icon="document-with-lines-icon"
|
icon="document-with-lines-icon"
|
||||||
data-cy="use-template-button"
|
data-cy="use-template-button"
|
||||||
text="Vorlage nutzen"
|
text="Vorlage nutzen"
|
||||||
@click.native="useTemplate"
|
@click="useTemplate"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<file-upload
|
<file-upload
|
||||||
|
|
@ -30,101 +35,107 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div slot="footer">
|
<template #footer>
|
||||||
<a class="button button--primary" data-cy="modal-save-button" @click="$emit('save', localProjectEntry)"
|
<a
|
||||||
>Speichern</a
|
class="button button--primary"
|
||||||
>
|
data-cy="modal-save-button"
|
||||||
<a class="button" @click="$emit('hide')">Abbrechen</a>
|
@click="$emit('save', localProjectEntry)"
|
||||||
</div>
|
>Speichern</a>
|
||||||
|
<a
|
||||||
|
class="button"
|
||||||
|
@click="$emit('hide')"
|
||||||
|
>Abbrechen</a>
|
||||||
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
import ButtonWithIconAndText from '@/components/ui/ButtonWithIconAndText';
|
import ButtonWithIconAndText from '@/components/ui/ButtonWithIconAndText';
|
||||||
|
|
||||||
import { PROJECT_ENTRY_TEMPLATE } from '@/consts/strings.consts';
|
import {PROJECT_ENTRY_TEMPLATE} from '@/consts/strings.consts';
|
||||||
const FileUpload = () => import('@/components/ui/file-upload/FileUpload.vue');
|
|
||||||
|
|
||||||
export default {
|
import {defineAsyncComponent} from 'vue';
|
||||||
props: {
|
const FileUpload = defineAsyncComponent(() => import('@/components/ui/file-upload/FileUpload'));
|
||||||
projectEntry: {
|
|
||||||
type: Object,
|
export default {
|
||||||
default: null,
|
props: {
|
||||||
|
projectEntry: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
FileUpload,
|
FileUpload,
|
||||||
ButtonWithIconAndText,
|
ButtonWithIconAndText,
|
||||||
Modal,
|
Modal,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
localProjectEntry: Object.assign(
|
localProjectEntry: Object.assign({}, {
|
||||||
{},
|
|
||||||
{
|
|
||||||
...this.projectEntry,
|
...this.projectEntry,
|
||||||
}
|
}),
|
||||||
),
|
};
|
||||||
};
|
},
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setDocumentUrl(url) {
|
setDocumentUrl(url) {
|
||||||
this.localProjectEntry.documentUrl = url;
|
this.localProjectEntry.documentUrl = url;
|
||||||
|
},
|
||||||
|
useTemplate() {
|
||||||
|
this.localProjectEntry.description = `${this.localProjectEntry.description}${PROJECT_ENTRY_TEMPLATE}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
useTemplate() {
|
};
|
||||||
this.localProjectEntry.description = `${this.localProjectEntry.description}${PROJECT_ENTRY_TEMPLATE}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.project-entry-modal {
|
.project-entry-modal {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
&__form-field {
|
|
||||||
@include inputstyle;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
grid-template-rows: auto 1rem;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__textarea {
|
&__form-field {
|
||||||
@include auto-grow;
|
@include inputstyle;
|
||||||
border: 0;
|
padding: 0;
|
||||||
min-height: 400px;
|
display: flex;
|
||||||
padding: $medium-spacing;
|
flex-direction: column;
|
||||||
}
|
grid-template-rows: auto 1rem;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
&__buttons {
|
width: 100%;
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
padding: $medium-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__button {
|
|
||||||
@include regular-text;
|
|
||||||
|
|
||||||
&--template {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--document {
|
&__textarea {
|
||||||
|
@include auto-grow;
|
||||||
|
border: 0;
|
||||||
|
min-height: 400px;
|
||||||
|
padding: $medium-spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
padding: $medium-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
@include regular-text;
|
||||||
|
|
||||||
|
&--template {
|
||||||
|
}
|
||||||
|
|
||||||
|
&--document {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__heading {
|
||||||
|
@include heading-3;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__heading {
|
|
||||||
@include heading-3;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<page-form :title="title" @save="$emit('save', localProject)">
|
<page-form
|
||||||
<page-form-input label="Titel" v-model="localProject.title" />
|
:title="title"
|
||||||
<page-form-input label="Beschreibung" type="textarea" v-model="localProject.description" />
|
@save="$emit('save', localProject)"
|
||||||
<template slot="footer">
|
>
|
||||||
|
<page-form-input
|
||||||
|
label="Titel"
|
||||||
|
v-model="localProject.title"
|
||||||
|
/>
|
||||||
|
<page-form-input
|
||||||
|
label="Beschreibung"
|
||||||
|
type="textarea"
|
||||||
|
v-model="localProject.description"
|
||||||
|
/>
|
||||||
|
<template #footer>
|
||||||
<button
|
<button
|
||||||
:class="{ 'button--disabled': !formValid }"
|
:class="{ 'button--disabled': !formValid }"
|
||||||
:disabled="!formValid"
|
:disabled="!formValid"
|
||||||
|
|
@ -12,7 +22,12 @@
|
||||||
>
|
>
|
||||||
Speichern
|
Speichern
|
||||||
</button>
|
</button>
|
||||||
<router-link to="/portfolio" tag="button" class="button"> Abbrechen </router-link>
|
<router-link
|
||||||
|
to="/portfolio"
|
||||||
|
class="button"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
</page-form>
|
</page-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<li class="project">
|
<li class="project">
|
||||||
<router-link
|
<router-link :to="{ name: 'project', params: { slug: project.slug } }" class="project__link" data-cy="project-link">
|
||||||
:to="{ name: 'project', params: { slug: project.slug } }"
|
|
||||||
tag="div"
|
|
||||||
class="project__link"
|
|
||||||
data-cy="project-link"
|
|
||||||
>
|
|
||||||
<span class="project__title" data-cy="project-title">{{ project.title }}</span>
|
<span class="project__title" data-cy="project-title">{{ project.title }}</span>
|
||||||
|
|
||||||
<owner-widget :owner="project.student" class="project__owner" />
|
<owner-widget :owner="project.student" class="project__owner" />
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<a class="share-icon">
|
<a
|
||||||
|
class="share-icon"
|
||||||
|
@click="$emit('share')"
|
||||||
|
>
|
||||||
<share-icon class="share-icon__icon" />
|
<share-icon class="share-icon__icon" />
|
||||||
<span class="share-icon__text">
|
<span class="share-icon__text">
|
||||||
<template v-if="!final">Mit Lehrperson teilen</template>
|
<template v-if="!final">Mit Lehrperson teilen</template>
|
||||||
|
|
@ -9,35 +12,37 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const ShareIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ShareIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const ShareIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ShareIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
final: {
|
final: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
emits: ['share'],
|
||||||
components: { ShareIcon },
|
components: {ShareIcon},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.share-icon {
|
.share-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
margin-right: $small-spacing;
|
margin-right: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
@include large-link;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
|
||||||
@include large-link;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -7,60 +7,64 @@
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="activity-entry__link" @click="$emit('link')">
|
<div
|
||||||
|
class="activity-entry__link"
|
||||||
|
@click="$emit('link')"
|
||||||
|
>
|
||||||
<chevron-right class="activity-entry__icon" />
|
<chevron-right class="activity-entry__icon" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const ChevronRight = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronRight');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const ChevronRight = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['title'],
|
props: ['title'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ChevronRight,
|
ChevronRight
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</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";
|
||||||
|
|
||||||
.activity-entry {
|
.activity-entry {
|
||||||
padding: $small-spacing 0;
|
padding: $small-spacing 0;
|
||||||
border-bottom: 1px solid $color-silver;
|
border-bottom: 1px solid $color-silver;
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
@include small-text;
|
|
||||||
// todo: make style definition for small text and silver color
|
|
||||||
color: $color-silver-dark;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
flex-grow: 1;
|
|
||||||
@include regular-text;
|
|
||||||
line-height: $default-line-height;
|
|
||||||
}
|
|
||||||
&__link {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 0;
|
justify-content: space-between;
|
||||||
align-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__icon {
|
&__title {
|
||||||
fill: $color-brand;
|
@include small-text;
|
||||||
width: 30px;
|
// todo: make style definition for small text and silver color
|
||||||
}
|
color: $color-silver-dark;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(p) {
|
&__content {
|
||||||
@include regular-text;
|
flex-grow: 1;
|
||||||
|
@include regular-text;
|
||||||
|
line-height: $default-line-height;
|
||||||
|
}
|
||||||
|
&__link {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 0;
|
||||||
|
align-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
fill: $color-brand;
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ p {
|
||||||
|
@include regular-text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,31 @@
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<default-avatar
|
<default-avatar
|
||||||
:class="{ 'avatar__placeholder--highlighted': iconHighlighted }"
|
:class="{'avatar__placeholder--highlighted': iconHighlighted}"
|
||||||
class="avatar__placeholder"
|
class="avatar__placeholder"
|
||||||
v-show="!isAvatarLoaded"
|
v-show="!isAvatarLoaded"
|
||||||
/>
|
/>
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="show">
|
<transition name="show">
|
||||||
<div
|
<div
|
||||||
:style="{ 'background-image': `url(${avatarUrl})` }"
|
:style="{'background-image': `url(${avatarUrl})`}"
|
||||||
class="avatar__image"
|
class="avatar__image"
|
||||||
v-show="isAvatarLoaded"
|
v-show="isAvatarLoaded"
|
||||||
ref="avatarImage"
|
ref="avatarImage"
|
||||||
/>
|
/>
|
||||||
</transition>
|
</transition>
|
||||||
<img :src="avatarUrl" class="avatar__fake-image" ref="fakeImage" />
|
<img
|
||||||
|
:src="avatarUrl"
|
||||||
|
class="avatar__fake-image"
|
||||||
|
ref="fakeImage"
|
||||||
|
>
|
||||||
|
|
||||||
<div class="avatar__edit" v-if="editable" @click="closeSidebar">
|
<div
|
||||||
<router-link :to="{ name: 'profile' }">
|
class="avatar__edit"
|
||||||
|
v-if="editable"
|
||||||
|
@click="closeSidebar"
|
||||||
|
>
|
||||||
|
<router-link :to="{name: 'profile'}">
|
||||||
<pen-icon />
|
<pen-icon />
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -26,121 +34,120 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TOGGLE_SIDEBAR from '@/graphql/gql/local/mutations/toggleSidebar.gql';
|
import TOGGLE_SIDEBAR from '@/graphql/gql/local/mutations/toggleSidebar.gql';
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
const DefaultAvatar = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DefaultAvatar');
|
const DefaultAvatar = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DefaultAvatar'));
|
||||||
const PenIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PenIcon');
|
const PenIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PenIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
avatarUrl: {
|
avatarUrl: {
|
||||||
type: String,
|
type: String
|
||||||
|
},
|
||||||
|
iconHighlighted: {},
|
||||||
|
editable: {
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
iconHighlighted: {},
|
components: {
|
||||||
editable: {
|
DefaultAvatar,
|
||||||
default: false,
|
PenIcon
|
||||||
},
|
},
|
||||||
},
|
data() {
|
||||||
components: {
|
return {
|
||||||
DefaultAvatar,
|
isAvatarLoaded: false
|
||||||
PenIcon,
|
};
|
||||||
},
|
},
|
||||||
data() {
|
mounted() {
|
||||||
return {
|
if (this.avatarUrl !== '') {
|
||||||
isAvatarLoaded: false,
|
this.$refs.fakeImage.addEventListener('load', () => {
|
||||||
};
|
if (this.$refs.fakeImage) {
|
||||||
},
|
this.$refs.fakeImage.remove();
|
||||||
mounted() {
|
this.isAvatarLoaded = true;
|
||||||
if (this.avatarUrl !== '') {
|
}
|
||||||
this.$refs.fakeImage.addEventListener('load', () => {
|
});
|
||||||
if (this.$refs.fakeImage) {
|
}
|
||||||
this.$refs.fakeImage.remove();
|
},
|
||||||
this.isAvatarLoaded = true;
|
methods: {
|
||||||
}
|
closeSidebar() {
|
||||||
});
|
this.$apollo.mutate({
|
||||||
|
mutation: TOGGLE_SIDEBAR,
|
||||||
|
variables: {
|
||||||
|
sidebar: {
|
||||||
|
profile: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
methods: {
|
|
||||||
closeSidebar() {
|
|
||||||
this.$apollo.mutate({
|
|
||||||
mutation: TOGGLE_SIDEBAR,
|
|
||||||
variables: {
|
|
||||||
sidebar: {
|
|
||||||
profile: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
$max-width: 100%;
|
$max-width: 100%;
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
height: $max-width;
|
|
||||||
width: $max-width;
|
|
||||||
overflow: hidden;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&__placeholder {
|
|
||||||
height: $max-width;
|
height: $max-width;
|
||||||
fill: $color-silver-dark;
|
width: $max-width;
|
||||||
border-radius: 50%;
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
&--highlighted {
|
&__placeholder {
|
||||||
fill: $color-brand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__image {
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center center;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
&--landscape {
|
|
||||||
width: auto;
|
|
||||||
height: $max-width;
|
height: $max-width;
|
||||||
|
fill: $color-silver-dark;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
&--highlighted {
|
||||||
|
fill: $color-brand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
&--landscape {
|
||||||
|
width: auto;
|
||||||
|
height: $max-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fake-image {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__edit {
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
display: block;
|
||||||
|
left: 50%;
|
||||||
|
bottom: -3px;
|
||||||
|
transform: translateX(80%);
|
||||||
|
background-color: $color-white;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 6px;
|
||||||
|
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-leave-active, .show-enter-active {
|
||||||
|
transition: opacity .5s;
|
||||||
|
}
|
||||||
|
.fade-leave-to, .show-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-enter-to {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__fake-image {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__edit {
|
|
||||||
position: absolute;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 34px;
|
|
||||||
height: 34px;
|
|
||||||
display: block;
|
|
||||||
left: 50%;
|
|
||||||
bottom: -3px;
|
|
||||||
transform: translateX(80%);
|
|
||||||
background-color: $color-white;
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: 6px;
|
|
||||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-leave-active,
|
|
||||||
.show-enter-active {
|
|
||||||
transition: opacity 0.5s;
|
|
||||||
}
|
|
||||||
.fade-leave-to,
|
|
||||||
.show-enter {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-enter-to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="content-bookmark module-activity-entry">
|
<div class="content-bookmark module-activity-entry">
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<div v-if="content.type === 'text_block'" v-html="text" />
|
<div
|
||||||
|
v-if="content.type === 'text_block'"
|
||||||
|
v-html="text"
|
||||||
|
/>
|
||||||
<div v-else-if="content.type === 'link_block'">
|
<div v-else-if="content.type === 'link_block'">
|
||||||
<link-block :value="content.value" :no-margin="true" />
|
<link-block
|
||||||
|
:value="content.value"
|
||||||
|
:no-margin="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p v-else>
|
<p v-else>
|
||||||
{{ type }}
|
{{ type }}
|
||||||
|
|
@ -12,34 +18,35 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const LinkBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/LinkBlock');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const LinkBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/LinkBlock'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['bookmark'],
|
props: ['bookmark'],
|
||||||
components: { LinkBlock },
|
components: {LinkBlock},
|
||||||
computed: {
|
computed: {
|
||||||
content() {
|
content() {
|
||||||
return this.bookmark.contentBlock
|
return this.bookmark.contentBlock
|
||||||
? this.bookmark.contentBlock.contents.find((e) => e.id === this.bookmark.uuid)
|
? this.bookmark.contentBlock.contents.find(e => e.id === this.bookmark.uuid)
|
||||||
: this.bookmark.instrument.contents.find((e) => e.id === this.bookmark.uuid);
|
: this.bookmark.instrument.contents.find(e => e.id === this.bookmark.uuid);
|
||||||
},
|
},
|
||||||
text() {
|
text() {
|
||||||
return this.content.value.text ? this.content.value.text : 'TO BE DEFINED';
|
return this.content.value.text ? this.content.value.text : 'TO BE DEFINED';
|
||||||
},
|
},
|
||||||
type() {
|
type() {
|
||||||
switch (this.content.type) {
|
switch (this.content.type) {
|
||||||
case 'assignment':
|
case 'assignment':
|
||||||
return 'Aufgabe & Ergebnis';
|
return 'Aufgabe & Ergebnis';
|
||||||
case 'link_block':
|
case 'link_block':
|
||||||
return this.content;
|
return this.content;
|
||||||
case 'survey':
|
case 'survey':
|
||||||
return 'Übung';
|
return 'Übung';
|
||||||
case 'image_url_block':
|
case 'image_url_block':
|
||||||
return 'Bild';
|
return 'Bild';
|
||||||
default:
|
default:
|
||||||
return this.content.type;
|
return this.content.type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
};
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,32 @@
|
||||||
<template>
|
<template>
|
||||||
<a class="edit-group-name" data-cy="edit-group-name-link" @click="$emit('edit')">
|
<a
|
||||||
|
class="edit-group-name"
|
||||||
|
data-cy="edit-group-name-link"
|
||||||
|
@click="$emit('edit')"
|
||||||
|
>
|
||||||
<pen-icon class="edit-group-name__icon" />
|
<pen-icon class="edit-group-name__icon" />
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const PenIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PenIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const PenIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PenIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PenIcon,
|
PenIcon
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/_variables.scss';
|
@import "~styles/_variables.scss";
|
||||||
|
|
||||||
.edit-group-name {
|
.edit-group-name {
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
fill: $color-brand;
|
fill: $color-brand;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,15 @@
|
||||||
|
|
||||||
<modal-input :value="name" :placeholder="placeholder" data-cy="edit-name-input" @input="$emit('input', $event)" />
|
<modal-input :value="name" :placeholder="placeholder" data-cy="edit-name-input" @input="$emit('input', $event)" />
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div slot="footer">
|
<a
|
||||||
<a class="button button--primary" data-cy="modal-save-button" @click="$emit('save')">Speichern</a>
|
class="button button--primary"
|
||||||
<a class="button" @click="$emit('cancel')">Abbrechen</a>
|
data-cy="modal-save-button"
|
||||||
</div>
|
@click="$emit('save')"
|
||||||
|
>Speichern</a>
|
||||||
|
<a
|
||||||
|
class="button"
|
||||||
|
@click="$emit('cancel')"
|
||||||
|
>Abbrechen</a>
|
||||||
</template>
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,105 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="profile">
|
<div class="profile">
|
||||||
<h1 class="profile__header">Profilbild</h1>
|
<h1 class="profile__header">
|
||||||
<div class="profile-avatar" v-if="me.avatarUrl">
|
Profilbild
|
||||||
|
</h1>
|
||||||
|
<div
|
||||||
|
class="profile-avatar"
|
||||||
|
v-if="me.avatarUrl"
|
||||||
|
>
|
||||||
<div class="profile-avatar__image">
|
<div class="profile-avatar__image">
|
||||||
<avatar :avatar-url="me.avatarUrl" />
|
<avatar :avatar-url="me.avatarUrl" />
|
||||||
</div>
|
</div>
|
||||||
<a class="profile-avatar__remove icon-button" @click="deleteAvatar()">
|
<a
|
||||||
|
class="profile-avatar__remove icon-button"
|
||||||
|
@click="deleteAvatar()"
|
||||||
|
>
|
||||||
<trash-icon class="profile-avatar__remove-icon icon-button__icon icon-button__icon--subtle" />
|
<trash-icon class="profile-avatar__remove-icon icon-button__icon icon-button__icon--subtle" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<avatar-upload-form v-else @avatarUpdate="updateAvatar" />
|
<avatar-upload-form
|
||||||
|
v-else
|
||||||
|
@avatarUpdate="updateAvatar"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import UPDATE_AVATAR_QUERY from '@/graphql/gql/mutations/updateAvatarUrl.gql';
|
import UPDATE_AVATAR_QUERY from '@/graphql/gql/mutations/updateAvatarUrl.gql';
|
||||||
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
||||||
import AvatarUploadForm from '@/components/profile/AvatarUploadForm';
|
import AvatarUploadForm from '@/components/profile/AvatarUploadForm';
|
||||||
import Avatar from '@/components/profile/Avatar';
|
import Avatar from '@/components/profile/Avatar';
|
||||||
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AvatarUploadForm,
|
AvatarUploadForm,
|
||||||
Avatar,
|
Avatar,
|
||||||
TrashIcon,
|
TrashIcon
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
me: {
|
||||||
|
avatarUrl: ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
apollo: {
|
||||||
me: {
|
me: {
|
||||||
avatarUrl: '',
|
query: ME_QUERY,
|
||||||
},
|
},
|
||||||
};
|
|
||||||
},
|
|
||||||
apollo: {
|
|
||||||
me: {
|
|
||||||
query: ME_QUERY,
|
|
||||||
},
|
},
|
||||||
},
|
methods: {
|
||||||
methods: {
|
deleteAvatar () {
|
||||||
deleteAvatar() {
|
this.updateAvatar('');
|
||||||
this.updateAvatar('');
|
},
|
||||||
},
|
updateAvatar (url) {
|
||||||
updateAvatar(url) {
|
this.$apollo.mutate({
|
||||||
this.$apollo
|
|
||||||
.mutate({
|
|
||||||
mutation: UPDATE_AVATAR_QUERY,
|
mutation: UPDATE_AVATAR_QUERY,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
avatarUrl: url,
|
avatarUrl: url
|
||||||
},
|
|
||||||
},
|
|
||||||
update(
|
|
||||||
store,
|
|
||||||
{
|
|
||||||
data: {
|
|
||||||
updateAvatar: { success },
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
) {
|
},
|
||||||
|
update(store, {data: {updateAvatar: {success}}}) {
|
||||||
if (success) {
|
if (success) {
|
||||||
const { me } = store.readQuery({ query: ME_QUERY });
|
const {me} = store.readQuery({query: ME_QUERY});
|
||||||
if (me) {
|
if (me) {
|
||||||
const data = {
|
const data = {
|
||||||
me: {
|
me: {
|
||||||
...me,
|
...me,
|
||||||
avatarUrl: url,
|
avatarUrl: url
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
store.writeQuery({ query: ME_QUERY, data });
|
store.writeQuery({query: ME_QUERY, data});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
}).catch((error) => {
|
||||||
.catch((error) => {
|
|
||||||
console.warn('UploadError', error);
|
console.warn('UploadError', error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '@/styles/_variables.scss';
|
@import "@/styles/_variables.scss";
|
||||||
|
|
||||||
.profile-avatar {
|
.profile-avatar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
&__image {
|
&__image {
|
||||||
height: 230px;
|
height: 230px;
|
||||||
width: 230px;
|
width: 230px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-avatar {
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.profile-avatar {
|
|
||||||
margin-bottom: $large-spacing;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,69 @@
|
||||||
<template>
|
<template>
|
||||||
<transition name="slide">
|
<transition name="slide">
|
||||||
<div class="profile-sidebar" data-cy="sidebar" v-if="sidebar.profile" v-click-outside="close">
|
<div
|
||||||
<a class="profile-sidebar__close-link" data-cy="close-profile-sidebar-link" @click="close">
|
class="profile-sidebar"
|
||||||
|
data-cy="sidebar"
|
||||||
|
v-if="sidebar.profile"
|
||||||
|
v-click-outside="close"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="profile-sidebar__close-link"
|
||||||
|
data-cy="close-profile-sidebar-link"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
<cross class="profile-sidebar__close-icon" />
|
<cross class="profile-sidebar__close-icon" />
|
||||||
</a>
|
</a>
|
||||||
<div class="profile-sidebar__section">
|
<div class="profile-sidebar__section">
|
||||||
<profile-widget class="profile-sidebar__item" />
|
<profile-widget class="profile-sidebar__item" />
|
||||||
<div class="profile-sidebar__item" @click="close">
|
<div
|
||||||
<router-link to="/me/activity" class="profile-sidebar__link"> Meine Aktivitäten </router-link>
|
class="profile-sidebar__item"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
to="/me/activity"
|
||||||
|
class="profile-sidebar__link"
|
||||||
|
>
|
||||||
|
Meine Aktivitäten
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-sidebar__item" v-if="me.isTeacher && !me.readOnly" @click="close">
|
<div
|
||||||
<router-link :to="myTeamPage" data-cy="my-team-link" class="profile-sidebar__link"> Mein Team </router-link>
|
class="profile-sidebar__item"
|
||||||
|
v-if="me.isTeacher && !me.readOnly"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="myTeamPage"
|
||||||
|
data-cy="my-team-link"
|
||||||
|
class="profile-sidebar__link"
|
||||||
|
>
|
||||||
|
Mein Team
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-sidebar__section">
|
<div class="profile-sidebar__section">
|
||||||
<div class="profile-sidebar__item">
|
<div class="profile-sidebar__item">
|
||||||
<class-selection-widget />
|
<class-selection-widget />
|
||||||
<div @click="close">
|
<div @click="close">
|
||||||
<router-link :to="{ name: 'my-class' }" data-cy="class-list-link" class="profile-sidebar__link">
|
<router-link
|
||||||
|
:to="{name: 'my-class'}"
|
||||||
|
data-cy="class-list-link"
|
||||||
|
class="profile-sidebar__link"
|
||||||
|
>
|
||||||
Klassenliste
|
Klassenliste
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-sidebar__section">
|
<div class="profile-sidebar__section">
|
||||||
<div class="profile-sidebar__item" @click="close">
|
<div
|
||||||
<router-link :to="{ name: 'join-class' }" data-cy="join-class-link" class="profile-sidebar__link">
|
class="profile-sidebar__item"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="{name:'join-class'}"
|
||||||
|
data-cy="join-class-link"
|
||||||
|
class="profile-sidebar__link"
|
||||||
|
>
|
||||||
Zugangscode
|
Zugangscode
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -38,112 +76,112 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ProfileWidget from '@/components/profile/ProfileWidget';
|
import ProfileWidget from '@/components/profile/ProfileWidget';
|
||||||
|
|
||||||
import ClassSelectionWidget from '@/components/school-class/ClassSelectionWidget';
|
import ClassSelectionWidget from '@/components/school-class/ClassSelectionWidget';
|
||||||
|
|
||||||
import sidebar from '@/mixins/sidebar';
|
import sidebar from '@/mixins/sidebar';
|
||||||
import me from '@/mixins/me';
|
import me from '@/mixins/me';
|
||||||
import LogoutWidget from '@/components/LogoutWidget';
|
import LogoutWidget from '@/components/LogoutWidget';
|
||||||
import { MY_TEAM } from '@/router/me.names';
|
import {MY_TEAM} from '@/router/me.names';
|
||||||
const Cross = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [sidebar, me],
|
|
||||||
|
|
||||||
components: {
|
mixins: [sidebar, me],
|
||||||
LogoutWidget,
|
|
||||||
ClassSelectionWidget,
|
|
||||||
ProfileWidget,
|
|
||||||
Cross,
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
components: {
|
||||||
myTeamPage() {
|
LogoutWidget,
|
||||||
return {
|
ClassSelectionWidget,
|
||||||
name: MY_TEAM,
|
ProfileWidget,
|
||||||
};
|
Cross,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
computed: {
|
||||||
close() {
|
myTeamPage() {
|
||||||
this.closeSidebar('profile');
|
return {
|
||||||
|
name: MY_TEAM,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
methods: {
|
||||||
|
close() {
|
||||||
|
this.closeSidebar('profile');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
$desktop-width: 333px;
|
$desktop-width: 333px;
|
||||||
|
|
||||||
.profile-sidebar {
|
.profile-sidebar {
|
||||||
padding: $large-spacing 0;
|
padding: $large-spacing 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
z-index: 15;
|
z-index: 15;
|
||||||
box-shadow: 0 3px 9px 0 rgba(0, 0, 0, 0.12);
|
box-shadow: 0 3px 9px 0 rgba(0, 0, 0, 0.12);
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@include desktop {
|
|
||||||
width: $desktop-width;
|
|
||||||
}
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
&__section {
|
|
||||||
margin-bottom: $large-spacing;
|
|
||||||
|
|
||||||
&:last-of-type {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__item {
|
|
||||||
padding: $small-spacing $medium-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__subtitle {
|
|
||||||
@include small-text;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: $small-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
@include default-link;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__close-link {
|
|
||||||
position: absolute;
|
|
||||||
right: $small-spacing;
|
|
||||||
top: $small-spacing;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide {
|
|
||||||
&-enter-active,
|
|
||||||
&-leave-active {
|
|
||||||
transition: right 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter,
|
|
||||||
&-leave-to {
|
|
||||||
right: -100vw;
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
right: -$desktop-width;
|
width: $desktop-width;
|
||||||
|
}
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
&__section {
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
padding: $small-spacing $medium-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
@include small-text;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
@include default-link;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__close-link {
|
||||||
|
position: absolute;
|
||||||
|
right: $small-spacing;
|
||||||
|
top: $small-spacing;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide {
|
||||||
|
&-enter-active, &-leave-active {
|
||||||
|
transition: right 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-from, &-leave-to {
|
||||||
|
right: -100vw;
|
||||||
|
@include desktop {
|
||||||
|
right: -$desktop-width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,65 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link class="add-room-entry-button" data-cy="add-room-entry-button" :to="addRoomEntryRoute">
|
<router-link
|
||||||
|
class="add-room-entry-button"
|
||||||
|
data-cy="add-room-entry-button"
|
||||||
|
:to="addRoomEntryRoute"
|
||||||
|
>
|
||||||
<plus-icon class="add-room-entry-button__icon" />
|
<plus-icon class="add-room-entry-button__icon" />
|
||||||
<span class="add-room-entry-button__text">Beitrag erfassen</span>
|
<span class="add-room-entry-button__text">Beitrag erfassen</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ADD_ROOM_ENTRY_PAGE } from '@/router/room.names';
|
import {defineAsyncComponent} from 'vue';
|
||||||
const PlusIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PlusIcon');
|
const PlusIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PlusIcon'));
|
||||||
|
import { ADD_ROOM_ENTRY_PAGE } from '@/router/room.names';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['parent'],
|
props: ['parent'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
addRoomEntryRoute: {
|
addRoomEntryRoute: {
|
||||||
name: ADD_ROOM_ENTRY_PAGE,
|
name: ADD_ROOM_ENTRY_PAGE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.add-room-entry-button {
|
.add-room-entry-button {
|
||||||
border: 2px solid $color-white;
|
border: 2px solid $color-white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
break-inside: avoid-column;
|
break-inside: avoid-column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
@include desktop {
|
@include desktop {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
width: 20px;
|
||||||
|
fill: $color-white;
|
||||||
|
margin-right: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
@include regular-text;
|
||||||
|
color: $color-white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
|
||||||
width: 20px;
|
|
||||||
fill: $color-white;
|
|
||||||
margin-right: $small-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
@include regular-text;
|
|
||||||
color: $color-white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,56 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="entry-count-widget">
|
<div class="entry-count-widget">
|
||||||
<component :is="icon" />
|
<component :is="icon" />
|
||||||
<span data-cy="entry-count"
|
<span data-cy="entry-count">{{ entryCount }} <template v-if="verbose">{{ entryCount === 1 ? 'Beitrag' : 'Beiträge' }}</template></span>
|
||||||
>{{ entryCount }} <template v-if="verbose">{{ entryCount === 1 ? 'Beitrag' : 'Beiträge' }}</template></span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SpeechBubbleIcon from '@/components/icons/SpeechBubbleIcon';
|
import {defineAsyncComponent} from 'vue';
|
||||||
const Cards = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Cards.vue');
|
import SpeechBubbleIcon from '@/components/icons/SpeechBubbleIcon';
|
||||||
|
const Cards = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Cards.vue'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
entryCount: {
|
entryCount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
},
|
||||||
|
verbose: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: 'cards'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
verbose: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
default: 'cards',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
'speech-bubble': SpeechBubbleIcon,
|
'speech-bubble': SpeechBubbleIcon,
|
||||||
SpeechBubbleIcon,
|
SpeechBubbleIcon,
|
||||||
Cards,
|
Cards,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.entry-count-widget {
|
.entry-count-widget {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
margin-right: $medium-spacing;
|
margin-right: $medium-spacing;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
fill: $color-charcoal-dark;
|
fill: $color-charcoal-dark;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
@include room-widget-text-style;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > span {
|
|
||||||
@include room-widget-text-style;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,71 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="more-actions">
|
<div class="more-actions">
|
||||||
<a
|
<a
|
||||||
:class="{ 'more-actions__toggle--background': background }"
|
:class="{'more-actions__toggle--background': background}"
|
||||||
class="more-actions__toggle"
|
class="more-actions__toggle"
|
||||||
data-cy="toggle-more-actions-menu"
|
data-cy="toggle-more-actions-menu"
|
||||||
@click.stop="toggleMenu"
|
@click.stop="toggleMenu"
|
||||||
>
|
>
|
||||||
<ellipses />
|
<ellipses />
|
||||||
</a>
|
</a>
|
||||||
<widget-popover class="more-actions__popover" v-if="showMenu" @hide-me="showMenu = false">
|
<widget-popover
|
||||||
|
class="more-actions__popover"
|
||||||
|
v-if="showMenu"
|
||||||
|
@hide-me="showMenu = false"
|
||||||
|
>
|
||||||
<slot :toggle="toggleMenu" />
|
<slot :toggle="toggleMenu" />
|
||||||
</widget-popover>
|
</widget-popover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import WidgetPopover from '@/components/ui/WidgetPopover';
|
import WidgetPopover from '@/components/ui/WidgetPopover';
|
||||||
const Ellipses = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Ellipses');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const Ellipses = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Ellipses'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
background: {
|
background: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Ellipses,
|
Ellipses,
|
||||||
WidgetPopover,
|
WidgetPopover,
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showMenu: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggleMenu: function () {
|
|
||||||
this.showMenu = !this.showMenu;
|
|
||||||
},
|
},
|
||||||
},
|
data() {
|
||||||
};
|
return {
|
||||||
|
showMenu: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleMenu: function () {
|
||||||
|
this.showMenu = !this.showMenu;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.more-actions {
|
.more-actions {
|
||||||
svg {
|
svg {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
fill: $color-charcoal-dark;
|
fill: $color-charcoal-dark;
|
||||||
//margin-right: 15px;
|
//margin-right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__toggle {
|
&__toggle {
|
||||||
display: flex;
|
display: flex;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
&--background {
|
&--background {
|
||||||
background: white;
|
background: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="room-entry" data-cy="room-entry">
|
<div
|
||||||
<router-link :to="{ name: 'article', params: { slug: slug } }" class="room-entry__router-link" tag="div">
|
class="room-entry"
|
||||||
<div class="room-entry__header" v-if="image">
|
data-cy="room-entry"
|
||||||
<img :src="image" :alt="title" class="room-entry__image" />
|
>
|
||||||
|
<router-link
|
||||||
|
:to="{name: 'article', params: { slug: slug }}"
|
||||||
|
class="room-entry__router-link"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="room-entry__header"
|
||||||
|
v-if="image"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="image"
|
||||||
|
:alt="title"
|
||||||
|
class="room-entry__image"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="room-entry__content">
|
<div class="room-entry__content">
|
||||||
<h2 class="room-entry__title">
|
<h2 class="room-entry__title">
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,107 @@
|
||||||
<template>
|
<template>
|
||||||
<page-form class="room-form" title="Neuer Raum" @save="$emit('save', localRoom)">
|
<page-form
|
||||||
<page-form-input label="Titel" v-model="localRoom.title" />
|
class="room-form"
|
||||||
|
title="Neuer Raum"
|
||||||
|
@save="$emit('save', localRoom)"
|
||||||
|
>
|
||||||
|
<page-form-input
|
||||||
|
label="Titel"
|
||||||
|
v-model="localRoom.title"
|
||||||
|
/>
|
||||||
|
|
||||||
<page-form-input label="Beschreibung" type="textarea" v-model="localRoom.description" />
|
<page-form-input
|
||||||
|
label="Beschreibung"
|
||||||
|
type="textarea"
|
||||||
|
v-model="localRoom.description"
|
||||||
|
/>
|
||||||
|
|
||||||
<h2 class="room-form__property-heading">Farbe</h2>
|
<h2 class="room-form__property-heading">
|
||||||
<color-chooser :selected-color="localRoom.appearance" @input="updateColor" />
|
Farbe
|
||||||
|
</h2>
|
||||||
|
<color-chooser
|
||||||
|
:selected-color="localRoom.appearance"
|
||||||
|
@input="updateColor"
|
||||||
|
/>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<button type="submit" data-cy="room-form-save" class="button button--primary room-form__save-button">
|
<button
|
||||||
|
type="submit"
|
||||||
|
data-cy="room-form-save"
|
||||||
|
class="button button--primary room-form__save-button"
|
||||||
|
>
|
||||||
Speichern
|
Speichern
|
||||||
</button>
|
</button>
|
||||||
<router-link to="/rooms" tag="button" class="button"> Abbrechen </router-link>
|
<router-link
|
||||||
|
to="/rooms"
|
||||||
|
class="button"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
</page-form>
|
</page-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ColorChooser from '@/components/ColorChooser';
|
import ColorChooser from '@/components/ColorChooser';
|
||||||
import PageForm from '@/components/page-form/PageForm';
|
import PageForm from '@/components/page-form/PageForm';
|
||||||
import PageFormInput from '@/components/page-form/PageFormInput';
|
import PageFormInput from '@/components/page-form/PageFormInput';
|
||||||
|
|
||||||
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['room'],
|
props: ['room'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ColorChooser,
|
ColorChooser,
|
||||||
PageForm,
|
PageForm,
|
||||||
PageFormInput,
|
PageFormInput
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
localRoom: Object.assign({}, this.room),
|
|
||||||
me: {},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.$store.dispatch('setSpecialContainerClass', this.localRoom.appearance);
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.$store.dispatch('setSpecialContainerClass', '');
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updateColor(newColor) {
|
|
||||||
this.localRoom.appearance = newColor;
|
|
||||||
this.$store.dispatch('setSpecialContainerClass', newColor);
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
apollo: {
|
data() {
|
||||||
me: {
|
return {
|
||||||
query: ME_QUERY,
|
localRoom: Object.assign({}, this.room),
|
||||||
|
me: {}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
created() {
|
||||||
|
this.$store.dispatch('setSpecialContainerClass', this.localRoom.appearance);
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeUnmount() {
|
||||||
|
this.$store.dispatch('setSpecialContainerClass', '');
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateColor(newColor) {
|
||||||
|
this.localRoom.appearance = newColor;
|
||||||
|
this.$store.dispatch('setSpecialContainerClass', newColor);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
apollo: {
|
||||||
|
me: {
|
||||||
|
query: ME_QUERY,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.room-form {
|
.room-form {
|
||||||
&__property-heading {
|
&__property-heading {
|
||||||
@include page-form-input-heading;
|
@include page-form-input-heading;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__input,
|
&__input,
|
||||||
&__textarea {
|
&__textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 35px;
|
margin-bottom: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__save-button {
|
&__save-button {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -8,33 +8,34 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const Group = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Group.vue');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const Group = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Group.vue'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['name'],
|
props: ['name'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Group,
|
Group
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.room-group-widget {
|
.room-group-widget {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
fill: $color-charcoal-dark;
|
fill: $color-charcoal-dark;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
@include room-widget-text-style;;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > span {
|
|
||||||
@include room-widget-text-style;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -12,41 +12,43 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const EyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon');
|
|
||||||
const ClosedEyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ClosedEyeIcon');
|
|
||||||
|
|
||||||
export default {
|
import {defineAsyncComponent} from 'vue';
|
||||||
props: {
|
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EyeIcon'));
|
||||||
restricted: {
|
const ClosedEyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ClosedEyeIcon'));
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
export default {
|
||||||
|
props: {
|
||||||
|
restricted: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ClosedEyeIcon,
|
ClosedEyeIcon,
|
||||||
EyeIcon,
|
EyeIcon
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.room-visibility-widget {
|
.room-visibility-widget {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
fill: $color-charcoal-dark;
|
fill: $color-charcoal-dark;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
flex-shrink: 0;
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
@include room-widget-text-style;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > span {
|
|
||||||
@include room-widget-text-style;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="roomClass" class="room-widget">
|
<div
|
||||||
<router-link :to="{ name: 'room', params: { slug: slug } }" tag="div" class="room-widget__content">
|
:class="roomClass"
|
||||||
|
class="room-widget"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="{name: 'room', params: {slug: slug}}"
|
||||||
|
class="room-widget__content"
|
||||||
|
>
|
||||||
<h2 class="room-widget__title">
|
<h2 class="room-widget__title">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rooms-onboarding">
|
<div class="rooms-onboarding">
|
||||||
<h1 class="rooms-onboarding__heading" data-cy="page-title">Räume</h1>
|
<h1
|
||||||
|
class="rooms-onboarding__heading"
|
||||||
|
data-cy="page-title"
|
||||||
|
>
|
||||||
|
Räume
|
||||||
|
</h1>
|
||||||
<rooms-illustration class="rooms-onboarding__illustration" />
|
<rooms-illustration class="rooms-onboarding__illustration" />
|
||||||
<p data-cy="rooms-onboarding-text" class="rooms-onboarding__text">
|
<p
|
||||||
|
data-cy="rooms-onboarding-text"
|
||||||
|
class="rooms-onboarding__text"
|
||||||
|
>
|
||||||
Hier können Sie Räume erstellen, damit SchülerInnen zusammenarbeiten und Beiträge teilen können.
|
Hier können Sie Räume erstellen, damit SchülerInnen zusammenarbeiten und Beiträge teilen können.
|
||||||
</p>
|
</p>
|
||||||
<div class="rooms-onboarding__button">
|
<div class="rooms-onboarding__button">
|
||||||
<router-link :to="newRoomRoute" class="button button--primary" data-cy="create-room-button" v-if="isTeacher">
|
<router-link
|
||||||
|
:to="newRoomRoute"
|
||||||
|
class="button button--primary"
|
||||||
|
data-cy="create-room-button"
|
||||||
|
v-if="isTeacher"
|
||||||
|
>
|
||||||
Raum erstellen
|
Raum erstellen
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -14,43 +27,43 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { NEW_ROOM_PAGE } from '@/router/room.names';
|
import {NEW_ROOM_PAGE} from '@/router/room.names';
|
||||||
const RoomsIllustration = () =>
|
import {defineAsyncComponent} from 'vue';
|
||||||
import(/* webpackChunkName: "illustrations" */ '@/components/illustrations/RoomsIllustration');
|
const RoomsIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/RoomsIllustration'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
isTeacher: {
|
isTeacher: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
components: {RoomsIllustration},
|
||||||
components: { RoomsIllustration },
|
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
newRoomRoute: NEW_ROOM_PAGE,
|
newRoomRoute: NEW_ROOM_PAGE,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.rooms-onboarding {
|
.rooms-onboarding {
|
||||||
@include onboarding-page;
|
@include onboarding-page;
|
||||||
|
|
||||||
&__heading {
|
&__heading {
|
||||||
margin-bottom: $large-spacing;
|
margin-bottom: $large-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__illustration {
|
||||||
|
@include onboarding-illustration;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
@include onboarding-text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__illustration {
|
|
||||||
@include onboarding-illustration;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
@include onboarding-text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="class-selection" v-if="currentClassSelection">
|
<div
|
||||||
|
class="class-selection"
|
||||||
|
v-if="currentClassSelection"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
data-cy="class-selection"
|
data-cy="class-selection"
|
||||||
class="class-selection__selected-class selected-class"
|
class="class-selection__selected-class selected-class"
|
||||||
@click.stop="showPopover = !showPopover"
|
@click.stop="toggle"
|
||||||
>
|
>
|
||||||
<current-class class="selected-class__text" />
|
<current-class
|
||||||
|
class="selected-class__text"
|
||||||
|
/>
|
||||||
<chevron-down class="selected-class__dropdown-icon" />
|
<chevron-down class="selected-class__dropdown-icon" />
|
||||||
</div>
|
</div>
|
||||||
<widget-popover :mobile="mobile" class="class-selection__popover" v-if="showPopover" @hide-me="showPopover = false">
|
<widget-popover
|
||||||
|
:mobile="mobile"
|
||||||
|
class="class-selection__popover"
|
||||||
|
v-if="showPopover"
|
||||||
|
@hide-me="showPopover = false"
|
||||||
|
>
|
||||||
<li
|
<li
|
||||||
:label="schoolClass.name"
|
:label="schoolClass.name"
|
||||||
:item="schoolClass"
|
:item="schoolClass"
|
||||||
|
|
@ -26,105 +36,122 @@
|
||||||
v-if="me.isTeacher && !me.readOnly"
|
v-if="me.isTeacher && !me.readOnly"
|
||||||
@click="closeSidebar"
|
@click="closeSidebar"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: 'create-class' }" tag="span" class="popover-links__link-with-icon">
|
<router-link
|
||||||
|
:to="{name: 'create-class'}"
|
||||||
|
class="popover-links__link-with-icon"
|
||||||
|
>
|
||||||
<add-icon class="popover-links__icon" />
|
<add-icon class="popover-links__icon" />
|
||||||
<span>Klasse erfassen</span>
|
<span>Klasse erfassen</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="popover-links__link popover-links__link--large popover-links__divider" @click="closeSidebar">
|
<li
|
||||||
<router-link :to="{ name: 'old-classes' }" tag="span"> Alte Klassen anzeigen </router-link>
|
class="popover-links__link popover-links__link--large popover-links__divider"
|
||||||
|
@click="closeSidebar"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="{name: 'old-classes'}"
|
||||||
|
>
|
||||||
|
Alte Klassen anzeigen
|
||||||
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</widget-popover>
|
</widget-popover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import WidgetPopover from '@/components/ui/WidgetPopover';
|
import WidgetPopover from '@/components/ui/WidgetPopover';
|
||||||
import CurrentClass from '@/components/school-class/CurrentClass';
|
import CurrentClass from '@/components/school-class/CurrentClass';
|
||||||
|
|
||||||
import updateSelectedClassMixin from '@/mixins/update-selected-class';
|
import updateSelectedClassMixin from '@/mixins/update-selected-class';
|
||||||
import sidebarMixin from '@/mixins/sidebar';
|
import sidebarMixin from '@/mixins/sidebar';
|
||||||
import meMixin from '@/mixins/me';
|
import meMixin from '@/mixins/me';
|
||||||
const ChevronDown = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronDown');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const AddIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddIcon');
|
const ChevronDown = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronDown'));
|
||||||
|
const AddIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
|
||||||
mobile: {
|
props: {
|
||||||
type: Boolean,
|
mobile: {
|
||||||
default: false,
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
mixins: [updateSelectedClassMixin, sidebarMixin, meMixin],
|
mixins: [updateSelectedClassMixin, sidebarMixin, meMixin],
|
||||||
components: {
|
components: {
|
||||||
WidgetPopover,
|
WidgetPopover,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
CurrentClass,
|
CurrentClass,
|
||||||
AddIcon,
|
AddIcon
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showPopover: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
currentClassSelection() {
|
|
||||||
let currentClass = this.me.schoolClasses.find((schoolClass) => {
|
|
||||||
return schoolClass.id === this.me.selectedClass.id;
|
|
||||||
});
|
|
||||||
return currentClass || this.me.schoolClasses[0];
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
data() {
|
||||||
updateSelectedClassAndHidePopover(selectedClass) {
|
return {
|
||||||
this.updateSelectedClass(selectedClass);
|
showPopover: false
|
||||||
this.showPopover = false;
|
};
|
||||||
this.closeSidebar('profile');
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
computed: {
|
||||||
|
currentClassSelection() {
|
||||||
|
let currentClass = this.me.schoolClasses.find(schoolClass => {
|
||||||
|
return schoolClass.id === this.me.selectedClass.id;
|
||||||
|
});
|
||||||
|
return currentClass || this.me.schoolClasses[0];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggle() {
|
||||||
|
this.showPopover = !this.showPopover;
|
||||||
|
},
|
||||||
|
updateSelectedClassAndHidePopover(selectedClass) {
|
||||||
|
this.updateSelectedClass(selectedClass);
|
||||||
|
this.showPopover = false;
|
||||||
|
this.closeSidebar('profile');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import "~styles/helpers";
|
||||||
|
|
||||||
.class-selection {
|
.class-selection {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-bottom: $medium-spacing;
|
margin-bottom: $medium-spacing;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
&__popover {
|
&__popover {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
transform: translateY($small-spacing);
|
transform: translateY($small-spacing);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.selected-class {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: $small-spacing 0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
line-height: $large-spacing;
|
|
||||||
@include heading-4;
|
|
||||||
margin-right: $small-spacing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__dropdown-icon {
|
.selected-class {
|
||||||
width: 20px;
|
width: 100%;
|
||||||
height: 20px;
|
box-sizing: border-box;
|
||||||
fill: $color-charcoal-dark;
|
padding: $small-spacing 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
line-height: $large-spacing;
|
||||||
|
@include heading-4;
|
||||||
|
margin-right: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__dropdown-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: $color-charcoal-dark;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,27 @@
|
||||||
<template>
|
<template>
|
||||||
<span class="current-class" data-cy="current-class-name">{{ currentClassName }}</span>
|
<span
|
||||||
|
class="current-class"
|
||||||
|
data-cy="current-class-name"
|
||||||
|
>{{ currentClassName }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import me from '@/mixins/me';
|
import me from '@/mixins/me';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [me],
|
mixins: [me],
|
||||||
};
|
};
|
||||||
</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";
|
||||||
|
|
||||||
.current-class {
|
.current-class {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
@include regular-text;
|
@include regular-text;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,47 @@
|
||||||
class="base-input-container__input"
|
class="base-input-container__input"
|
||||||
data-cy="base-input-input"
|
data-cy="base-input-input"
|
||||||
@change.prevent="$emit('input', $event.target.checked, item)"
|
@change.prevent="$emit('input', $event.target.checked, item)"
|
||||||
/>
|
>
|
||||||
<span
|
<span
|
||||||
:class="{
|
:class="{'base-input-container__checkbox': type==='checkbox', 'base-input-container__radiobutton': type === 'radiobutton'}"
|
||||||
'base-input-container__checkbox': type === 'checkbox',
|
|
||||||
'base-input-container__radiobutton': type === 'radiobutton',
|
|
||||||
}"
|
|
||||||
class="base-input-container__icon checkbox"
|
class="base-input-container__icon checkbox"
|
||||||
>
|
>
|
||||||
<tick v-if="type === 'checkbox'" />
|
<tick v-if="type === 'checkbox'" />
|
||||||
<circle-icon data-cy="circle-icon" v-if="type === 'radiobutton'" />
|
<circle-icon
|
||||||
|
data-cy="circle-icon"
|
||||||
|
v-if="type === 'radiobutton'"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="base-input-container__label" data-cy="base-input-label" v-if="label">{{ label }}</span>
|
<span
|
||||||
<slot class="base-input-container__label" v-if="!label" />
|
class="base-input-container__label"
|
||||||
|
data-cy="base-input-label"
|
||||||
|
v-if="label"
|
||||||
|
>{{ label }}</span>
|
||||||
|
<slot
|
||||||
|
class="base-input-container__label"
|
||||||
|
v-if="!label"
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const Tick = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Tick');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const CircleIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CircleIcon');
|
const Tick = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Tick'));
|
||||||
|
const CircleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CircleIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
label: String,
|
label: String,
|
||||||
checked: {
|
checked: {
|
||||||
type: Boolean,
|
type: Boolean
|
||||||
|
},
|
||||||
|
item: Object,
|
||||||
|
type: String
|
||||||
},
|
},
|
||||||
item: Object,
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Tick,
|
Tick,
|
||||||
CircleIcon,
|
CircleIcon
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,65 @@
|
||||||
<template>
|
<template>
|
||||||
<li class="popover-links__link">
|
<li
|
||||||
<a class="popover-link" @click="$emit('link-action')">
|
class="popover-links__link"
|
||||||
<component class="popover-link__icon" :is="icon" />
|
>
|
||||||
|
<a
|
||||||
|
class="popover-link"
|
||||||
|
@click="$emit('link-action')"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
class="popover-link__icon"
|
||||||
|
:is="icon"
|
||||||
|
/>
|
||||||
<span class="popover-link__text">{{ text }}</span>
|
<span class="popover-link__text">{{ text }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const EyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon');
|
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EyeIcon'));
|
||||||
const PenIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PenIcon');
|
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
|
||||||
|
const PenIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PenIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
text: {
|
components: {
|
||||||
type: String,
|
EyeIcon,
|
||||||
default: '',
|
TrashIcon,
|
||||||
|
PenIcon,
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
components: {
|
|
||||||
EyeIcon,
|
|
||||||
TrashIcon,
|
|
||||||
PenIcon,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.popover-link {
|
.popover-link {
|
||||||
@include popover-link;
|
@include popover-link;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
fill: $color-charcoal-dark;
|
fill: $color-charcoal-dark;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-basis: auto;
|
flex-basis: auto;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
width: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-basis: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
|
||||||
width: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-basis: auto;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="file-upload">
|
<div class="file-upload">
|
||||||
<template v-if="document">
|
<template v-if="document">
|
||||||
<document-block :value="{ url: document }" show-trash-icon @trash="$emit('change-document-url', '')" />
|
<document-block
|
||||||
|
:value="{url: document}"
|
||||||
|
show-trash-icon
|
||||||
|
@trash="$emit('change-document-url', '')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<simple-file-upload
|
<simple-file-upload
|
||||||
|
|
@ -14,24 +18,26 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const SimpleFileUpload = () => import('@/components/ui/file-upload/SimpleFileUpload.vue');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const DocumentBlock = () => import('@/components/content-blocks/DocumentBlock.vue');
|
const SimpleFileUpload = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUpload'));
|
||||||
|
const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
document: {
|
document: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
|
},
|
||||||
|
withText: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withText: {
|
components: {SimpleFileUpload, DocumentBlock},
|
||||||
type: Boolean,
|
};
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: { SimpleFileUpload, DocumentBlock },
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,72 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="simple-file-upload">
|
<div class="simple-file-upload">
|
||||||
<component :is="button" @click.native="clickUploadCare" />
|
<component
|
||||||
|
:is="button"
|
||||||
|
@click="clickUploadCare"
|
||||||
|
/>
|
||||||
<simple-file-upload-hidden-input @link-change-url="$emit('link-change-url', $event)" />
|
<simple-file-upload-hidden-input @link-change-url="$emit('link-change-url', $event)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const SimpleFileUploadHiddenInput = () => import('@/components/ui/file-upload/SimpleFileUploadHiddenInput.vue');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const SimpleFileUploadIcon = () => import('@/components/ui/file-upload/SimpleFileUploadIcon.vue');
|
const SimpleFileUploadHiddenInput = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUploadHiddenInput'));
|
||||||
const SimpleFileUploadIconAndText = () => import('@/components/ui/file-upload/SimpleFileUploadIconAndText.vue');
|
const SimpleFileUploadIcon = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUploadIcon'));
|
||||||
const DocumentIcon = () => import('@/components/icons/DocumentIcon.vue');
|
const SimpleFileUploadIconAndText = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUploadIconAndText'));
|
||||||
|
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: ''
|
||||||
|
},
|
||||||
|
withText: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withText: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
SimpleFileUploadHiddenInput,
|
SimpleFileUploadHiddenInput,
|
||||||
DocumentIcon,
|
DocumentIcon,
|
||||||
SimpleFileUploadIcon,
|
SimpleFileUploadIcon,
|
||||||
SimpleFileUploadIconAndText,
|
SimpleFileUploadIconAndText
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
button() {
|
|
||||||
return this.withText ? 'simple-file-upload-icon-and-text' : 'simple-file-upload-icon';
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
computed: {
|
||||||
clickUploadCare() {
|
button() {
|
||||||
// workaround for styling the uploadcare widget
|
return this.withText ? 'simple-file-upload-icon-and-text' : 'simple-file-upload-icon';
|
||||||
let button = this.$el.querySelector('.uploadcare--widget__button');
|
}
|
||||||
button.click();
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
methods: {
|
||||||
|
clickUploadCare() {
|
||||||
|
// workaround for styling the uploadcare widget
|
||||||
|
let button = this.$el.querySelector('.uploadcare--widget__button');
|
||||||
|
button.click();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/_helpers';
|
@import "~styles/_helpers";
|
||||||
|
|
||||||
.simple-file-upload {
|
.simple-file-upload {
|
||||||
height: 25px;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
display: inline-block;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
height: 25px;
|
||||||
}
|
overflow: hidden;
|
||||||
}
|
cursor: pointer;
|
||||||
|
|
||||||
:deep(.uploadcare--widget) {
|
&__link {
|
||||||
display: none;
|
display: inline-block;
|
||||||
}
|
overflow: hidden;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .uploadcare--widget {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,21 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { DocumentIcon },
|
components: {DocumentIcon},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
.simple-file-upload-icon {
|
.simple-file-upload-icon {
|
||||||
&__icon {
|
&__icon {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
fill: $color-silver-dark;
|
fill: $color-silver-dark;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
icon="document-icon"
|
icon="document-icon"
|
||||||
text="Dokument hochladen"
|
text="Dokument hochladen"
|
||||||
v-if="!value"
|
v-if="!value"
|
||||||
@click.native="clickUploadCare"
|
@click="clickUploadCare"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<simple-file-upload-hidden-input @link-change-url="$emit('link-change-url', $event)" />
|
<simple-file-upload-hidden-input @link-change-url="$emit('link-change-url', $event)" />
|
||||||
|
|
@ -12,49 +12,50 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const SimpleFileUploadHiddenInput = () => import('@/components/ui/file-upload/SimpleFileUploadHiddenInput.vue');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const ButtonWithIconAndText = () => import('@/components/ui/ButtonWithIconAndText.vue');
|
const SimpleFileUploadHiddenInput = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUploadHiddenInput'));
|
||||||
|
const ButtonWithIconAndText = defineAsyncComponent(() => import('@/components/ui/ButtonWithIconAndText'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['value'],
|
props: ['value'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ButtonWithIconAndText,
|
ButtonWithIconAndText,
|
||||||
SimpleFileUploadHiddenInput,
|
SimpleFileUploadHiddenInput,
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
clickUploadCare() {
|
|
||||||
// workaround for styling the uploadcare widget
|
|
||||||
let button = this.$el.querySelector('.uploadcare--widget__button');
|
|
||||||
button.click();
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
methods: {
|
||||||
|
clickUploadCare() {
|
||||||
|
// workaround for styling the uploadcare widget
|
||||||
|
let button = this.$el.querySelector('.uploadcare--widget__button');
|
||||||
|
button.click();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/_helpers';
|
@import "~styles/_helpers";
|
||||||
|
|
||||||
.simple-file-upload {
|
.simple-file-upload {
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&__icon {
|
|
||||||
width: 25px;
|
|
||||||
fill: $color-silver-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
display: inline-block;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
}
|
overflow: hidden;
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.uploadcare--widget) {
|
&__icon {
|
||||||
display: none;
|
width: 25px;
|
||||||
}
|
fill: $color-silver-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .uploadcare--widget {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
const LinkIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/LinkIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const VideoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/VideoIcon');
|
const LinkIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LinkIcon'));
|
||||||
const ImageIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ImageIcon');
|
const VideoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/VideoIcon'));
|
||||||
const TextIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TextIcon');
|
const ImageIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ImageIcon'));
|
||||||
const SpeechBubbleIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/SpeechBubbleIcon');
|
const TextIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TextIcon'));
|
||||||
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon');
|
const SpeechBubbleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/SpeechBubbleIcon'));
|
||||||
const TitleIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TitleIcon');
|
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
|
||||||
const DocumentWithLinesIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentWithLinesIcon');
|
const TitleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TitleIcon'));
|
||||||
|
const DocumentWithLinesIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentWithLinesIcon'));
|
||||||
const ArrowThinBottom = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowThinBottom');
|
const ArrowThinBottom = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinBottom'));
|
||||||
const ArrowThinDown = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowThinDown');
|
const ArrowThinDown = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinDown'));
|
||||||
const ArrowThinTop = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowThinTop');
|
const ArrowThinTop = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinTop'));
|
||||||
const ArrowThinUp = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowThinUp');
|
const ArrowThinUp = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinUp'));
|
||||||
|
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
|
||||||
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon');
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
for icons with a single word, leave the *-icon name, to prevent conflicts
|
for icons with a single word, leave the *-icon name, to prevent conflicts
|
||||||
|
|
@ -31,5 +30,5 @@ export default {
|
||||||
ArrowThinDown,
|
ArrowThinDown,
|
||||||
ArrowThinTop,
|
ArrowThinTop,
|
||||||
ArrowThinUp,
|
ArrowThinUp,
|
||||||
TrashIcon,
|
TrashIcon
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="skillboxform-input"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
:for="id"
|
||||||
|
class="skillboxform-input__label"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</label>
|
||||||
|
<slot :id="id" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
id: String,
|
||||||
|
label: String
|
||||||
|
}) ;
|
||||||
|
</script>
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
<template>
|
|
||||||
<ValidationProvider v-slot="{ errors }" :name="name" :rules="rules">
|
|
||||||
<div class="skillboxform-input">
|
|
||||||
<label :for="id" class="skillboxform-input__label">{{ label }}</label>
|
|
||||||
<input
|
|
||||||
:value="value"
|
|
||||||
:class="{ 'skillboxform-input__input--error': errors.length }"
|
|
||||||
v-bind="$attrs"
|
|
||||||
class="change-form__email skillbox-input skillboxform-input__input"
|
|
||||||
autocomplete="off"
|
|
||||||
:id="id"
|
|
||||||
@input="$emit('input', $event.target.value)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<small :data-cy="localErrorsCy" class="skillboxform-input__error" v-if="errors.length">{{ errors[0] }}</small>
|
|
||||||
|
|
||||||
<small :data-cy="remoteErrorsCy" class="skillboxform-input__error" v-for="error in remoteErrors" :key="error">{{
|
|
||||||
error
|
|
||||||
}}</small>
|
|
||||||
</div>
|
|
||||||
</ValidationProvider>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { extend, localize, ValidationProvider } from 'vee-validate';
|
|
||||||
import de from 'vee-validate/dist/locale/de.json';
|
|
||||||
import { required } from 'vee-validate/dist/rules';
|
|
||||||
|
|
||||||
extend('required', required);
|
|
||||||
|
|
||||||
localize('de', {
|
|
||||||
...de,
|
|
||||||
names: {
|
|
||||||
password: 'Passwort',
|
|
||||||
email: 'E-Mail',
|
|
||||||
coupon: 'Coupon-Code',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// todo: use this in beta-login, license-activation and PasswordChangeForm
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
remoteErrors: {
|
|
||||||
type: Array,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
type: String,
|
|
||||||
default: 'required',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
ValidationProvider,
|
|
||||||
},
|
|
||||||
inheritAttrs: false,
|
|
||||||
computed: {
|
|
||||||
id() {
|
|
||||||
return this.$attrs.id || this._uid;
|
|
||||||
},
|
|
||||||
remoteErrorsCy() {
|
|
||||||
return `${this.name}-remote-errors`;
|
|
||||||
},
|
|
||||||
localErrorsCy() {
|
|
||||||
return `${this.name}-local-errors`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
@import '~styles/helpers';
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,84 +1,93 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="visibility-action">
|
<div class="visibility-action">
|
||||||
<a class="visibility-action__action-button" v-if="canManageContent" @click="toggleVisibility()">
|
<a
|
||||||
<closed-eye-icon class="visibility-action__action-icon action-icon" v-if="hidden" />
|
class="visibility-action__action-button"
|
||||||
<eye-icon class="visibility-action__action-icon action-icon" v-else />
|
v-if="canManageContent"
|
||||||
|
@click="toggleVisibility()"
|
||||||
|
>
|
||||||
|
<closed-eye-icon
|
||||||
|
class="visibility-action__action-icon action-icon"
|
||||||
|
v-if="hidden"
|
||||||
|
/>
|
||||||
|
<eye-icon
|
||||||
|
class="visibility-action__action-icon action-icon"
|
||||||
|
v-else
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import me from '@/mixins/me';
|
import me from '@/mixins/me';
|
||||||
|
|
||||||
import { TYPES, CONTENT_TYPE } from '@/consts/types';
|
import {TYPES, CONTENT_TYPE} from '@/consts/types';
|
||||||
import { createVisibilityMutation, hidden } from '@/helpers/visibility';
|
import {createVisibilityMutation, hidden} from '@/helpers/visibility';
|
||||||
|
|
||||||
const EyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const ClosedEyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ClosedEyeIcon');
|
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EyeIcon'));
|
||||||
|
const ClosedEyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ClosedEyeIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
block: {
|
block: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({})
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: CONTENT_TYPE,
|
||||||
|
validator: value => {
|
||||||
|
// value must be one of TYPES
|
||||||
|
return TYPES.indexOf(value) !== -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
type: {
|
|
||||||
type: String,
|
mixins: [me],
|
||||||
default: CONTENT_TYPE,
|
|
||||||
validator: (value) => {
|
components: {
|
||||||
// value must be one of TYPES
|
EyeIcon,
|
||||||
return TYPES.indexOf(value) !== -1;
|
ClosedEyeIcon
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
hidden() {
|
||||||
|
return hidden({type: this.type, block: this.block, schoolClass: this.schoolClass});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleVisibility() {
|
||||||
|
const hidden = !this.hidden;
|
||||||
|
const schoolClassId = this.schoolClass.id;
|
||||||
|
|
||||||
|
const visibility = [{
|
||||||
|
schoolClassId,
|
||||||
|
hidden
|
||||||
|
}];
|
||||||
|
|
||||||
|
const {mutation, variables} = createVisibilityMutation(this.type, this.block.id, visibility);
|
||||||
|
|
||||||
|
this.$apollo.mutate({
|
||||||
|
mutation,
|
||||||
|
variables
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
|
|
||||||
mixins: [me],
|
|
||||||
|
|
||||||
components: {
|
|
||||||
EyeIcon,
|
|
||||||
ClosedEyeIcon,
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
hidden() {
|
|
||||||
return hidden({ type: this.type, block: this.block, schoolClass: this.schoolClass });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggleVisibility() {
|
|
||||||
const hidden = !this.hidden;
|
|
||||||
const schoolClassId = this.schoolClass.id;
|
|
||||||
|
|
||||||
const visibility = [
|
|
||||||
{
|
|
||||||
schoolClassId,
|
|
||||||
hidden,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const { mutation, variables } = createVisibilityMutation(this.type, this.block.id, visibility);
|
|
||||||
|
|
||||||
this.$apollo.mutate({
|
|
||||||
mutation,
|
|
||||||
variables,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.visibility-action {
|
.visibility-action {
|
||||||
margin-top: 9px;
|
margin-top: 9px;
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -70px;
|
left: -70px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
&__visibility-menu {
|
&__visibility-menu {
|
||||||
top: 40px;
|
top: 40px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
const resizeElement = (el) => {
|
const resizeElement = (el: HTMLElement) => {
|
||||||
el.style.height = `auto`;
|
el.style.height = `auto`;
|
||||||
el.style.height = `${el.clientHeight - el.offsetHeight + el.scrollHeight}px`;
|
el.style.height = `${el.clientHeight - el.offsetHeight + el.scrollHeight}px`;
|
||||||
};
|
};
|
||||||
|
|
@ -6,13 +6,13 @@ const resizeElement = (el) => {
|
||||||
export default {
|
export default {
|
||||||
update: resizeElement,
|
update: resizeElement,
|
||||||
inserted: resizeElement,
|
inserted: resizeElement,
|
||||||
bind(el) {
|
created(el: HTMLElement) {
|
||||||
el.classList.add('skillbox-auto-grow');
|
el.classList.add('skillbox-auto-grow');
|
||||||
el.addEventListener('input', () => {
|
el.addEventListener('input', () => {
|
||||||
resizeElement(el);
|
resizeElement(el);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
unbind(el) {
|
unmounted(el: HTMLElement) {
|
||||||
el.classList.remove('skillbox-auto-grow');
|
el.classList.remove('skillbox-auto-grow');
|
||||||
el.removeEventListener('input', () => {
|
el.removeEventListener('input', () => {
|
||||||
resizeElement(el);
|
resizeElement(el);
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
// taken from https://stackoverflow.com/questions/36170425/detect-click-outside-element
|
|
||||||
export default {
|
|
||||||
bind(el, binding, vnode) {
|
|
||||||
el.clickOutsideEvent = (event) => {
|
|
||||||
if (!(el === event.target || el.contains(event.target))) {
|
|
||||||
vnode.context[binding.expression](event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
document.body.addEventListener('click', el.clickOutsideEvent);
|
|
||||||
},
|
|
||||||
unbind(el) {
|
|
||||||
document.body.removeEventListener('click', el.clickOutsideEvent);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
// taken from https://stackoverflow.com/questions/36170425/detect-click-outside-element
|
||||||
|
import {DirectiveBinding, VNode} from "vue";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElement {
|
||||||
|
clickOutsideEvent: (event: Event) => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
todo:
|
||||||
|
there is a special interaction with nested elements where the parent has a @click event:
|
||||||
|
the parent triggers the event, something happens, but the click event bubbles to the child element.
|
||||||
|
If the event is then used to open some kind of sidebar or modal that has the `click-outside` propert, t
|
||||||
|
he bubbled event will be outside of it, thereby closing it.
|
||||||
|
|
||||||
|
example:
|
||||||
|
<div
|
||||||
|
class="sidebar"
|
||||||
|
v-if="showSidebar"
|
||||||
|
v-click-outside="showSidebar=false"
|
||||||
|
>
|
||||||
|
...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a class="sidebar-toggle" @click="showSidebar=true">
|
||||||
|
<span>Hello</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
FIX:
|
||||||
|
In this example, setting the event on the a-tag as `@click.stop` will solve the problem
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
unmounted(el: HTMLElement) {
|
||||||
|
document.body.removeEventListener('click', el.clickOutsideEvent);
|
||||||
|
},
|
||||||
|
created: (el: HTMLElement, binding: DirectiveBinding) => {
|
||||||
|
el.clickOutsideEvent = (event: Event) => {
|
||||||
|
if (!(el === event.target || el.contains(event.target as Node))) {
|
||||||
|
const eventHandler = binding.value;
|
||||||
|
eventHandler(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.body.addEventListener('click', el.clickOutsideEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import log from 'loglevel';
|
// import log from 'loglevel';
|
||||||
import type { FlavorValues } from '@/helpers/types';
|
import type { FlavorValues } from '@/helpers/types';
|
||||||
import { defaultFlavorValues, dhaValues, dhfValues, myKvValues } from '@/helpers/app-flavor.constants';
|
import { defaultFlavorValues, dhaValues, dhfValues, myKvValues } from '@/helpers/app-flavor.constants';
|
||||||
|
|
||||||
|
|
@ -18,6 +18,6 @@ switch (process.env.VUE_APP_FLAVOR) {
|
||||||
flavorValues = defaultFlavorValues;
|
flavorValues = defaultFlavorValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug('flavorValues', flavorValues);
|
// log.debug('flavorValues', flavorValues);
|
||||||
|
|
||||||
export default flavorValues;
|
export default flavorValues;
|
||||||
|
|
|
||||||
|
|
@ -1,131 +1,160 @@
|
||||||
<template>
|
<template>
|
||||||
<footer class="default-footer" data-cy="page-footer">
|
<footer
|
||||||
|
class="default-footer"
|
||||||
|
data-cy="page-footer"
|
||||||
|
>
|
||||||
<div class="default-footer__section">
|
<div class="default-footer__section">
|
||||||
<div class="default-footer__info">
|
<div class="default-footer__info">
|
||||||
<div class="default-footer__who-are-we who-are-we">
|
<div class="default-footer__who-are-we who-are-we">
|
||||||
<h5 class="who-are-we__title">Wer sind wir?</h5>
|
<h5 class="who-are-we__title">
|
||||||
|
Wer sind wir?
|
||||||
|
</h5>
|
||||||
<p class="who-are-we__text">
|
<p class="who-are-we__text">
|
||||||
mySkillbox ist ein Angebot des hep Verlags in Zusammenarbeit mit der Eidgenössischen Hochschule für
|
mySkillbox ist ein Angebot des hep Verlags in
|
||||||
Berufsbildung (EHB).
|
Zusammenarbeit mit der Eidgenössischen Hochschule für Berufsbildung (EHB).
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="https://www.hep-verlag.ch/" target="_blank">
|
<a
|
||||||
|
href="https://www.hep-verlag.ch/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
<hep-logo class="default-footer__logo-hep" />
|
<hep-logo class="default-footer__logo-hep" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.ehb.swiss/" target="_blank">
|
<a
|
||||||
|
href="https://www.ehb.swiss/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
<ehb-logo class="default-footer__logo-ehb" />
|
<ehb-logo class="default-footer__logo-ehb" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="default-footer__section">
|
<div class="default-footer__section">
|
||||||
<div class="default-footer__links">
|
<div class="default-footer__links">
|
||||||
<a href="https://myskillbox.ch/datenschutz" target="_blank" class="default-footer__link">Datenschutz</a>
|
<a
|
||||||
<a href="https://myskillbox.ch/impressum" target="_blank" class="default-footer__link">Impressum</a>
|
href="https://myskillbox.ch/datenschutz"
|
||||||
<a href="https://myskillbox.ch/agb" target="_blank" class="default-footer__link">AGB</a>
|
target="_blank"
|
||||||
<a :href="$flavor.supportLink" target="_blank" class="default-footer__link">Support</a>
|
class="default-footer__link"
|
||||||
|
>Datenschutz</a>
|
||||||
|
<a
|
||||||
|
href="https://myskillbox.ch/impressum"
|
||||||
|
target="_blank"
|
||||||
|
class="default-footer__link"
|
||||||
|
>Impressum</a>
|
||||||
|
<a
|
||||||
|
href="https://myskillbox.ch/agb"
|
||||||
|
target="_blank"
|
||||||
|
class="default-footer__link"
|
||||||
|
>AGB</a>
|
||||||
|
<a
|
||||||
|
:href="$flavor.supportLink"
|
||||||
|
target="_blank"
|
||||||
|
class="default-footer__link"
|
||||||
|
>Support</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const HepLogo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/HepLogo');
|
import {defineAsyncComponent} from 'vue';
|
||||||
const EhbLogo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EhbLogo');
|
|
||||||
|
|
||||||
export default {
|
const HepLogo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/HepLogo'));
|
||||||
components: {
|
const EhbLogo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EhbLogo'));
|
||||||
HepLogo,
|
|
||||||
EhbLogo,
|
export default {
|
||||||
},
|
components: {
|
||||||
};
|
HepLogo,
|
||||||
|
EhbLogo
|
||||||
|
}
|
||||||
|
};
|
||||||
</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";
|
||||||
|
|
||||||
.default-footer {
|
.default-footer {
|
||||||
background-color: $color-silver-light;
|
background-color: $color-silver-light;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&__section {
|
&__section {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-bottom: $color-silver 1px solid;
|
border-bottom: $color-silver 1px solid;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__info {
|
&__info {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: $footer-width;
|
max-width: $footer-width;
|
||||||
padding: 2 * $large-spacing 0;
|
padding: 2*$large-spacing 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__who-are-we {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
width: 330px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__logo-hep {
|
||||||
|
width: auto;
|
||||||
|
height: 35px;
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
width: 147px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__logo-ehb {
|
||||||
|
width: 100px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__links {
|
||||||
|
width: 100%;
|
||||||
|
max-width: $footer-width;
|
||||||
|
padding: $large-spacing 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
@include aside-with-cheese;
|
||||||
|
margin-right: $large-spacing;
|
||||||
|
margin-bottom: $small-spacing;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__who-are-we {
|
.who-are-we {
|
||||||
width: 100%;
|
&__title {
|
||||||
margin-bottom: $large-spacing;
|
@include heading-4;
|
||||||
|
}
|
||||||
|
|
||||||
@include desktop {
|
&__text {
|
||||||
width: 330px;
|
@include aside-text;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__logo-hep {
|
|
||||||
width: auto;
|
|
||||||
height: 35px;
|
|
||||||
margin-bottom: $large-spacing;
|
|
||||||
|
|
||||||
@include desktop {
|
|
||||||
width: 147px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__logo-ehb {
|
|
||||||
width: 100px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__links {
|
|
||||||
width: 100%;
|
|
||||||
max-width: $footer-width;
|
|
||||||
padding: $large-spacing 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
@include desktop {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link {
|
|
||||||
@include aside-with-cheese;
|
|
||||||
margin-right: $large-spacing;
|
|
||||||
margin-bottom: $small-spacing;
|
|
||||||
|
|
||||||
@include desktop {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.who-are-we {
|
|
||||||
&__title {
|
|
||||||
@include heading-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
@include aside-text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="specialContainerClass" class="container layout layout--fullscreen">
|
<div
|
||||||
<div class="close-button" @click="back">
|
:class="specialContainerClass"
|
||||||
|
class="container layout layout--fullscreen"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="close-button"
|
||||||
|
@click="back"
|
||||||
|
>
|
||||||
<cross class="close-button__icon" />
|
<cross class="close-button__icon" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -9,37 +15,39 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const Cross = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon');
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Cross,
|
Cross
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
specialContainerClass() {
|
specialContainerClass() {
|
||||||
let cls = this.$store.state.specialContainerClass;
|
let cls = this.$store.state.specialContainerClass;
|
||||||
return [cls ? `skillbox--${cls}` : ''];
|
return [cls ? `skillbox--${cls}` : ''];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
methods: {
|
||||||
methods: {
|
back() {
|
||||||
back() {
|
this.$router.go(-1);
|
||||||
this.$router.go(-1);
|
}
|
||||||
},
|
}
|
||||||
},
|
};
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '@/styles/_default-layout.scss';
|
@import "@/styles/_default-layout.scss";
|
||||||
|
|
||||||
.close-button {
|
.close-button {
|
||||||
margin-top: $medium-spacing;
|
margin-top: $medium-spacing;
|
||||||
margin-right: $medium-spacing;
|
margin-right: $medium-spacing;
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
display:flex;
|
||||||
|
justify-content:flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue