Restyle individual page: FAB edit button, uncropped profile pics, silhouette placeholders, dark mode tabs
- Override individual-page-images template to request 400x400 contain (no server-side crop) - Move circle crop to CSS on a.gallery wrapper, fixing single-image profiles - Add gender-colored circular silhouette placeholders matching family navigator - Replace edit button with fixed FAB (bottom-right) using homepage pencil SVG - Add page cloak to prevent FOUC from JS DOM manipulations - Dark mode: white profile shadow, black active tab with light blue accent - Remove carousel arrows and "add media" link at template level
This commit is contained in:
+11
-4
@@ -70,12 +70,17 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul
|
|||||||
|
|
||||||
return <<<HTML
|
return <<<HTML
|
||||||
<link rel="icon" href="{$faviconUrl}" type="image/svg+xml">
|
<link rel="icon" href="{$faviconUrl}" type="image/svg+xml">
|
||||||
|
<style>
|
||||||
|
.bocken-cloak .wt-main-wrapper { opacity: 0; }
|
||||||
|
.bocken-ready .wt-main-wrapper { transition: opacity 0.05s ease; opacity: 1; }
|
||||||
|
</style>
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var saved = localStorage.getItem('bocken-theme');
|
var saved = localStorage.getItem('bocken-theme');
|
||||||
if (saved === 'dark' || saved === 'light') {
|
if (saved === 'dark' || saved === 'light') {
|
||||||
document.documentElement.setAttribute('data-theme', saved);
|
document.documentElement.setAttribute('data-theme', saved);
|
||||||
}
|
}
|
||||||
|
document.documentElement.classList.add('bocken-cloak');
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
HTML;
|
HTML;
|
||||||
@@ -358,11 +363,8 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul
|
|||||||
var iconSpan = editBtn.querySelector('.wt-icon-menu');
|
var iconSpan = editBtn.querySelector('.wt-icon-menu');
|
||||||
if (iconSpan) {
|
if (iconSpan) {
|
||||||
editBtn.innerHTML = '';
|
editBtn.innerHTML = '';
|
||||||
iconSpan.innerHTML = '<i data-lucide="pencil"></i>';
|
iconSpan.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M410.3 231l11.3-11.3-33.9-33.9-62.1-62.1L291.7 89.8l-11.3 11.3-22.6 22.6L58.6 322.9c-10.4 10.4-18 23.3-22.2 37.4L1 480.7c-2.5 8.4-.2 17.5 6.1 23.7s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L387.7 253.7 410.3 231zM160 399.4l-9.1 22.7c-4 3.1-8.5 5.4-13.3 6.9L59.4 452l23-78.1c1.4-4.9 3.8-9.4 6.9-13.3l22.7-9.1v32c0 8.8 7.2 16 16 16h32zM362.7 18.7L348.3 33.2 325.7 55.8 314.3 67.1l33.9 33.9 62.1 62.1 33.9 33.9 11.3-11.3 22.6-22.6 14.5-14.5c25-25 25-65.5 0-90.5L453.3 18.7c-25-25-65.5-25-90.5 0zm-47.4 168l-144 144c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6l144-144c6.2-6.2 16.4-6.2 22.6 0s6.2 16.4 0 22.6z"/></svg>';
|
||||||
editBtn.appendChild(iconSpan);
|
editBtn.appendChild(iconSpan);
|
||||||
if (typeof lucide !== 'undefined') {
|
|
||||||
lucide.createIcons({ nodes: [editBtn] });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,6 +402,10 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul
|
|||||||
if (typeof lucide !== 'undefined') {
|
if (typeof lucide !== 'undefined') {
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reveal page after DOM manipulations are done
|
||||||
|
document.documentElement.classList.remove('bocken-cloak');
|
||||||
|
document.documentElement.classList.add('bocken-ready');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
@@ -426,6 +432,7 @@ class BockenTheme extends AbstractModule implements ModuleCustomInterface, Modul
|
|||||||
View::registerCustomView('::modules/place-hierarchy/list', $this->name() . '::modules/place-hierarchy/list');
|
View::registerCustomView('::modules/place-hierarchy/list', $this->name() . '::modules/place-hierarchy/list');
|
||||||
View::registerCustomView('::lists/individuals-table', $this->name() . '::lists/individuals-table');
|
View::registerCustomView('::lists/individuals-table', $this->name() . '::lists/individuals-table');
|
||||||
View::registerCustomView('::lists/families-table', $this->name() . '::lists/families-table');
|
View::registerCustomView('::lists/families-table', $this->name() . '::lists/families-table');
|
||||||
|
View::registerCustomView('::individual-page-images', $this->name() . '::individual-page-images');
|
||||||
View::registerCustomView('::individual-page-menu', $this->name() . '::individual-page-menu');
|
View::registerCustomView('::individual-page-menu', $this->name() . '::individual-page-menu');
|
||||||
View::registerCustomView('::modules/family_nav/sidebar-family', $this->name() . '::modules/family_nav/sidebar-family');
|
View::registerCustomView('::modules/family_nav/sidebar-family', $this->name() . '::modules/family_nav/sidebar-family');
|
||||||
}
|
}
|
||||||
|
|||||||
+160
-38
@@ -10,23 +10,30 @@
|
|||||||
|
|
||||||
/* ---------- Profile photo: large circle, no border ---------- */
|
/* ---------- Profile photo: large circle, no border ---------- */
|
||||||
|
|
||||||
.wt-route-IndividualPage .col-sm-3 .img-thumbnail {
|
#individual-images .carousel-inner {
|
||||||
border-radius: 50%;
|
overflow: visible;
|
||||||
aspect-ratio: 1 / 1;
|
|
||||||
object-fit: cover;
|
|
||||||
border: none;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
||||||
padding: 0;
|
|
||||||
transition: box-shadow 0.3s ease, transform 0.3s ease;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 260px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wt-route-IndividualPage .col-sm-3 .img-thumbnail:hover {
|
.wt-route-IndividualPage .col-sm-3 a.gallery {
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
display: block;
|
||||||
transform: translateY(-2px);
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wt-route-IndividualPage .col-sm-3 a.gallery .img-thumbnail {
|
||||||
|
border-radius: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Center the photo column */
|
/* Center the photo column */
|
||||||
.wt-route-IndividualPage .col-sm-3 {
|
.wt-route-IndividualPage .col-sm-3 {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -34,26 +41,41 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Silhouette placeholder: constrain to match photo size, circular background */
|
/* Silhouette placeholder: circular with gender-based background */
|
||||||
.wt-route-IndividualPage .col-sm-3 .wt-individual-silhouette {
|
.wt-route-IndividualPage .col-sm-3 > .img-thumbnail {
|
||||||
display: block;
|
|
||||||
width: auto !important;
|
|
||||||
max-height: 260px;
|
|
||||||
aspect-ratio: 1 / 1;
|
|
||||||
object-fit: contain;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
padding: 2rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "Add a media object" link — subtle */
|
.wt-route-IndividualPage .col-sm-3 .wt-individual-silhouette {
|
||||||
.wt-route-IndividualPage .col-sm-3 .text-center a {
|
display: block;
|
||||||
font-size: 0.8rem;
|
width: 100% !important;
|
||||||
opacity: 0.45;
|
height: 100%;
|
||||||
transition: opacity 0.2s;
|
opacity: 0.15;
|
||||||
|
filter: brightness(0);
|
||||||
}
|
}
|
||||||
.wt-route-IndividualPage .col-sm-3 .text-center a:hover {
|
|
||||||
opacity: 1;
|
/* Gender-based silhouette container — light mode */
|
||||||
|
.wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-m) {
|
||||||
|
background-color: #d1dce7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-f) {
|
||||||
|
background-color: #ded7e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-u) {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ---------- Individual header: photo + title + edit ---------- */
|
/* ---------- Individual header: photo + title + edit ---------- */
|
||||||
|
|
||||||
.wt-route-IndividualPage .bocken-individual-header {
|
.wt-route-IndividualPage .bocken-individual-header {
|
||||||
@@ -68,7 +90,7 @@
|
|||||||
width: auto;
|
width: auto;
|
||||||
max-width: 260px;
|
max-width: 260px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wt-route-IndividualPage .wt-page-title {
|
.wt-route-IndividualPage .wt-page-title {
|
||||||
@@ -100,20 +122,56 @@
|
|||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Edit button: icon-only pencil */
|
.wt-route-IndividualPage .bocken-title-user a {
|
||||||
|
color: #2E3440;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Edit button: fixed FAB in bottom-right corner */
|
||||||
.wt-route-IndividualPage .wt-page-menu-button {
|
.wt-route-IndividualPage .wt-page-menu-button {
|
||||||
width: 2.25rem;
|
position: fixed;
|
||||||
height: 2.25rem;
|
bottom: 0;
|
||||||
padding: 0;
|
right: 0;
|
||||||
display: inline-flex;
|
width: 1rem;
|
||||||
align-items: center;
|
height: 1rem;
|
||||||
|
padding: 2rem;
|
||||||
|
margin: 2rem;
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
background-color: #BF616A;
|
||||||
|
border: none;
|
||||||
|
z-index: 100;
|
||||||
|
transition: background-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
--shake-angle: 15deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wt-route-IndividualPage .wt-page-menu-button:hover,
|
||||||
|
.wt-route-IndividualPage .wt-page-menu-button:focus {
|
||||||
|
background-color: #2E3440;
|
||||||
|
box-shadow: 0 0 0.4em 0.15em rgba(0, 0, 0, 0.15);
|
||||||
|
animation: bocken-shake 0.5s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wt-route-IndividualPage .wt-page-menu-button .wt-icon-menu svg {
|
.wt-route-IndividualPage .wt-page-menu-button .wt-icon-menu svg {
|
||||||
width: 16px;
|
width: 2rem;
|
||||||
height: 16px;
|
height: 2rem;
|
||||||
|
fill: white;
|
||||||
|
stroke: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bocken-shake {
|
||||||
|
0% { transform: rotate(0) scale(1); }
|
||||||
|
25% { box-shadow: 0 0 0.5em 0.15em rgba(0, 0, 0, 0.25); transform: rotate(var(--shake-angle)) scale(1.2); }
|
||||||
|
50% { box-shadow: 0 0 0.5em 0.15em rgba(0, 0, 0, 0.25); transform: rotate(calc(-1 * var(--shake-angle))) scale(1.2); }
|
||||||
|
74% { box-shadow: 0 0 0.5em 0.15em rgba(0, 0, 0, 0.25); transform: rotate(var(--shake-angle)) scale(1.2); }
|
||||||
|
100% { transform: rotate(0) scale(1.2); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
.wt-route-IndividualPage .wt-page-menu-button {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide dropdown caret on the round button */
|
/* Hide dropdown caret on the round button */
|
||||||
@@ -516,9 +574,9 @@
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wt-route-IndividualPage .col-sm-3 .img-thumbnail {
|
.wt-route-IndividualPage .col-sm-3 a.gallery {
|
||||||
max-width: 80px;
|
|
||||||
width: 80px;
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Names accordion: full width */
|
/* Names accordion: full width */
|
||||||
@@ -571,6 +629,38 @@
|
|||||||
background: var(--color-primary, #5E81AC);
|
background: var(--color-primary, #5E81AC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Active tab: black background, light blue accent */
|
||||||
|
:root[data-theme=dark] .wt-tabs-individual .nav-tabs .nav-link.active {
|
||||||
|
background: #000;
|
||||||
|
border-bottom-color: #88C0D0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Silhouette icon tint */
|
||||||
|
:root[data-theme=dark] .wt-route-IndividualPage .col-sm-3 .wt-individual-silhouette {
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Silhouette gender colors */
|
||||||
|
:root[data-theme=dark] .wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-m) {
|
||||||
|
background-color: #505f73;
|
||||||
|
}
|
||||||
|
:root[data-theme=dark] .wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-f) {
|
||||||
|
background-color: #5f596d;
|
||||||
|
}
|
||||||
|
:root[data-theme=dark] .wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-u) {
|
||||||
|
background-color: #4C566A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Profile picture shadow */
|
||||||
|
:root[data-theme=dark] .wt-route-IndividualPage .col-sm-3 a.gallery {
|
||||||
|
box-shadow: 0 4px 8px rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Username link */
|
||||||
|
:root[data-theme=dark] .wt-route-IndividualPage .bocken-title-user a {
|
||||||
|
color: #ECEFF4;
|
||||||
|
}
|
||||||
|
|
||||||
/* Links */
|
/* Links */
|
||||||
:root[data-theme=dark] .wt-route-IndividualPage .wt-facts-table a,
|
:root[data-theme=dark] .wt-route-IndividualPage .wt-facts-table a,
|
||||||
:root[data-theme=dark] .wt-route-IndividualPage .wt-family-navigator-family a,
|
:root[data-theme=dark] .wt-route-IndividualPage .wt-family-navigator-family a,
|
||||||
@@ -617,6 +707,38 @@
|
|||||||
background: var(--color-primary, #5E81AC);
|
background: var(--color-primary, #5E81AC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Active tab: black background, light blue accent */
|
||||||
|
:root:not([data-theme=light]) .wt-tabs-individual .nav-tabs .nav-link.active {
|
||||||
|
background: #000;
|
||||||
|
border-bottom-color: #88C0D0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Silhouette icon tint */
|
||||||
|
:root:not([data-theme=light]) .wt-route-IndividualPage .col-sm-3 .wt-individual-silhouette {
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Silhouette gender colors */
|
||||||
|
:root:not([data-theme=light]) .wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-m) {
|
||||||
|
background-color: #505f73;
|
||||||
|
}
|
||||||
|
:root:not([data-theme=light]) .wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-f) {
|
||||||
|
background-color: #5f596d;
|
||||||
|
}
|
||||||
|
:root:not([data-theme=light]) .wt-route-IndividualPage .col-sm-3 > .img-thumbnail:has(.wt-individual-silhouette-u) {
|
||||||
|
background-color: #4C566A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Profile picture shadow */
|
||||||
|
:root:not([data-theme=light]) .wt-route-IndividualPage .col-sm-3 a.gallery {
|
||||||
|
box-shadow: 0 4px 8px rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Username link */
|
||||||
|
:root:not([data-theme=light]) .wt-route-IndividualPage .bocken-title-user a {
|
||||||
|
color: #ECEFF4;
|
||||||
|
}
|
||||||
|
|
||||||
/* Links */
|
/* Links */
|
||||||
:root:not([data-theme=light]) .wt-route-IndividualPage .wt-facts-table a,
|
:root:not([data-theme=light]) .wt-route-IndividualPage .wt-facts-table a,
|
||||||
:root:not([data-theme=light]) .wt-route-IndividualPage .wt-family-navigator-family a,
|
:root:not([data-theme=light]) .wt-route-IndividualPage .wt-family-navigator-family a,
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Fisharebest\Webtrees\Fact;
|
||||||
|
use Fisharebest\Webtrees\Http\RequestHandlers\AddNewFact;
|
||||||
|
use Fisharebest\Webtrees\I18N;
|
||||||
|
use Fisharebest\Webtrees\Individual;
|
||||||
|
use Fisharebest\Webtrees\Media;
|
||||||
|
use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
|
||||||
|
use Fisharebest\Webtrees\Module\ModuleTabInterface;
|
||||||
|
use Fisharebest\Webtrees\Tree;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $age
|
||||||
|
* @var bool $can_upload_media
|
||||||
|
* @var Collection<int,Media> $individual_media
|
||||||
|
* @var Collection<int,Fact> $name_records
|
||||||
|
* @var Individual $record
|
||||||
|
* @var Collection<int,Fact> $sex_records
|
||||||
|
* @var Collection<int,string> $shares
|
||||||
|
* @var Collection<int,ModuleSidebarInterface> $sidebars
|
||||||
|
* @var Collection<int,ModuleTabInterface> $tabs
|
||||||
|
* @var Tree $tree
|
||||||
|
* @var string $user_link
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if ($individual_media->isNotEmpty() || $tree->getPreference('USE_SILHOUETTE') === '1') : ?>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<?php if ($individual_media->isEmpty()) : ?>
|
||||||
|
<div class="img-thumbnail">
|
||||||
|
<i class="wt-individual-silhouette wt-individual-silhouette-<?= strtolower($record->sex()) ?> wt-icon-flip-rtl w-100"></i>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($individual_media->count() === 1) : ?>
|
||||||
|
<?= $individual_media->first()->displayImage(400, 400, 'contain', ['class' => 'img-thumbnail img-fluid w-100']) ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<div id="individual-images" class="carousel slide" data-bs-interval="false">
|
||||||
|
<div class="carousel-inner">
|
||||||
|
<?php foreach ($individual_media as $n => $media_file) : ?>
|
||||||
|
<div class="carousel-item <?= $n === 0 ? 'active' : '' ?>">
|
||||||
|
<?= $media_file->displayImage(400, 400, 'contain', ['class' => 'img-thumbnail img-fluid w-100']) ?>
|
||||||
|
</div>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
Reference in New Issue
Block a user