UNPKG

@digital-blueprint/nexus-app

Version:

[GitHub Repository](https://github.com/digital-blueprint/nexus-app) | [npmjs package](https://www.npmjs.com/package/@digital-blueprint/nexus-app) | [Unpkg CDN](https://unpkg.com/browse/@digital-blueprint/nexus-app/) |

946 lines (827 loc) 51.9 kB
import {AppShell} from '@dbp-toolkit/app-shell'; import {css, html, unsafeCSS} from 'lit'; import {classMap} from 'lit/directives/class-map.js'; import * as commonStyles from '@dbp-toolkit/common/styles'; import {getIconSVGURL} from './utils.js'; import {send} from '@dbp-toolkit/common/notification'; const TYPESENSE_COLLECTION = 'nexus--current'; export class NexusAppShell extends AppShell { constructor() { super(); this.boundOpenActivityHandler = this.openActivity.bind(this); this.boundActivityFavorized = this.handleActivityFavorized.bind(this); this.typesenseActivities = []; this.favoriteActivities = []; } static get properties() { return { ...super.properties, favoriteActivities: {type: Object, attribute: false} }; } connectedCallback() { super.connectedCallback(); document.addEventListener('click', this.boundOpenActivityHandler); document.addEventListener('dbp-favorized', this.boundActivityFavorized); this.favoriteActivities = JSON.parse(localStorage.getItem('nexus-favorite-activities')) || []; } disconnectedCallback() { document.removeEventListener('click', this.boundOpenActivityHandler); document.removeEventListener('dbp-favorized', this.boundActivityFavorized); } async waitForAuth() { return new Promise((resolve) => { const checkAuth = () => { if (this.auth && this.auth.token) { resolve(); } else { setTimeout(checkAuth, 100); // Retry after 100ms } }; checkAuth(); }); } /** * Fetches the metadata of the components we want to use in the menu, dynamically imports the js modules for them, * then triggers a rebuilding of the menu and resolves the current route * @param {string} topicURL The topic metadata URL or relative path to load things from */ async fetchMetadata(topicURL) { // Wait for this.auth to be populated await this.waitForAuth(); let metadata = {}; let routes = []; const result = await ( await fetch(topicURL, { headers: {'Content-Type': 'application/json'}, }) ).json(); this.topic = result; // Get other activities from typesense // console.log('this.auth.token', this.auth.token); try { let typesenseUrl = new URL(this.entryPointUrl + "/nexus/typesense"); const typesenseActivities = await fetch(`${typesenseUrl}/multi_search`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.auth.token }, body: JSON.stringify({ "searches": [ { "query_by": "activityName", "collection": TYPESENSE_COLLECTION, "q": "*", "page": 1, "per_page": 250 } ] }) }); const typesenseResult = await typesenseActivities.json(); console.log('typesenseResult.results', typesenseResult.results); if (Array.isArray(typesenseResult.results) && typesenseResult.results.length > 0) { const hits = typesenseResult.results.pop().hits; if (Array.isArray(hits) && hits.length > 0) { // Merge Typesense activities into result.activities hits.forEach(hit => { const activity = { name: hit.document.activityName, path: hit.document.activityPath, }; result.activities.push(activity); }); } } } catch (error) { console.error('Error fetching Typesense activities:', error); send({ summary: 'Error!', body: 'Error fetching Typesense activities', type: 'danger', timeout: 5, }); } const fetchOne = async (url) => { const result = await fetch(url, { headers: {'Content-Type': 'application/json'}, }); if (!result.ok) throw result; const jsondata = await result.json(); if (jsondata['element'] === undefined) throw new Error('no element defined in metadata'); return jsondata; }; let promises = []; for (const activity of result.activities) { const actURL = new URL(activity.path, new URL(topicURL, window.location.href).href).href; promises.push([ activity.visible === undefined || activity.visible, actURL, fetchOne(actURL), ]); } for (const [visible, actURL, p] of promises) { try { const activity = await p; activity.visible = visible; // Resolve module_src relative to the location of the json file activity.module_src = new URL(activity.module_src, actURL).href; activity.required_roles = activity.required_roles || []; metadata[activity.routing_name] = activity; routes.push(activity.routing_name); } catch (error) { console.log(error); } } // this also triggers a rebuilding of the menu this.metadata = metadata; this.routes = routes; // Switch to the first route if none is selected if (!this.activeView) this.switchComponent(routes[0]); else this.switchComponent(this.activeView); } _renderActivity() { const act = this.metadata[this.activeView]; if (act === undefined) return html``; const elm = this._createActivityElement(act); // add subscriptions for the provider component if (act.subscribe !== undefined) { elm.setAttribute('subscribe', act.subscribe); } // add any additional attributes defined in the metadata if (act.attributes !== undefined) { for (const [key] of Object.entries(act.attributes)) { if (key === 'favorite-activities') { elm.setAttribute(key, JSON.stringify(this.favoriteActivities)); } } } return elm; } openActivity(e) { // @TODO change to custom event console.log('openActivity', e); const link = e.composedPath()[0]; const href = link.getAttribute('data-nav'); if (href) { let partialState = { component: href, }; let location = this.router.getPathname(partialState); this.router.updateFromUrl(location); } } handleActivityFavorized(e) { console.log('handleActivityFavorized', e); const activityName = e.detail.activityName; const icon = e.detail.icon; const activityRoute = e.detail.activityRoute; // Toggle icon and favorite status if (icon.name === 'star-empty') { const activityExists = this.favoriteActivities.some(item => item.name === activityName); if (!activityExists) { this.favoriteActivities = [...this.favoriteActivities, { name: activityName, route: activityRoute }]; } icon.name="star-filled"; } else { this.favoriteActivities = this.favoriteActivities.filter((item) => item.name !== activityName); icon.name="star-empty"; } // Save this.favoriteActivities to localstorage localStorage.setItem('nexus-favorite-activities', JSON.stringify(this.favoriteActivities)); this.requestUpdate(); } toggleFavorites(e) { console.log('toggle fav event', e); const favoriteContainer = this._('.favorite-activities-container'); favoriteContainer.classList.toggle('closed'); } static get styles() { // language=css return css` ${commonStyles.getThemeCSS()} ${commonStyles.getGeneralCSS()} ${commonStyles.getLinkCss()} .hidden { display: none; } h1.title { margin-bottom: 0; font-weight: 300; } #main { --default-layout-max-width: 1440px; --wide-layout-max-width: 1920px; --sidebar-width: 250px; --page-padding: 20px; display: grid; grid-template-columns: 1fr; grid-template-areas: 'header' 'headline' 'main' 'footer'; grid-template-rows: min-content min-content 1fr min-content; margin: 0 auto; min-height: 100vh; } header, #headline, main, footer { width: calc(100% - 2 * var(--page-padding)); max-width: var(--default-layout-max-width); margin: 0 auto; padding: 0 20px; } header { grid-area: header; display: grid; grid-template-columns: 50% 1px auto; grid-template-rows: 60px 60px; grid-template-areas: 'hd1-left hd1-middle hd1-right' 'hd2-left . hd2-right'; } #headline { grid-area: headline; text-align: center; margin-top: 2em; margin-bottom: 3em; } main { grid-area: main; container: main / inline-size; } footer { grid-area: footer; text-align: right; } #main.wide-layout { max-width: var(--wide-layout-max-width); header, main, footer { max-width: var(--wide-layout-max-width); } .activity-container { max-width: calc(var(--wide-layout-max-width) - var(--sidebar-width)); } } header .hd1-left { display: flex; flex-direction: row; justify-content: flex-end; -webkit-justify-content: flex-end; grid-area: hd1-left; text-align: right; padding-right: 20px; align-items: center; -webkit-align-items: center; gap: 10px; } header .hd1-middle { grid-area: hd1-middle; background-color: var(--dbp-content); background: linear-gradient( 180deg, var(--dbp-content) 0%, var(--dbp-content) 85%, rgba(0, 0, 0, 0) 90% ); } header .hd1-right { grid-area: hd1-right; display: flex; justify-content: flex-start; -webkit-justify-content: flex-start; padding: 0 20px; min-width: 0; align-items: center; -webkit-align-items: center; } header .hd1-right .auth-button { min-width: 0; } header .hd2-left { grid-area: hd2-left; display: flex; flex-direction: column; white-space: nowrap; } header .hd2-left .header { margin-left: 50px; } header .hd2-left a:hover { color: var(--dbp-hover-color, var(--dbp-content)); background-color: var(--dbp-hover-background-color); } header .hd2-right { grid-area: hd2-right; display: flex; flex-direction: column; justify-content: center; text-align: right; } header a { color: var(--dbp-content); display: inline; } footer { display: flex; justify-content: flex-end; flex-wrap: wrap; } footer > *, footer slot > * { margin: 0.5em 0 0 1em; } footer a { border-bottom: var(--dbp-border); padding: 0; } footer a:hover { color: var(--dbp-hover-color, var(--dbp-content)); background-color: var(--dbp-hover-background-color); border-color: var(--dbp-hover-color, var(--dbp-content)); } .description { text-align: left; margin-bottom: 1rem; display: none; } #dbp-notification { z-index: 99999; } @media (max-width: 768px) { #main { grid-template-columns: minmax(0, auto); grid-template-rows: min-content min-content min-content 1fr min-content; grid-template-areas: 'header' 'headline' 'sidebar' 'main' 'footer'; } header { grid-template-rows: 40px; grid-template-areas: 'hd1-left hd1-middle hd1-right'; } header .hd2-left, header .hd2-right { display: none; } } header .hd2-left .header { margin: 0 0 0 1em; } .page-container { display: grid; grid-template-columns: var(--sidebar-width) minmax(0, auto); gap: 2em; } @container main (width < 700px) { .page-container { display: grid; grid-template-columns: minmax(0, auto); gap: 1em; } } .activity-container { width: 100%; max-width: calc(var(--default-layout-max-width) - var(--sidebar-width)); } .favorite-activities-container { grid-area: initial; /* override appshell style */ padding: 0; margin: 0; border: 1px solid #c4c8d8; box-shadow: 0 2px 5px 0px #e3e5ec; position: relative; } .favorite-activities-container.closed .favorite-list { height: 0; overflow: hidden; } .favorite-item { transform: translateY(10px); animation: 0.3s ease forwards fadeIn; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .favorite-header { background-color: var(--dbp-background); position: relative; height: 3em; display: flex; justify-content: space-between; padding: 1em 1em 0 1em; align-items: center; } .favorite-header--home { background-color: #e7e7e7; cursor: pointer; padding: 0; } .favorite-header-home-title { width: 100%; height: 100%; display: flex; align-items: center; padding: 0 .75em; font-size: 1.5em; background: right .75em center no-repeat url("${unsafeCSS( getIconSVGURL('home') )}"); background-size: 1em; } .favorite-header-title { font-size: 1.5em; } .favorite-header-icon { color: var(--dbp-override-primary); font-size: 24px; position: static; } .toggle-favorites { position: absolute; top: 1em; right: 1em; display: none; } @media (width < 768px) { .toggle-favorites { display: block !important; } } .favorite-list { padding: 1em; margin: 0; list-style: none; display: flex; flex-direction: column; gap: .5em; } `; } render() { let i18n = this._i18n; // We hide the app until we are either fully logged in or logged out // At the same time when we hide the main app we show the main slot (e.g. a loading spinner) const appHidden = this._loginStatus === 'unknown' || this._loginStatus === 'logging-in'; const mainClassMap = classMap({hidden: appHidden}); const slotClassMap = classMap({hidden: !appHidden}); if (!appHidden) { // if app is loaded correctly, remove spinner this.updateComplete.then(() => { const slot = this.shadowRoot.querySelector('slot:not([name])'); // remove for safari 12 support. safari 13+ supports display: none on slots. if (slot) slot.remove(); }); } const prodClassMap = classMap({ hidden: this.env === 'production' || this.env === 'demo' || this.env === '', }); this.updatePageTitle(); this.updatePageMetaDescription(); for (let routingName of this.visibleRoutes) { let partialState = { component: routingName, }; // clear the extra state for everything but the current activity if (this.activeView !== routingName) { partialState['extra'] = []; } } const kc = this.keycloakConfig; const wideLayout = this.currentLayout === 'wide'; return html` <slot class="${slotClassMap}"></slot> <dbp-auth-keycloak subscribe="requested-login-status" lang="${this.lang}" entry-point-url="${this.entryPointUrl}" url="${kc.url}" realm="${kc.realm}" client-id="${kc.clientId}" silent-check-sso-redirect-uri="${kc.silentCheckSsoRedirectUri || ''}" scope="${kc.scope || ''}" idp-hint="${kc.idpHint || ''}" ?no-check-login-iframe="${kc.noCheckLoginIframe ?? false}" ?force-login="${kc.forceLogin}" ?try-login="${!kc.forceLogin}"></dbp-auth-keycloak> <dbp-matomo subscribe="auth,analytics-event" endpoint="${this.matomoUrl}" site-id="${this.matomoSiteId}" git-info="${this.gitInfo}"></dbp-matomo> <div class="${mainClassMap}" id="root"> <div id="main" class="${classMap({'wide-layout': wideLayout })}"> <dbp-notification id="dbp-notification" lang="${this.lang}"></dbp-notification> <header> <slot name="header"> <div class="hd1-left"> <dbp-theme-switcher subscribe="themes,dark-mode-theme-override" lang="${this.lang}"></dbp-theme-switcher> <dbp-layout-switcher class="${classMap({"hidden": this.disableLayouts})}" subscribe="default-layout,disabled-layout,app-name" lang="${this.lang}" @layout-changed="${this.handleLayoutChange}"></dbp-layout-switcher> <dbp-language-select id="lang-select" lang="${this.lang}"></dbp-language-select> </div> <div class="hd1-middle"></div> <div class="hd1-right"> <dbp-auth-menu-button data-testid="dbp-auth-menu-button" subscribe="auth" class="auth-button" lang="${this.lang}"></dbp-auth-menu-button> </div> <div class="hd2-left"> <div class="header"> <slot name="name"> DBP <br /> Digital Blueprint </slot> </div> </div> <div class="hd2-right"> <slot name="logo"> <dbp-themed> <div slot ="light" style="width: 80px; height:80px; float:right;"> <svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 400"> <defs> <style> .cls-1 { fill: none; } .cls-2 { clip-path: url(#clippath); } .cls-3 { fill: url(#Unbenannter_Verlauf_24-2); } .cls-4 { fill: #002a60; } .cls-5 { fill: #fff; } .cls-6 { clip-path: url(#clippath-1); } .cls-7 { clip-path: url(#clippath-2); } .cls-8 { opacity: .23; } .cls-9 { opacity: .43; } .cls-10 { fill: url(#Unbenannter_Verlauf_25); } .cls-11 { fill: url(#Unbenannter_Verlauf_23); } .cls-12 { fill: url(#Unbenannter_Verlauf_24); } .cls-13 { fill: url(#Unbenannter_Verlauf_26); } .cls-14 { fill: url(#Unbenannter_Verlauf_29); } .cls-15 { fill: url(#Unbenannter_Verlauf_27); } .cls-16 { fill: url(#Unbenannter_Verlauf_28); } .cls-17 { fill: url(#Unbenannter_Verlauf_22); } .cls-18 { fill: url(#Unbenannter_Verlauf_20); } .cls-19 { fill: url(#Unbenannter_Verlauf_7); } .cls-20 { fill: url(#Unbenannter_Verlauf_21); opacity: .29; } .cls-20, .cls-21, .cls-22, .cls-23 { isolation: isolate; } .cls-21 { fill: url(#Unbenannter_Verlauf_18); opacity: .9; } .cls-22 { fill: url(#Unbenannter_Verlauf_17); opacity: .5; } .cls-23 { fill: url(#Unbenannter_Verlauf_19); opacity: .61; } </style> <clipPath id="clippath"> <rect class="cls-1" x="71.91" y="102.74" width="197.49" height="197.49" transform="translate(-92.48 179.68) rotate(-45)"/> </clipPath> <linearGradient id="Unbenannter_Verlauf_24" data-name="Unbenannter Verlauf 24" x1="113.84" y1="-794.55" x2="113.84" y2="-1126.95" gradientTransform="translate(57.31 1166.62)" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="#c2244e"/> <stop offset=".03" stop-color="#b42855"/> <stop offset=".15" stop-color="#8a346a"/> <stop offset=".25" stop-color="#693d7a"/> <stop offset=".36" stop-color="#524486"/> <stop offset=".45" stop-color="#44488d"/> <stop offset=".54" stop-color="#3f498f"/> <stop offset=".88" stop-color="#2c8ae1"/> <stop offset="1" stop-color="#25a1ff"/> </linearGradient> <linearGradient id="Unbenannter_Verlauf_29" data-name="Unbenannter Verlauf 29" x1="-165.83" y1="-964.7" x2="113.75" y2="-964.7" gradientTransform="translate(57.31 1166.62)" gradientUnits="userSpaceOnUse"> <stop offset=".22" stop-color="#0051b4"/> <stop offset="1" stop-color="#37529c" stop-opacity="0"/> </linearGradient> <radialGradient id="Unbenannter_Verlauf_28" data-name="Unbenannter Verlauf 28" cx="9160.09" cy="-564.48" fx="9160.09" fy="-564.48" r="68.82" gradientTransform="translate(6760.46 19576.59) rotate(-105.23) scale(2.23 2.04) skewX(.89)" gradientUnits="userSpaceOnUse"> <stop offset=".1" stop-color="#002a60"/> <stop offset=".32" stop-color="#042d65" stop-opacity=".76"/> <stop offset=".57" stop-color="#113672" stop-opacity=".48"/> <stop offset=".83" stop-color="#254589" stop-opacity=".19"/> <stop offset="1" stop-color="#37529c" stop-opacity="0"/> </radialGradient> <linearGradient id="Unbenannter_Verlauf_27" data-name="Unbenannter Verlauf 27" x1="1979.02" y1="-1764.25" x2="1979.02" y2="-1917.19" gradientTransform="translate(-1952.58 2473.57) rotate(-9.17) scale(1.25 1)" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="#c2244e"/> <stop offset=".54" stop-color="#703f7c" stop-opacity=".61"/> <stop offset=".54" stop-color="#6e407d" stop-opacity=".6"/> <stop offset=".88" stop-color="#37529c" stop-opacity="0"/> </linearGradient> <linearGradient id="Unbenannter_Verlauf_26" data-name="Unbenannter Verlauf 26" x1="1151.48" y1="791.85" x2="1138.92" y2="932.97" gradientTransform="translate(-1089.46 -1621.51) rotate(4.97) scale(1.22 2.01)" gradientUnits="userSpaceOnUse"> <stop offset=".05" stop-color="#25a1ff"/> <stop offset=".47" stop-color="#108fff" stop-opacity=".74"/> <stop offset="1" stop-color="#0037d3" stop-opacity="0"/> </linearGradient> <linearGradient id="Unbenannter_Verlauf_25" data-name="Unbenannter Verlauf 25" x1="118.66" y1="-796.84" x2="118.66" y2="-986.06" gradientTransform="translate(57.31 1166.62)" gradientUnits="userSpaceOnUse"> <stop offset=".15" stop-color="#c2244e"/> <stop offset=".61" stop-color="#703f7c" stop-opacity=".61"/> <stop offset=".88" stop-color="#37529c" stop-opacity="0"/> </linearGradient> <clipPath id="clippath-1"> <rect class="cls-1" x="102.72" y="102.72" width="197.51" height="197.51" transform="translate(-83.46 201.48) rotate(-45)"/> </clipPath> <linearGradient id="Unbenannter_Verlauf_24-2" data-name="Unbenannter Verlauf 24" x1="144.67" y1="1426.07" x2="144.67" y2="1758.51" gradientTransform="translate(57.31 1798.16) scale(1 -1)" xlink:href="#Unbenannter_Verlauf_24"/> <linearGradient id="Unbenannter_Verlauf_23" data-name="Unbenannter Verlauf 23" x1="217.53" y1="1716.77" x2="137.96" y2="1521.21" gradientTransform="translate(57.31 1798.16) scale(1 -1)" gradientUnits="userSpaceOnUse"> <stop offset=".08" stop-color="#25a1ff"/> <stop offset=".23" stop-color="#1b81d5"/> <stop offset=".4" stop-color="#1162ab"/> <stop offset=".57" stop-color="#0a4a8a"/> <stop offset=".72" stop-color="#043873"/> <stop offset=".87" stop-color="#012e65"/> <stop offset="1" stop-color="#002a60"/> </linearGradient> <radialGradient id="Unbenannter_Verlauf_22" data-name="Unbenannter Verlauf 22" cx="4353.28" cy="-101.73" fx="4353.28" fy="-101.73" r="182.49" gradientTransform="translate(-8977.34 -70.94) scale(2.11 -1.56)" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="#002a60" stop-opacity="0"/> <stop offset=".1" stop-color="#082a5f" stop-opacity=".1"/> <stop offset=".27" stop-color="#1e295d" stop-opacity=".27"/> <stop offset=".47" stop-color="#43285a" stop-opacity=".47"/> <stop offset=".7" stop-color="#752655" stop-opacity=".7"/> <stop offset=".95" stop-color="#b5244f" stop-opacity=".95"/> <stop offset="1" stop-color="#c2244e"/> </radialGradient> <linearGradient id="Unbenannter_Verlauf_21" data-name="Unbenannter Verlauf 21" x1="2393.8" y1="-9473.74" x2="2747.55" y2="-9473.74" gradientTransform="translate(9117.66 -3987.06) rotate(80.2) scale(1 -1)" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="#002a60" stop-opacity="0"/> <stop offset=".1" stop-color="#082a5f" stop-opacity=".1"/> <stop offset=".27" stop-color="#1e295d" stop-opacity=".27"/> <stop offset=".47" stop-color="#43285a" stop-opacity=".47"/> <stop offset=".7" stop-color="#752655" stop-opacity=".7"/> <stop offset=".89" stop-color="#b5244f" stop-opacity=".95"/> <stop offset="1" stop-color="#c2244e"/> </linearGradient> <linearGradient id="Unbenannter_Verlauf_20" data-name="Unbenannter Verlauf 20" x1="1466.29" y1="-43.44" x2="1453.98" y2="-141.33" gradientTransform="translate(-1539.01 -194.46) rotate(4.97) scale(1.22 -2.01)" gradientUnits="userSpaceOnUse"> <stop offset=".18" stop-color="#25a1ff"/> <stop offset=".46" stop-color="#1a7cd5" stop-opacity=".74"/> <stop offset="1" stop-color="#37529c" stop-opacity="0"/> </linearGradient> <linearGradient id="Unbenannter_Verlauf_19" data-name="Unbenannter Verlauf 19" x1="4295.4" y1="1392.88" x2="4304.34" y2="1306.2" gradientTransform="translate(1543.83 5899.22) rotate(-76.71) scale(1.22 -2.01)" gradientUnits="userSpaceOnUse"> <stop offset=".05" stop-color="#25a1ff"/> <stop offset=".28" stop-color="#1a7cd5" stop-opacity=".74"/> <stop offset="1" stop-color="#37529c" stop-opacity="0"/> </linearGradient> <clipPath id="clippath-2"> <rect id="rect62862-7" class="cls-1" x="133.56" y="102.74" width="197.49" height="197.49" transform="translate(-74.43 223.27) rotate(-45)"/> </clipPath> <linearGradient id="Unbenannter_Verlauf_18" data-name="Unbenannter Verlauf 18" x1="-1269.43" y1="584.37" x2="-1071.94" y2="584.37" gradientTransform="translate(1402.98 785.85) scale(1 -1)" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="#073d84"/> <stop offset=".84" stop-color="#003c8b" stop-opacity=".64"/> <stop offset="1" stop-color="#004eb5" stop-opacity=".6"/> </linearGradient> <radialGradient id="Unbenannter_Verlauf_17" data-name="Unbenannter Verlauf 17" cx="322.24" cy="482.42" fx="322.24" fy="482.42" r="98.74" gradientTransform="translate(275.77 2247.32) rotate(-4.61) scale(.69 -4.38)" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="#2295ff" stop-opacity=".65"/> <stop offset="1" stop-color="#2295ff" stop-opacity="0"/> </radialGradient> <radialGradient id="Unbenannter_Verlauf_7" data-name="Unbenannter Verlauf 7" cx="774.18" cy="435.49" fx="774.18" fy="435.49" r="98.74" gradientTransform="translate(-7732.23 487.42) rotate(16.19) scale(9.86 -6.01)" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="#b34d4d" stop-opacity="0"/> <stop offset="1" stop-color="red"/> </radialGradient> </defs> <rect id="white_background_1" data-name="white background 1" class="cls-5" x="71.91" y="102.74" width="197.49" height="197.49" transform="translate(-92.48 179.68) rotate(-45)"/> <rect id="white_background_2" data-name="white background 2" class="cls-5" x="102.72" y="102.72" width="197.51" height="197.51" transform="translate(-83.46 201.48) rotate(-45)"/> <rect id="white_background_3" data-name="white background 3" class="cls-5" x="133.56" y="102.74" width="197.49" height="197.49" transform="translate(-74.43 223.27) rotate(-45)"/> <g class="cls-8"> <g class="cls-2"> <rect id="rect35896-3-4-6-6-2" class="cls-4" x="31.36" y="16.45" width="279.59" height="355.63"/> <rect id="rect35917-90-7-5-4-2" class="cls-12" x="31.36" y="39.67" width="279.59" height="332.41"/> <rect id="rect35924-9-9-9-42-2" class="cls-14" x="-108.52" y="62.13" width="279.59" height="279.59"/> <polygon id="polygon35937-3-9-0-7-2" class="cls-16" points="199.72 410.27 119.89 111.41 397.36 37.26 477.25 336.06 199.72 410.27"/> <polygon id="polygon35948-65-4-8-5-2" class="cls-15" points="11.85 214.45 356.22 158.86 380.63 309.84 36.25 365.42 11.85 214.45"/> <polygon id="polygon35957-1-3-2-4-2" class="cls-13" points="-1.06 12.44 339.71 51.32 321.26 329.76 -19.51 290.79 -1.06 12.44"/> <rect id="rect35968-1-9-3-10-2" class="cls-10" x="-7.66" y="180.56" width="367.25" height="189.22"/> </g> </g> <g class="cls-9"> <g class="cls-6"> <rect id="rect35803-7-5-9-32-2" class="cls-4" x="62.17" y="16.42" width="279.62" height="355.67"/> <rect id="rect35824-0-2-8-75-2" class="cls-3" x="62.17" y="39.65" width="279.62" height="332.44"/> <rect id="rect35826-6-7-8-5-2" class="cls-4" x="-24.02" y="16.66" width="365.51" height="365.51"/> <rect id="rect35843-7-8-8-7-2" class="cls-11" x="8.99" y="-10.46" width="460.61" height="358.44"/> <path id="path35860-8-7-6-4-2" class="cls-17" d="M592.99,445.31H-189.74V-260.66H592.99V445.31Z"/> <polygon id="polygon35877-4-4-8-9-2" class="cls-20" points="370.32 -46.84 430.51 301.7 69.12 364.13 8.93 15.54 370.32 -46.84"/> <polygon id="polygon35886-19-00-4-03-3" class="cls-18" points="57.04 4.45 397.85 34.11 371.79 347.98 32.69 246.22 57.04 4.45"/> <polygon id="polygon35886-19-00-4-03-4" class="cls-23" points="-43.83 280.89 19.72 11.82 233.28 54.62 169.67 323.62 -43.83 280.89"/> </g> </g> <g class="cls-7"> <rect id="rect62862-8" class="cls-21" x="133.56" y="102.74" width="197.49" height="197.49" transform="translate(-74.43 223.27) rotate(-45)"/> <rect id="rect62862" class="cls-22" x="133.56" y="102.74" width="197.49" height="197.49" transform="translate(-74.43 223.27) rotate(-45)"/> <rect id="rect62862-7-2" data-name="rect62862-7" class="cls-19" x="133.56" y="102.74" width="197.49" height="197.49" transform="translate(-74.43 223.27) rotate(-45)"/> </g> </svg> </div> <div slot ="dark" style="width: 80px; height:80px; float:right;"> <svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 402.96 402.96"> <defs> <style> .cls-1 { fill: url(#Unbenannter_Verlauf_10); opacity: .3; } .cls-2 { fill: url(#Unbenannter_Verlauf_10-2); opacity: .5; } .cls-3 { fill: url(#Unbenannter_Verlauf_10-3); opacity: .85; } </style> <linearGradient id="Unbenannter_Verlauf_10" data-name="Unbenannter Verlauf 10" x1="170.82" y1="-1500.86" x2="170.82" y2="-1780.18" gradientTransform="translate(-1109.99 1240.71) rotate(45)" gradientUnits="userSpaceOnUse"> <stop offset=".01" stop-color="#bfbfbf"/> <stop offset=".7" stop-color="#fff"/> </linearGradient> <linearGradient id="Unbenannter_Verlauf_10-2" data-name="Unbenannter Verlauf 10" x1="201.48" y1="-1500.86" x2="201.48" y2="-1780.18" gradientTransform="translate(-1101.02 1219.03) rotate(45)" xlink:href="#Unbenannter_Verlauf_10"/> <linearGradient id="Unbenannter_Verlauf_10-3" data-name="Unbenannter Verlauf 10" x1="232.13" y1="-1500.86" x2="232.13" y2="-1780.18" gradientTransform="translate(-1092.04 1197.36) rotate(45)" xlink:href="#Unbenannter_Verlauf_10"/> </defs> <rect class="cls-1" x="72.06" y="102.72" width="197.51" height="197.51" transform="translate(-92.44 179.8) rotate(-45)"/> <rect class="cls-2" x="102.72" y="102.72" width="197.51" height="197.51" transform="translate(-83.46 201.48) rotate(-45)"/> <rect class="cls-3" x="133.38" y="102.72" width="197.51" height="197.51" transform="translate(-74.48 223.16) rotate(-45)"/> </svg> </div> </dbp-themed> </slot> </div> </slot> </header> <div id="headline"> <h1 class="title"> <slot name="title"> <a data-nav="activity-search">${this.topicMetaDataText('name')}</a></slot> </h1> </div> <main> <div style="display: ${!this.metadata[this.activeView] ? 'block' : 'none'};"> <h2>${i18n.t('page-not-found')}</h2> </div> <p class="description">${this.description}</p> <div class="page-container"> <aside class="favorite-activities-container"> <div class="favorite-header favorite-header--home"> <h3 class="favorite-header-home-title" data-nav="activity-search"> Home </h3> </div> <div class="favorite-header"> <h3 class="favorite-header-title">My Favorites</h3> <dbp-icon class="favorite-header-icon" name="star-filled"></dbp-icon>