Display CMS Optimierungen 29-05-2026
- Mediathek: Video-Vorschaubilder statt Icons (FFmpeg-Thumbnails + Backfill-Command), Kategorie "Sonstiges" - B2in Media-Picker zeigt alle Medientypen, Typ wird automatisch erkannt; Thumbnail-Preview vor allen Medien-URL-Feldern - B2in Marke/Footer: Footer ein/aus, Logo+Claim frei positionierbar (Ecken) mit Constraints, separate Anzeige-Schalter - Angebote-Modul dynamisch: kein Slide-Typ mehr, einheitliches Detail-Layout mit ein-/ausblendbaren Bloecken, Logo/Brand pro Slide, Streichpreis-Option - Player: leere Module stoppen Endlosschleife, dynamische Layout-Anpassung bei verstecktem Footer/Header - Fix: Script-Ladereihenfolge (Livewire vor Flux), entfernte stale public/flux/flux.js, Modal-Crash beim Aktualisieren behoben Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
9262132325
commit
6c6d683b9a
42 changed files with 2267 additions and 13905 deletions
|
|
@ -143,6 +143,24 @@
|
|||
text-transform: uppercase; color: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
/* B2in Brand (positionable logo + claim) */
|
||||
.b2in-brand { position: absolute; z-index: 12; display: flex; align-items: center; max-width: 60%; }
|
||||
.b2in-brand-logo img { height: 3.5vh; display: block; filter: drop-shadow(0 1px 4px rgba(0,0,0,0.45)); }
|
||||
.b2in-brand-claim {
|
||||
font-size: 1.3vh; font-weight: 300; letter-spacing: 0.15em;
|
||||
text-transform: uppercase; color: rgba(255,255,255,0.85);
|
||||
text-shadow: 0 1px 4px rgba(0,0,0,0.55);
|
||||
}
|
||||
.b2in-brand.pos-top-left { top: 2.5vh; left: 3vh; }
|
||||
.b2in-brand.pos-top-right { top: 2.5vh; right: 3vh; }
|
||||
.b2in-brand.pos-bottom-left { bottom: 2.5vh; left: 3vh; }
|
||||
.b2in-brand.pos-bottom-right { bottom: 2.5vh; right: 3vh; }
|
||||
|
||||
/* Legibility scrims behind positioned brand elements */
|
||||
.b2in-scrim { position: absolute; left: 0; right: 0; height: 12vh; z-index: 11; pointer-events: none; }
|
||||
.b2in-scrim-top { top: 0; background: linear-gradient(to bottom, rgba(0,0,0,0.6), transparent); }
|
||||
.b2in-scrim-bottom { bottom: 0; background: linear-gradient(to top, rgba(0,0,0,0.6), transparent); }
|
||||
|
||||
/* B2in Media */
|
||||
.b2in-media {
|
||||
flex: 1; position: relative; overflow: hidden;
|
||||
|
|
@ -171,6 +189,8 @@
|
|||
font-size: 1.8vh; font-weight: 300; color: rgba(255,255,255,0.7);
|
||||
line-height: 1.4;
|
||||
}
|
||||
/* Without footer the text reclaims the footer's space at the bottom */
|
||||
.b2in-layer.no-footer .b2in-text { padding-bottom: 4vh; }
|
||||
|
||||
/* B2in Footer */
|
||||
.b2in-footer {
|
||||
|
|
@ -204,6 +224,9 @@
|
|||
background: linear-gradient(to bottom, rgba(247,248,250,0.9), transparent);
|
||||
}
|
||||
.b2in-layer[data-theme="light"] .b2in-claim { color: rgba(43,63,81,0.6); }
|
||||
.b2in-layer[data-theme="light"] .b2in-brand-claim { color: rgba(43,63,81,0.75); text-shadow: 0 1px 3px rgba(255,255,255,0.5); }
|
||||
.b2in-layer[data-theme="light"] .b2in-scrim-top { background: linear-gradient(to bottom, rgba(247,248,250,0.9), transparent); }
|
||||
.b2in-layer[data-theme="light"] .b2in-scrim-bottom { background: linear-gradient(to top, rgba(247,248,250,0.9), transparent); }
|
||||
.b2in-layer[data-theme="light"] .b2in-text {
|
||||
background: linear-gradient(to top, rgba(247,248,250,0.85) 40%, transparent);
|
||||
}
|
||||
|
|
@ -325,6 +348,10 @@
|
|||
font-size: 24px; color: #737373; text-align: right;
|
||||
line-height: 1.35; font-weight: 400;
|
||||
}
|
||||
.offer-price-note.strike {
|
||||
color: #dc2626; text-decoration: line-through;
|
||||
text-decoration-color: #dc2626; text-decoration-thickness: 3px;
|
||||
}
|
||||
|
||||
/* Bullets */
|
||||
.offer-bullets {
|
||||
|
|
@ -412,6 +439,16 @@
|
|||
.status-error { color: #ef4444; font-weight: 500; }
|
||||
.status-sub { font-size: 1.4vh; opacity: 0.4; margin-top: 1vh; }
|
||||
|
||||
.display-empty {
|
||||
position: absolute; inset: 0; z-index: 10;
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
text-align: center; padding: 6vw;
|
||||
background: #000; color: #fff;
|
||||
}
|
||||
.display-empty__title { font-size: 2.4vh; font-weight: 600; }
|
||||
.display-empty__hint { font-size: 1.6vh; opacity: 0.5; margin-top: 1.2vh; font-weight: 300; }
|
||||
|
||||
.display-overview {
|
||||
position: fixed; inset: 0; z-index: 10000;
|
||||
overflow-y: auto; background: radial-gradient(circle at top, #12364d 0, #05070a 42%, #000 100%);
|
||||
|
|
@ -549,6 +586,7 @@ class DisplayPlayer {
|
|||
this.lastSuccessTime = Date.now();
|
||||
this.isRunning = false;
|
||||
this.activeVersionRenderer = null;
|
||||
this.emptyVersionStreak = 0;
|
||||
|
||||
// DOM
|
||||
this.viewport = document.getElementById('viewport');
|
||||
|
|
@ -810,6 +848,19 @@ class DisplayPlayer {
|
|||
return;
|
||||
}
|
||||
|
||||
// Guard against a delay-free infinite restart loop when no version in the
|
||||
// playlist has any playable content (e.g. a freshly created, empty module).
|
||||
if (!this.versionHasContent(version)) {
|
||||
this.emptyVersionStreak++;
|
||||
if (this.emptyVersionStreak >= this.playlist.length) {
|
||||
this.showEmptyPlaylist();
|
||||
return;
|
||||
}
|
||||
this.advanceVersion();
|
||||
return;
|
||||
}
|
||||
this.emptyVersionStreak = 0;
|
||||
|
||||
console.log(`[Display] Playing version ${this.currentVersionIndex + 1}/${this.playlist.length}: ${version.version_name} (${version.type})`);
|
||||
|
||||
// Clean up previous renderer
|
||||
|
|
@ -850,6 +901,38 @@ class DisplayPlayer {
|
|||
this.playCurrentVersion();
|
||||
}
|
||||
|
||||
versionHasContent(version) {
|
||||
if (!version) return false;
|
||||
switch (version.type) {
|
||||
case 'video-display':
|
||||
return (version.videoPlaylist || []).length > 0;
|
||||
case 'b2in':
|
||||
return (version.items || []).some(item => item.is_active);
|
||||
case 'offers':
|
||||
return (version.slides || []).length > 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
showEmptyPlaylist() {
|
||||
this.isRunning = false;
|
||||
this.emptyVersionStreak = 0;
|
||||
|
||||
if (this.activeVersionRenderer) {
|
||||
this.activeVersionRenderer.destroy();
|
||||
this.activeVersionRenderer = null;
|
||||
}
|
||||
|
||||
this.viewport.innerHTML = `
|
||||
<div class="display-empty">
|
||||
<p class="display-empty__title">Noch keine Inhalte vorhanden</p>
|
||||
<p class="display-empty__hint">Sobald Inhalte angelegt und aktiviert sind, erscheinen sie hier.</p>
|
||||
</div>
|
||||
`;
|
||||
console.log('[Display] Playlist has no playable content – playback stopped.');
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// UI HELPERS
|
||||
// ========================================
|
||||
|
|
@ -1132,7 +1215,7 @@ class B2inRenderer {
|
|||
|
||||
build() {
|
||||
const layer = document.createElement('div');
|
||||
layer.className = 'version-layer b2in-layer active';
|
||||
layer.className = 'version-layer b2in-layer active' + (this.settings.show_footer === false ? ' no-footer' : '');
|
||||
layer.setAttribute('data-theme', this.theme);
|
||||
|
||||
const headerLogoUrl = this.resolveUrl(this.settings.header_logo_url || '../assets/b2in-logo-positive.svg');
|
||||
|
|
@ -1145,11 +1228,57 @@ class B2inRenderer {
|
|||
: '';
|
||||
const qrUrl = normalizeQrUrl(this.settings.qr_url || footerUrl || 'b2in.eu');
|
||||
|
||||
layer.innerHTML = `
|
||||
<header class="b2in-header">
|
||||
// Footer visibility, brand element visibility + corner positioning
|
||||
const showFooter = this.settings.show_footer !== false;
|
||||
const showLogo = this.settings.show_logo !== false;
|
||||
const showClaim = this.settings.show_claim !== false;
|
||||
const validPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
||||
let logoPos = validPositions.includes(this.settings.logo_position) ? this.settings.logo_position : 'top-left';
|
||||
let claimPos = validPositions.includes(this.settings.claim_position) ? this.settings.claim_position : 'top-right';
|
||||
|
||||
// Bottom corners only allowed when the footer is hidden
|
||||
if (showFooter) {
|
||||
if (logoPos.startsWith('bottom')) logoPos = logoPos.replace('bottom-', 'top-');
|
||||
if (claimPos.startsWith('bottom')) claimPos = claimPos.replace('bottom-', 'top-');
|
||||
}
|
||||
// Claim must not share the logo corner
|
||||
if (claimPos === logoPos) {
|
||||
claimPos = (validPositions.filter(p => (showFooter ? p.startsWith('top') : true) && p !== logoPos)[0]) || claimPos;
|
||||
}
|
||||
|
||||
const logoVisible = showLogo;
|
||||
const claimVisible = showClaim && !!headerClaim;
|
||||
|
||||
const hasTop = (logoVisible && logoPos.startsWith('top')) || (claimVisible && claimPos.startsWith('top'));
|
||||
const hasBottom = (logoVisible && logoPos.startsWith('bottom')) || (claimVisible && claimPos.startsWith('bottom'));
|
||||
|
||||
const logoHtml = logoVisible
|
||||
? `<div class="b2in-brand b2in-brand-logo pos-${logoPos}">
|
||||
<img src="${escapeHtml(headerLogoUrl)}" alt="B2in">
|
||||
<span class="b2in-claim">${escapeHtml(headerClaim)}</span>
|
||||
</header>
|
||||
</div>`
|
||||
: '';
|
||||
|
||||
const claimHtml = claimVisible
|
||||
? `<div class="b2in-brand b2in-brand-claim pos-${claimPos}">${escapeHtml(headerClaim)}</div>`
|
||||
: '';
|
||||
|
||||
const footerHtml = showFooter
|
||||
? `<footer class="b2in-footer">
|
||||
<div>
|
||||
<span class="b2in-footer-url">${escapeHtml(footerUrl)}</span>
|
||||
${footerNameHtml}
|
||||
</div>
|
||||
<div class="b2in-footer-qr">
|
||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&margin=5&data=${encodeURIComponent(qrUrl)}" alt="QR">
|
||||
</div>
|
||||
</footer>`
|
||||
: '';
|
||||
|
||||
layer.innerHTML = `
|
||||
${hasTop ? '<div class="b2in-scrim b2in-scrim-top"></div>' : ''}
|
||||
${(hasBottom && !showFooter) ? '<div class="b2in-scrim b2in-scrim-bottom"></div>' : ''}
|
||||
${logoHtml}
|
||||
${claimHtml}
|
||||
<section class="b2in-media">
|
||||
<div class="b2in-media-layer active" id="b2in-layer-a"></div>
|
||||
<div class="b2in-media-layer" id="b2in-layer-b"></div>
|
||||
|
|
@ -1158,15 +1287,7 @@ class B2inRenderer {
|
|||
<div class="b2in-headline" id="b2in-headline"></div>
|
||||
<div class="b2in-subline" id="b2in-subline"></div>
|
||||
</section>
|
||||
<footer class="b2in-footer">
|
||||
<div>
|
||||
<span class="b2in-footer-url">${escapeHtml(footerUrl)}</span>
|
||||
${footerNameHtml}
|
||||
</div>
|
||||
<div class="b2in-footer-qr">
|
||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&margin=5&data=${encodeURIComponent(qrUrl)}" alt="QR">
|
||||
</div>
|
||||
</footer>
|
||||
${footerHtml}
|
||||
<div class="b2in-progress-track">
|
||||
<div class="b2in-progress-fill" id="b2in-progress"></div>
|
||||
</div>
|
||||
|
|
@ -1375,42 +1496,64 @@ class OffersRenderer {
|
|||
}
|
||||
|
||||
buildSlide(slide) {
|
||||
// Single dynamic detail layout: every block is toggleable. Older slides
|
||||
// without explicit show_* flags fall back to "is there content?".
|
||||
const has = v => v !== undefined && v !== null && v !== '';
|
||||
const qrUrl = slide.qr_url || this.settings.footer_url || '';
|
||||
const contactText = slide.contact || this.settings.footer_claim || '';
|
||||
const show = {
|
||||
logo: slide.show_logo ?? true,
|
||||
badge: (slide.show_badge ?? has(slide.badge_text)) && has(slide.badge_text),
|
||||
eyebrow: (slide.show_eyebrow ?? has(slide.eyebrow)) && has(slide.eyebrow),
|
||||
subline: (slide.show_subline ?? has(slide.subline)) && has(slide.subline),
|
||||
bullets: (slide.show_bullets ?? (slide.bullets && slide.bullets.length > 0)) && (slide.bullets && slide.bullets.length > 0),
|
||||
price: (slide.show_price ?? has(slide.price)) && has(slide.price),
|
||||
disclaimer: (slide.show_disclaimer ?? has(slide.disclaimer)) && has(slide.disclaimer),
|
||||
qr: (slide.show_qr ?? has(slide.qr_url)) && has(qrUrl),
|
||||
contact: (slide.show_contact ?? has(slide.contact)) && has(contactText),
|
||||
};
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'offers-slide-container';
|
||||
|
||||
const article = document.createElement('article');
|
||||
article.className = 'offer-slide';
|
||||
|
||||
// --- HEADER ---
|
||||
const header = document.createElement('header');
|
||||
header.className = 'offer-header';
|
||||
// --- HEADER (Logo & Marke) – per slide, toggleable ---
|
||||
if (show.logo) {
|
||||
const header = document.createElement('header');
|
||||
header.className = 'offer-header';
|
||||
|
||||
const brand = document.createElement('div');
|
||||
brand.className = 'offer-brand';
|
||||
const brandLogo = document.createElement('img');
|
||||
brandLogo.src = this.resolveUrl(this.settings.logo_url || '../logo-cabinet-300.png');
|
||||
brandLogo.alt = 'CABINET';
|
||||
brandLogo.className = 'offer-brand-logo';
|
||||
brand.appendChild(brandLogo);
|
||||
const brand = document.createElement('div');
|
||||
brand.className = 'offer-brand';
|
||||
const brandLogo = document.createElement('img');
|
||||
brandLogo.src = this.resolveUrl(slide.logo_url || '../logo-cabinet-300.png');
|
||||
brandLogo.alt = 'Logo';
|
||||
brandLogo.className = 'offer-brand-logo';
|
||||
brand.appendChild(brandLogo);
|
||||
|
||||
if (slide.show_brand_text) {
|
||||
const brandText = document.createElement('span');
|
||||
brandText.className = 'offer-brand-text';
|
||||
brandText.textContent = this.settings.brand_text || 'Bielefeld';
|
||||
brand.appendChild(brandText);
|
||||
if (has(slide.brand_text)) {
|
||||
const brandText = document.createElement('span');
|
||||
brandText.className = 'offer-brand-text';
|
||||
brandText.textContent = slide.brand_text;
|
||||
brand.appendChild(brandText);
|
||||
}
|
||||
|
||||
header.appendChild(brand);
|
||||
|
||||
if (has(slide.brand_tagline)) {
|
||||
const tagline = document.createElement('div');
|
||||
tagline.className = 'offer-tagline';
|
||||
tagline.innerHTML = slide.brand_tagline.replace(/\n/g, '<br>');
|
||||
header.appendChild(tagline);
|
||||
}
|
||||
|
||||
article.appendChild(header);
|
||||
} else {
|
||||
// Without the header row, let the hero stay the flexible middle row.
|
||||
article.style.gridTemplateRows = '1fr auto';
|
||||
}
|
||||
|
||||
header.appendChild(brand);
|
||||
|
||||
if (slide.show_brand_text && slide.brand_tagline) {
|
||||
const tagline = document.createElement('div');
|
||||
tagline.className = 'offer-tagline';
|
||||
tagline.innerHTML = slide.brand_tagline.replace(/\n/g, '<br>');
|
||||
header.appendChild(tagline);
|
||||
}
|
||||
|
||||
article.appendChild(header);
|
||||
|
||||
// --- HERO ---
|
||||
const hero = document.createElement('section');
|
||||
hero.className = 'offer-hero';
|
||||
|
|
@ -1419,7 +1562,7 @@ class OffersRenderer {
|
|||
hero.style.background = `url('${imgUrl}') center/cover no-repeat`;
|
||||
}
|
||||
|
||||
if (slide.badge_text) {
|
||||
if (show.badge) {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'offer-hero-badge large';
|
||||
badge.textContent = slide.badge_text;
|
||||
|
|
@ -1439,7 +1582,7 @@ class OffersRenderer {
|
|||
const infoContent = document.createElement('div');
|
||||
infoContent.className = 'offer-info-content';
|
||||
|
||||
if (slide.eyebrow) {
|
||||
if (show.eyebrow) {
|
||||
const eyebrow = document.createElement('p');
|
||||
eyebrow.className = 'offer-eyebrow';
|
||||
eyebrow.textContent = slide.eyebrow;
|
||||
|
|
@ -1448,22 +1591,19 @@ class OffersRenderer {
|
|||
|
||||
if (slide.title) {
|
||||
const title = document.createElement('h1');
|
||||
const titleSize = (slide.type === 'product-details') ? 'medium' : 'large';
|
||||
title.className = `offer-title ${titleSize}`;
|
||||
title.className = 'offer-title medium';
|
||||
title.innerHTML = slide.title.replace(/\n/g, '<br>');
|
||||
infoContent.appendChild(title);
|
||||
}
|
||||
|
||||
// Subline (product-impulse)
|
||||
if (slide.subline) {
|
||||
if (show.subline) {
|
||||
const subline = document.createElement('p');
|
||||
subline.className = 'offer-subline';
|
||||
subline.textContent = slide.subline;
|
||||
infoContent.appendChild(subline);
|
||||
}
|
||||
|
||||
// Bullets (product-details)
|
||||
if (slide.bullets && slide.bullets.length > 0) {
|
||||
if (show.bullets) {
|
||||
const ul = document.createElement('ul');
|
||||
ul.className = 'offer-bullets';
|
||||
slide.bullets.forEach(text => {
|
||||
|
|
@ -1477,8 +1617,8 @@ class OffersRenderer {
|
|||
|
||||
info.appendChild(infoContent);
|
||||
|
||||
// Price block (product-hero, product-impulse)
|
||||
if (slide.price) {
|
||||
// Price block
|
||||
if (show.price) {
|
||||
const priceBlock = document.createElement('div');
|
||||
priceBlock.className = 'offer-price-block';
|
||||
|
||||
|
|
@ -1492,7 +1632,7 @@ class OffersRenderer {
|
|||
|
||||
if (slide.original_price) {
|
||||
const note = document.createElement('div');
|
||||
note.className = 'offer-price-note';
|
||||
note.className = slide.strike_original_price ? 'offer-price-note strike' : 'offer-price-note';
|
||||
note.textContent = slide.original_price;
|
||||
priceRow.appendChild(note);
|
||||
}
|
||||
|
|
@ -1511,8 +1651,8 @@ class OffersRenderer {
|
|||
info.appendChild(priceBlock);
|
||||
}
|
||||
|
||||
// Disclaimer (intro)
|
||||
if (slide.disclaimer) {
|
||||
// Disclaimer
|
||||
if (show.disclaimer) {
|
||||
const footer = document.createElement('div');
|
||||
footer.className = 'offer-info-footer';
|
||||
const disc = document.createElement('span');
|
||||
|
|
@ -1524,38 +1664,41 @@ class OffersRenderer {
|
|||
|
||||
bottom.appendChild(info);
|
||||
|
||||
// QR Box
|
||||
const qrBox = document.createElement('aside');
|
||||
qrBox.className = 'offer-qr-box';
|
||||
// QR / Contact box – only when at least one of the two is enabled.
|
||||
if (show.qr || show.contact) {
|
||||
const qrBox = document.createElement('aside');
|
||||
qrBox.className = 'offer-qr-box';
|
||||
|
||||
const qrHeader = document.createElement('div');
|
||||
qrHeader.className = 'offer-qr-header';
|
||||
qrHeader.innerHTML = `
|
||||
<p class="offer-qr-title">${this.escapeHtml(slide.qr_title || this.settings.qr_default_title || 'Kontakt')}</p>
|
||||
<p class="offer-qr-subtitle">${this.escapeHtml(this.settings.qr_subtitle || 'QR scannen')}</p>
|
||||
`;
|
||||
qrBox.appendChild(qrHeader);
|
||||
if (show.qr) {
|
||||
const qrHeader = document.createElement('div');
|
||||
qrHeader.className = 'offer-qr-header';
|
||||
qrHeader.innerHTML = `
|
||||
<p class="offer-qr-title">${this.escapeHtml(slide.qr_title || this.settings.qr_default_title || 'Kontakt')}</p>
|
||||
<p class="offer-qr-subtitle">${this.escapeHtml(this.settings.qr_subtitle || 'QR scannen')}</p>
|
||||
`;
|
||||
qrBox.appendChild(qrHeader);
|
||||
|
||||
const qrWrapper = document.createElement('div');
|
||||
qrWrapper.className = 'offer-qr-wrapper';
|
||||
const qrUrl = slide.qr_url || this.settings.footer_url || '';
|
||||
if (qrUrl) {
|
||||
const qrImg = document.createElement('img');
|
||||
qrImg.src = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&color=000000&bgcolor=ffffff&margin=8&data=${encodeURIComponent(normalizeQrUrl(qrUrl))}`;
|
||||
qrImg.alt = 'QR Code';
|
||||
qrWrapper.appendChild(qrImg);
|
||||
}
|
||||
qrBox.appendChild(qrWrapper);
|
||||
const qrWrapper = document.createElement('div');
|
||||
qrWrapper.className = 'offer-qr-wrapper';
|
||||
const qrImg = document.createElement('img');
|
||||
qrImg.src = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&color=000000&bgcolor=ffffff&margin=8&data=${encodeURIComponent(normalizeQrUrl(qrUrl))}`;
|
||||
qrImg.alt = 'QR Code';
|
||||
qrWrapper.appendChild(qrImg);
|
||||
qrBox.appendChild(qrWrapper);
|
||||
}
|
||||
|
||||
const contactText = slide.contact || this.settings.footer_claim || '';
|
||||
if (contactText) {
|
||||
const contact = document.createElement('p');
|
||||
contact.className = 'offer-qr-contact';
|
||||
contact.innerHTML = contactText.replace(/\n/g, '<br>');
|
||||
qrBox.appendChild(contact);
|
||||
if (show.contact) {
|
||||
const contact = document.createElement('p');
|
||||
contact.className = 'offer-qr-contact';
|
||||
contact.innerHTML = contactText.replace(/\n/g, '<br>');
|
||||
qrBox.appendChild(contact);
|
||||
}
|
||||
|
||||
bottom.appendChild(qrBox);
|
||||
} else {
|
||||
bottom.style.gridTemplateColumns = '1fr';
|
||||
}
|
||||
|
||||
bottom.appendChild(qrBox);
|
||||
article.appendChild(bottom);
|
||||
wrapper.appendChild(article);
|
||||
|
||||
|
|
|
|||
13171
public/flux/flux.js
13171
public/flux/flux.js
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue