Implement image gallery
This commit is contained in:
parent
e4b4930009
commit
93d5f61f4f
@ -11,7 +11,6 @@ use crate::theme::ThemeContext;
|
|||||||
use crate::theme::ThemeMsg;
|
use crate::theme::ThemeMsg;
|
||||||
use crate::theme::ThemeProvider;
|
use crate::theme::ThemeProvider;
|
||||||
use crate::component::actionbar::{Actionbar, ActionbarOption};
|
use crate::component::actionbar::{Actionbar, ActionbarOption};
|
||||||
use crate::util::log;
|
|
||||||
|
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
@ -38,6 +37,7 @@ fn ThemedApp() -> Html {
|
|||||||
Page::Home => Icon::Home,
|
Page::Home => Icon::Home,
|
||||||
Page::Projects => Icon::Code,
|
Page::Projects => Icon::Code,
|
||||||
Page::Socials => Icon::SocialMedia,
|
Page::Socials => Icon::SocialMedia,
|
||||||
|
Page::Gallery => Icon::Camera,
|
||||||
Page::Contact => Icon::Envelope,
|
Page::Contact => Icon::Envelope,
|
||||||
},
|
},
|
||||||
Callback::from(move |_| {
|
Callback::from(move |_| {
|
||||||
@ -62,11 +62,15 @@ fn ThemedApp() -> Html {
|
|||||||
|
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
|
<div class="main">
|
||||||
|
<Pages />
|
||||||
|
</div>
|
||||||
<Actionbar>
|
<Actionbar>
|
||||||
{vec![
|
{vec![
|
||||||
page_option(¤t_page, Page::Home, navigator.clone(), page_state.clone()),
|
page_option(¤t_page, Page::Home, navigator.clone(), page_state.clone()),
|
||||||
page_option(¤t_page, Page::Projects, navigator.clone(), page_state.clone()),
|
page_option(¤t_page, Page::Projects, navigator.clone(), page_state.clone()),
|
||||||
page_option(¤t_page, Page::Socials, navigator.clone(), page_state.clone()),
|
page_option(¤t_page, Page::Socials, navigator.clone(), page_state.clone()),
|
||||||
|
page_option(¤t_page, Page::Gallery, navigator.clone(), page_state.clone()),
|
||||||
page_option(¤t_page, Page::Contact, navigator, page_state),
|
page_option(¤t_page, Page::Contact, navigator, page_state),
|
||||||
ActionbarOption::new_opt(
|
ActionbarOption::new_opt(
|
||||||
None,
|
None,
|
||||||
@ -78,9 +82,6 @@ fn ThemedApp() -> Html {
|
|||||||
)
|
)
|
||||||
]}
|
]}
|
||||||
</Actionbar>
|
</Actionbar>
|
||||||
<div class="main">
|
|
||||||
<Pages />
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,11 @@
|
|||||||
|
use web_sys::MouseEvent;
|
||||||
use yew::{function_component, Html, html, Callback, Properties, use_state};
|
use yew::{function_component, Html, html, Callback, Properties, use_state};
|
||||||
use yewprint::{Overlay, Icon, IconSize, Intent};
|
use yewprint::{Overlay, Icon, IconSize, Intent};
|
||||||
|
|
||||||
const CHEVRON_SIZE: f64 = 40.0;
|
const CHEVRON_SIZE: f64 = 40.0;
|
||||||
const CHEVRON_TYPE: Intent = Intent::Danger;
|
const CHEVRON_TYPE: Intent = Intent::Danger;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Clone)]
|
||||||
pub struct ImageDescription {
|
pub struct ImageDescription {
|
||||||
link: String,
|
link: String,
|
||||||
description: Option<&'static str>
|
description: Option<&'static str>
|
||||||
@ -27,67 +28,115 @@ impl ImageDescription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct ImageViewerProps {
|
pub struct DefaultImageViewerProps {
|
||||||
pub images: Vec<ImageDescription>,
|
pub images: Vec<ImageDescription>,
|
||||||
pub open: bool,
|
pub open: bool,
|
||||||
pub onclose: Callback<()>
|
#[prop_or_default]
|
||||||
|
pub infinite: bool,
|
||||||
|
pub onclose: Callback<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn DefaultImageViewer(props: &DefaultImageViewerProps) -> Html {
|
||||||
|
let selected_image = use_state(|| 0);
|
||||||
|
let (select_next, select_prev) = (selected_image.clone(), selected_image.clone());
|
||||||
|
|
||||||
|
let (has_next, has_prev) = (props.infinite || *selected_image < props.images.len() - 1, props.infinite || *selected_image > 0);
|
||||||
|
let next = (*select_next + props.images.len() + 1) % props.images.len();
|
||||||
|
let prev = (*select_prev + props.images.len() - 1) % props.images.len();
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<ImageViewer
|
||||||
|
image={props.images[*selected_image].clone()}
|
||||||
|
open={props.open && !props.images.is_empty()}
|
||||||
|
onclose={props.onclose.clone()}
|
||||||
|
has_next={has_next}
|
||||||
|
has_prev={has_prev}
|
||||||
|
onnext={Callback::from(move |_| if has_next {
|
||||||
|
select_next.set(next);
|
||||||
|
})}
|
||||||
|
onprev={Callback::from(move |_| if has_prev {
|
||||||
|
select_prev.set(prev);
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct ImageViewerProps {
|
||||||
|
pub image: Option<ImageDescription>,
|
||||||
|
pub open: bool,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub has_prev: bool,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub has_next: bool,
|
||||||
|
pub onclose: Callback<()>,
|
||||||
|
pub onnext: Callback<MouseEvent>,
|
||||||
|
pub onprev: Callback<MouseEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn ImageViewer(props: &ImageViewerProps) -> Html {
|
pub fn ImageViewer(props: &ImageViewerProps) -> Html {
|
||||||
let selected_image = use_state(|| 0);
|
let onclose = props.onclose.clone();
|
||||||
let (select_next, select_prev) = (selected_image.clone(), selected_image.clone());
|
|
||||||
html! {
|
html! {
|
||||||
<Overlay
|
<>
|
||||||
class="gallery"
|
<Overlay
|
||||||
open={props.open && !props.images.is_empty()}
|
class="gallery"
|
||||||
onclose={&props.onclose}>
|
open={props.open && props.image.is_some()}
|
||||||
{
|
onclose={&props.onclose}>
|
||||||
if let Some(image_desc) = props.images.get(*selected_image) {
|
<>
|
||||||
Some(html! {
|
<div onclick={Callback::from(move |_| onclose.emit(()))}></div>
|
||||||
<>
|
{
|
||||||
<img class="gallery-image" src={if image_desc.link.starts_with("https?://") { image_desc.link.to_string() } else { format!("/img/{}", image_desc.link) }} />
|
if let Some(image_desc) = props.image.as_ref() {
|
||||||
{
|
let onclose = props.onclose.clone();
|
||||||
if let Some(description) = &image_desc.description {
|
Some(html! {
|
||||||
Some(html! {
|
<>
|
||||||
<div class="gallery-image-description">{description}</div>
|
<div onclick={Callback::from(move |_| onclose.emit(()))}>
|
||||||
})
|
<img src={if image_desc.link.starts_with("https?://") { image_desc.link.to_string() } else { format!("/img/{}", image_desc.link) }} />
|
||||||
} else { None }
|
{
|
||||||
}
|
if let Some(description) = &image_desc.description {
|
||||||
|
Some(html! {
|
||||||
|
<div>{description}</div>
|
||||||
|
})
|
||||||
|
} else { None }
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
// Controls: Next
|
// Controls: Next
|
||||||
if *selected_image < props.images.len() - 1 {
|
if props.has_next {
|
||||||
Some(html! {
|
Some(html! {
|
||||||
<Icon
|
<Icon
|
||||||
icon={yewprint::Icon::ChevronRight}
|
icon={yewprint::Icon::ChevronRight}
|
||||||
intent={CHEVRON_TYPE}
|
intent={CHEVRON_TYPE}
|
||||||
size={IconSize(CHEVRON_SIZE)}
|
size={IconSize(CHEVRON_SIZE)}
|
||||||
onclick={Callback::from(move |_| select_next.set(*select_next + 1))}
|
onclick={props.onnext.clone()}
|
||||||
class="gallery-next"
|
class="gallery-next"
|
||||||
/>
|
/>
|
||||||
})
|
})
|
||||||
} else { None }
|
} else { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Controls: Prev
|
// Controls: Prev
|
||||||
if *selected_image > 0 {
|
if props.has_prev {
|
||||||
Some(html! {
|
Some(html! {
|
||||||
<Icon
|
<Icon
|
||||||
icon={yewprint::Icon::ChevronLeft}
|
icon={yewprint::Icon::ChevronLeft}
|
||||||
intent={CHEVRON_TYPE}
|
intent={CHEVRON_TYPE}
|
||||||
size={IconSize(CHEVRON_SIZE)}
|
size={IconSize(CHEVRON_SIZE)}
|
||||||
onclick={Callback::from(move |_| select_prev.set(*select_prev - 1))}
|
onclick={props.onprev.clone()}
|
||||||
class="gallery-prev"
|
class="gallery-prev"
|
||||||
/>
|
/>
|
||||||
})
|
})
|
||||||
} else { None }
|
} else { None }
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
})
|
})
|
||||||
} else { None }
|
} else { None }
|
||||||
}
|
}
|
||||||
</Overlay>
|
</>
|
||||||
|
</Overlay>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
63
src/page/gallery.rs
Normal file
63
src/page/gallery.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use web_sys::MouseEvent;
|
||||||
|
use yew::{function_component, html, Html, use_state, Callback};
|
||||||
|
|
||||||
|
use crate::{component::image_viewer::{ImageDescription, ImageViewer}};
|
||||||
|
|
||||||
|
const GALLERY: [&str; 13] = [
|
||||||
|
"gallery-1.jpg",
|
||||||
|
"gallery-2.jpg",
|
||||||
|
"gallery-3.jpg",
|
||||||
|
"gallery-4.jpg",
|
||||||
|
"gallery-5.jpg",
|
||||||
|
"gallery-6.jpg",
|
||||||
|
"gallery-7.jpg",
|
||||||
|
"gallery-8.jpg",
|
||||||
|
"gallery-9.png",
|
||||||
|
"gallery-10.jpg",
|
||||||
|
"gallery-11.jpg",
|
||||||
|
"gallery-12.jpg",
|
||||||
|
"gallery-13.jpg",
|
||||||
|
];
|
||||||
|
|
||||||
|
fn gallery_entry(link: &&str, onclick: Callback<MouseEvent>) -> Html {
|
||||||
|
html! {
|
||||||
|
<div><img src={format!("/img/{}", link)} onclick={onclick}/></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn Gallery() -> Html {
|
||||||
|
let images = GALLERY.iter().map(|it| ImageDescription::new_blank(it.to_string())).collect::<Vec<ImageDescription>>();
|
||||||
|
let is_open = use_state(|| false);
|
||||||
|
let selected_image = use_state(|| 0);
|
||||||
|
let select_next = selected_image.clone();
|
||||||
|
let select_prev = selected_image.clone();
|
||||||
|
let next = (*select_next + images.len() + 1) % images.len();
|
||||||
|
let prev = (*select_prev + images.len() - 1) % images.len();
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<>
|
||||||
|
<div class="image-gallery">
|
||||||
|
{
|
||||||
|
GALLERY.iter().enumerate().map(|(index, img_str)| {
|
||||||
|
let select = selected_image.clone();
|
||||||
|
let open = is_open.clone();
|
||||||
|
gallery_entry(img_str, Callback::from(move |_| {
|
||||||
|
select.set(index);
|
||||||
|
open.set(true);
|
||||||
|
}))
|
||||||
|
}).collect::<Html>()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<ImageViewer
|
||||||
|
image={images[*selected_image].clone()}
|
||||||
|
open={*is_open && !images.is_empty()}
|
||||||
|
onclose={Callback::from(move |_| is_open.set(false))}
|
||||||
|
has_next=true
|
||||||
|
has_prev=true
|
||||||
|
onnext={Callback::from(move |_| select_next.set(next))}
|
||||||
|
onprev={Callback::from(move |_| select_prev.set(prev))}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ use serde::Deserialize;
|
|||||||
use yew::{function_component, html, Html, UseStateHandle, use_state, use_effect_with_deps, Properties, Children, use_context, Callback};
|
use yew::{function_component, html, Html, UseStateHandle, use_state, use_effect_with_deps, Properties, Children, use_context, Callback};
|
||||||
use yewprint::{Divider, Elevation, Card, Tag, Intent, Icon};
|
use yewprint::{Divider, Elevation, Card, Tag, Intent, Icon};
|
||||||
|
|
||||||
use crate::{util::log, theme::{ThemeContext, ThemeState}, component::image_viewer::{ImageDescription, ImageViewer}};
|
use crate::{util::log, theme::{ThemeContext, ThemeState}, component::image_viewer::{ImageDescription, DefaultImageViewer}};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum TagType {
|
enum TagType {
|
||||||
@ -216,12 +216,8 @@ fn HomeCard(props: &HomeCardProps) -> Html {
|
|||||||
if image.image.clickable {
|
if image.image.clickable {
|
||||||
if let ImageSource::Link(link) = &image.image.source {
|
if let ImageSource::Link(link) = &image.image.source {
|
||||||
html! {
|
html! {
|
||||||
<ImageViewer
|
<DefaultImageViewer
|
||||||
images={vec![
|
images={vec![ ImageDescription::new_blank(link) ]}
|
||||||
ImageDescription::new(link, "This is me"),
|
|
||||||
ImageDescription::new(link, "This is also me"),
|
|
||||||
ImageDescription::new(link, "This is not me")
|
|
||||||
]}
|
|
||||||
open={*overlay_state}
|
open={*overlay_state}
|
||||||
onclose={Callback::from(move |_| overlay_state.set(false))} />
|
onclose={Callback::from(move |_| overlay_state.set(false))} />
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ mod home;
|
|||||||
mod projects;
|
mod projects;
|
||||||
mod contact;
|
mod contact;
|
||||||
mod socials;
|
mod socials;
|
||||||
|
mod gallery;
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Routable)]
|
#[derive(PartialEq, Copy, Clone, Routable)]
|
||||||
pub enum Page {
|
pub enum Page {
|
||||||
@ -14,19 +15,23 @@ pub enum Page {
|
|||||||
#[at("/projects")]
|
#[at("/projects")]
|
||||||
Projects,
|
Projects,
|
||||||
|
|
||||||
|
#[at("/socials")]
|
||||||
|
Socials,
|
||||||
|
|
||||||
|
#[at("/photos")]
|
||||||
|
Gallery,
|
||||||
|
|
||||||
#[at("/contacts")]
|
#[at("/contacts")]
|
||||||
Contact,
|
Contact,
|
||||||
|
|
||||||
#[at("/socials")]
|
|
||||||
Socials
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn switch(page: Page) -> Html {
|
fn switch(page: Page) -> Html {
|
||||||
match page {
|
match page {
|
||||||
Page::Home => html!{<home::Home />},
|
Page::Home => html!{<home::Home />},
|
||||||
Page::Contact => html!{<contact::Contact />},
|
|
||||||
Page::Socials => html!{<socials::Socials />},
|
|
||||||
Page::Projects => html!{<projects::Projects />},
|
Page::Projects => html!{<projects::Projects />},
|
||||||
|
Page::Socials => html!{<socials::Socials />},
|
||||||
|
Page::Gallery => html!{<gallery::Gallery />},
|
||||||
|
Page::Contact => html!{<contact::Contact />},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +62,7 @@ impl Page {
|
|||||||
Page::Home => "Home",
|
Page::Home => "Home",
|
||||||
Page::Projects => "Projects",
|
Page::Projects => "Projects",
|
||||||
Page::Socials => "Social Media",
|
Page::Socials => "Social Media",
|
||||||
|
Page::Gallery => "Gallery",
|
||||||
Page::Contact => "Contact"
|
Page::Contact => "Contact"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<title>Gabriel Tofvesson</title>
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap" rel="stylesheet">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
@ -1,11 +1,45 @@
|
|||||||
:root {
|
:root {
|
||||||
--bg-color: #f5f8fa;
|
--bg-color: #f5f8fa;
|
||||||
|
--gallery-scale: 95vmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme='dark'] {
|
:root[data-theme='dark'] {
|
||||||
--bg-color: #182026;
|
--bg-color: #00251a;
|
||||||
|
--color-primary: #004d40;
|
||||||
|
--color-accent: #39796b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root[data-theme='mauve'] {
|
||||||
|
--bg-color: #560027;
|
||||||
|
--color-primary: #880e4f;
|
||||||
|
--color-accent: #bc477b;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='gray'] {
|
||||||
|
--bg-color: #455A64;
|
||||||
|
--color-primary: #607D8B;
|
||||||
|
--color-accent: #212121;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='purple'] {
|
||||||
|
--bg-color: #512DA8;
|
||||||
|
--color-primary: #673AB7;
|
||||||
|
--color-accent: #009688;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-card.bp3-dark, .bp3-dark .bp3-card {
|
||||||
|
background-color: var(--color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-dark .bp3-button:not([class*="bp3-intent-"]):active, .bp3-dark .bp3-button:not([class*="bp3-intent-"]).bp3-active {
|
||||||
|
background-color: var(--color-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-dark .bp3-button:not([class*="bp3-intent-"]) {
|
||||||
|
background-color: var(--color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
button:focus, input[type=button]:focus {
|
button:focus, input[type=button]:focus {
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
@ -121,21 +155,46 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.gallery {
|
.gallery {
|
||||||
|
width: var(--gallery-scale);
|
||||||
|
height: var(--gallery-scale);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
overflow: hidden;
|
display: flex;
|
||||||
border-radius: 20px;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-image {
|
.gallery > div:nth-of-type(1) {
|
||||||
width: 33vw;
|
position: absolute;
|
||||||
height: auto;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery > div:nth-of-type(2) {
|
||||||
|
transform: translate(calc(calc(var(--gallery-scale) / 2) - 50%), 0);
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery > div:nth-of-type(2) > img {
|
||||||
|
float: left;
|
||||||
|
max-width: var(--gallery-scale);
|
||||||
|
max-height: var(--gallery-scale);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery > div:nth-of-type(2) > div {
|
||||||
|
bottom: 0;
|
||||||
|
padding: 60px 10px 10px 10px;
|
||||||
|
width: 100%;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
background: linear-gradient(to top, rgba(0, 75, 196, 0.699), rgba(133, 180, 255, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gallery overlay transition properties */
|
/* Gallery overlay transition properties */
|
||||||
.gallery-image-description,
|
.gallery > div:nth-of-type(2) > div,
|
||||||
.gallery-next,
|
.gallery-next,
|
||||||
.gallery-prev {
|
.gallery-prev {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -144,19 +203,12 @@ body {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-image-description {
|
|
||||||
width: 33vw;
|
|
||||||
transform: translate(0px, calc(-100% - 4px));
|
|
||||||
background: linear-gradient(to top, rgba(0, 75, 196, 0.699), rgba(133, 180, 255, 0));
|
|
||||||
padding: 60px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gallery overlay button positioning */
|
/* Gallery overlay button positioning */
|
||||||
.gallery-next,
|
.gallery-next,
|
||||||
.gallery-prev {
|
.gallery-prev {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
height: max(calc(100% - 4px), 10px);
|
height: max(calc(100% - 4px), 10px);
|
||||||
width: 7.5%;
|
width: 12.5%;
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@ -165,21 +217,73 @@ body {
|
|||||||
left: 100%;
|
left: 100%;
|
||||||
transform: translate(-100%, -50%);
|
transform: translate(-100%, -50%);
|
||||||
padding-right: 2.5%;
|
padding-right: 2.5%;
|
||||||
|
padding-left: 5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-prev {
|
.gallery-prev {
|
||||||
left: 0%;
|
left: 0%;
|
||||||
transform: translate(-0%, -50%);
|
transform: translate(-0%, -50%);
|
||||||
padding-left: 2.5%;
|
padding-left: 2.5%;
|
||||||
|
padding-right: 5%;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery:hover > .gallery-image-description,
|
.gallery > div:nth-of-type(2):hover > div,
|
||||||
.gallery:hover > .gallery-next,
|
.gallery:hover .gallery-next,
|
||||||
.gallery:hover > .gallery-prev {
|
.gallery:hover .gallery-prev {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-next:hover,
|
.gallery-next:hover,
|
||||||
.gallery-prev:hover {
|
.gallery-prev:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-gallery {
|
||||||
|
line-height: 0;
|
||||||
|
column-count: 5;
|
||||||
|
column-gap: 5px;
|
||||||
|
transition-timing-function: cubic-bezier(0.1, 0.7, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-gallery div {
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-gallery div img {
|
||||||
|
width: 98% !important;
|
||||||
|
height: auto !important;
|
||||||
|
filter: grayscale(var(--grayscale,100%)) /*blur(var(--blur,1px))*/;
|
||||||
|
transition: 250ms all;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-gallery div:hover {
|
||||||
|
--grayscale: 0%;
|
||||||
|
--blur: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.image-gallery {
|
||||||
|
column-count: 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.image-gallery {
|
||||||
|
column-count: 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.image-gallery {
|
||||||
|
column-count: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.image-gallery {
|
||||||
|
column-count: 1;
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user