folder structure
|
@ -1,13 +0,0 @@
|
||||||
# editorconfig.org
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
25
.eslintrc.js
|
@ -1,25 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
node: true
|
|
||||||
},
|
|
||||||
parserOptions: {
|
|
||||||
parser: 'babel-eslint'
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'plugin:vue/recommended'
|
|
||||||
],
|
|
||||||
// required to lint *.vue files
|
|
||||||
plugins: [
|
|
||||||
'vue'
|
|
||||||
],
|
|
||||||
// add your custom rules here
|
|
||||||
rules: {
|
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
|
||||||
'vue/html-self-closing': 'off',
|
|
||||||
'vue/attributes-order': 'off',
|
|
||||||
'vue/max-attributes-per-line': 'off'
|
|
||||||
}
|
|
||||||
}
|
|
22
README.md
|
@ -1,22 +0,0 @@
|
||||||
# MarcLeopold
|
|
||||||
|
|
||||||
> Marc Leopold Website
|
|
||||||
|
|
||||||
## Build Setup
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
# install dependencies
|
|
||||||
$ yarn install
|
|
||||||
|
|
||||||
# serve with hot reload at localhost:3000
|
|
||||||
$ yarn run dev
|
|
||||||
|
|
||||||
# build for production and launch server
|
|
||||||
$ yarn run build
|
|
||||||
$ yarn start
|
|
||||||
|
|
||||||
# generate static project
|
|
||||||
$ yarn run generate
|
|
||||||
```
|
|
||||||
|
|
||||||
For detailed explanation on how things work, checkout [Nuxt.js docs](https://nuxtjs.org).
|
|
|
@ -1,63 +0,0 @@
|
||||||
import express from 'express'
|
|
||||||
const nodemailer = require('nodemailer')
|
|
||||||
const validator = require('validator')
|
|
||||||
const xssFilters = require('xss-filters')
|
|
||||||
|
|
||||||
const app = express()
|
|
||||||
app.use(express.json())
|
|
||||||
|
|
||||||
app.post('/', function (req, res) {
|
|
||||||
const attributes = ['name', 'email', 'msg']
|
|
||||||
const sanitizedAttributes = attributes.map(n => validateAndSanitize(n, req.body[n]))
|
|
||||||
const someInvalid = sanitizedAttributes.some(r => !r)
|
|
||||||
|
|
||||||
if (someInvalid) {
|
|
||||||
return res.status(400).json({ 'error': 'bad request'})
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMail(...sanitizedAttributes)
|
|
||||||
return res.status(200).json({ 'message': 'success'})
|
|
||||||
})
|
|
||||||
|
|
||||||
export default {
|
|
||||||
path: '/api/contact',
|
|
||||||
handler: app,
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateAndSanitize (key, value) {
|
|
||||||
const rejectFunctions = {
|
|
||||||
name: v => v.length < 4,
|
|
||||||
email: v => !validator.isEmail(v),
|
|
||||||
msg: v => v.length < 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === undefined || value.length < 1) { return false }
|
|
||||||
|
|
||||||
// if object has key and function returns false, return sanitised input.
|
|
||||||
// Else, return false
|
|
||||||
return rejectFunctions.hasOwnProperty(key) && !rejectFunctions[key](value) && xssFilters.inHTMLData(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendMail (name, email, msg) {
|
|
||||||
const transporter = nodemailer.createTransport({
|
|
||||||
sendmail: true,
|
|
||||||
newline: 'unix',
|
|
||||||
path: '/usr/sbin/sendmail'
|
|
||||||
})
|
|
||||||
|
|
||||||
const text =
|
|
||||||
`Message from ${name}:
|
|
||||||
|
|
||||||
${msg}`
|
|
||||||
|
|
||||||
const mailJson = {
|
|
||||||
from: 'server@gabbaell.co.uk',
|
|
||||||
replyTo: email,
|
|
||||||
to: 'marcleopold.isnet@gabbaell.co.uk',
|
|
||||||
subject: 'Contact form message concerning Marc Leopold',
|
|
||||||
text: text,
|
|
||||||
}
|
|
||||||
|
|
||||||
transporter.sendMail(mailJson)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# ASSETS
|
|
||||||
|
|
||||||
**This directory is not required, you can delete it if you don't want to use it.**
|
|
||||||
|
|
||||||
This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
|
|
||||||
|
|
||||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).
|
|
|
@ -1,36 +0,0 @@
|
||||||
// * {
|
|
||||||
// outline: 1px dotted red;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
html {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Raleway', sans-serif;
|
|
||||||
font-size: 100%;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
margin: 0 0 3rem;
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2, h3, h4, h5, h6 {
|
|
||||||
color: $color__neutral-600;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
background-color: initial;
|
|
||||||
@include font-cursive();
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
$link: $color__primary-500;
|
|
||||||
$link-hover: $color__primary-700;
|
|
||||||
|
|
||||||
$primary: #f00;
|
|
||||||
$white: $color__neutral-900;
|
|
||||||
$danger: $color__accent-danger-300;
|
|
||||||
$success: $color__accent-success-300;
|
|
||||||
|
|
||||||
@import "~bulma/sass/utilities/_all.sass";
|
|
||||||
@import "~bulma/bulma.sass";
|
|
||||||
@import "~buefy/src/scss/buefy";
|
|
||||||
|
|
||||||
.label {
|
|
||||||
position: relative;
|
|
||||||
color: $color__neutral-700;
|
|
||||||
@include font-title(600);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input, .textarea {
|
|
||||||
$color-bg: $color__neutral-900;
|
|
||||||
$gradient-from: .1;
|
|
||||||
$gradient-to: .3;
|
|
||||||
font-weight: 600;
|
|
||||||
background-color: rgba($color__neutral-900, .7);
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color-bg, $gradient-from),
|
|
||||||
rgba($color-bg, $gradient-to),
|
|
||||||
rgba($color-bg, $gradient-to),
|
|
||||||
rgba($color-bg, $gradient-from),
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba($color-bg, $gradient-from),
|
|
||||||
rgba($color-bg, $gradient-to),
|
|
||||||
rgba($color-bg, $gradient-to),
|
|
||||||
rgba($color-bg, $gradient-from),
|
|
||||||
);
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
$color-bg: #fff;
|
|
||||||
$gradient-from: .1;
|
|
||||||
$gradient-to: .5;
|
|
||||||
|
|
||||||
background: $color-bg;
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color-bg, $gradient-from),
|
|
||||||
rgba($color-bg, $gradient-to),
|
|
||||||
rgba($color-bg, $gradient-to),
|
|
||||||
rgba($color-bg, $gradient-from),
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba($color-bg, $gradient-from),
|
|
||||||
rgba($color-bg, $gradient-to),
|
|
||||||
rgba($color-bg, $gradient-to),
|
|
||||||
rgba($color-bg, $gradient-from),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.help {
|
|
||||||
@include font-body(600);
|
|
||||||
font-size: .8em;
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
@import 'palette';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
$bp__m: 40em;
|
|
||||||
|
|
||||||
// layout optomised for larger screens
|
|
||||||
$bp__layout: 40em;
|
|
||||||
|
|
||||||
// gallery ui becomes more compact
|
|
||||||
$bp__gallery-compact: 75em;
|
|
||||||
|
|
||||||
$z-index__page: 50;
|
|
||||||
$z-index__page-overlay: 75;
|
|
||||||
$z-index__menu: 100;
|
|
||||||
|
|
||||||
$site-menu__width: 20rem;
|
|
||||||
$site-menu__header-width: 3rem;
|
|
||||||
$site-menu__header-height: 3rem;
|
|
||||||
$site-menu__color-bg: $color__neutral-200;
|
|
||||||
|
|
||||||
$gallery-featured-width: 20rem;
|
|
||||||
$gallery-featured-width--compact: 12rem;
|
|
||||||
|
|
||||||
$gallery-thumbs-height: 8rem;
|
|
||||||
$gallery-thumbs-height--compact: 6rem;
|
|
|
@ -1,14 +0,0 @@
|
||||||
@mixin font-body($weight: 400) {
|
|
||||||
font-family: 'Raleway', sans-serif;
|
|
||||||
font-weight: $weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin font-title($weight: 400) {
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
font-weight: $weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin font-cursive() {
|
|
||||||
font-family: 'Satisfy', cursive;
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
$color__primary-100: #090b10;
|
|
||||||
$color__primary-200: #242b3e;
|
|
||||||
$color__primary-300: #35405d;
|
|
||||||
$color__primary-400: #505f8c;
|
|
||||||
$color__primary-500: #6174aa;
|
|
||||||
$color__primary-600: #7d8db9;
|
|
||||||
$color__primary-700: #a8b3d0;
|
|
||||||
$color__primary-800: #c5cce0;
|
|
||||||
$color__primary-900: #e2e5ef;
|
|
||||||
|
|
||||||
$color__neutral-100: #060606;
|
|
||||||
$color__neutral-200: #121212;
|
|
||||||
$color__neutral-300: #212121;
|
|
||||||
$color__neutral-400: #353535;
|
|
||||||
$color__neutral-500: #494949;
|
|
||||||
$color__neutral-600: #717171;
|
|
||||||
$color__neutral-700: #9a9a9a;
|
|
||||||
$color__neutral-800: #c2c2c2;
|
|
||||||
$color__neutral-900: #eaeaea;
|
|
||||||
|
|
||||||
$color__accent-danger-100: #270202;
|
|
||||||
$color__accent-danger-300: #450403;
|
|
||||||
$color__accent-danger-500: #6b0504;
|
|
||||||
$color__accent-danger-700: #a05f5f;
|
|
||||||
$color__accent-danger-900: #d6baba;
|
|
||||||
|
|
||||||
$color__accent-success-100: #182816;
|
|
||||||
$color__accent-success-300: #264123;
|
|
||||||
$color__accent-success-500: #345830;
|
|
||||||
$color__accent-success-700: #7d947b;
|
|
||||||
$color__accent-success-900: #b5c2b3;
|
|
|
@ -1,194 +0,0 @@
|
||||||
@import 'buefy';
|
|
||||||
@import 'base';
|
|
||||||
|
|
||||||
.page-title {
|
|
||||||
@include font-title();
|
|
||||||
color: $color__neutral-900;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
transition: opacity 3s 2s;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
@at-root .is-mounted & {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
font-size: 3.7vw;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 90em) {
|
|
||||||
font-size: 3.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-heading {
|
|
||||||
margin: 1rem;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
width: 14em;
|
|
||||||
max-width: calc(50% - #{$site-menu__header-width} - 3rem);
|
|
||||||
margin: 2rem 1rem 1rem 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-overlay {
|
|
||||||
$color: $color__neutral-100; // color of overlay
|
|
||||||
|
|
||||||
box-shadow: 0 0 12px 0 $color__neutral-100 inset,
|
|
||||||
0 2px 12px 3px rgba($color__neutral-100, .5);
|
|
||||||
|
|
||||||
transition: filter 1s;
|
|
||||||
filter: grayscale(.5);
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
box-shadow: 0 0 12px 0 $color__neutral-400 inset,
|
|
||||||
0 2px 12px 3px rgba($color__neutral-300, .5);
|
|
||||||
|
|
||||||
filter: grayscale(.95);
|
|
||||||
&:hover {
|
|
||||||
filter: grayscale(.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color, .5),
|
|
||||||
rgba($color, 0),
|
|
||||||
rgba($color, .7)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
background: linear-gradient(
|
|
||||||
to left,
|
|
||||||
rgba($color, .2),
|
|
||||||
rgba($color, 0),
|
|
||||||
rgba($color, .2)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-link {
|
|
||||||
padding: .4em .8em;
|
|
||||||
color: $color__neutral-900;
|
|
||||||
border: 2px solid $color__neutral-900;
|
|
||||||
@include font-title();
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
z-index: -1;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
background-color: $color__neutral-200;
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom left,
|
|
||||||
$color__neutral-400,
|
|
||||||
$color__neutral-200
|
|
||||||
);
|
|
||||||
|
|
||||||
transition: opacity .3s;
|
|
||||||
opacity: .4;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:link, &:visited {
|
|
||||||
color: $color__neutral-900;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover, &:active {
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-overlay {
|
|
||||||
z-index: 10;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-tint {
|
|
||||||
background-color: rgba($color__primary-100, .7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-deco {
|
|
||||||
$color: rgba($color__neutral-100, .3);
|
|
||||||
|
|
||||||
background-color: $color;
|
|
||||||
box-shadow: 0 0 64px 64px $color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-content-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-content: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-content-text {
|
|
||||||
color: $color__neutral-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-indicator {
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
transition: opacity 1s;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
border: 1px solid $color__neutral-700;;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active::before {
|
|
||||||
transition: opacity 2s .2s;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-enter-active, .page-leave-active {
|
|
||||||
transition: opacity .5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-enter, .page-leave-active {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nuxt-progress {
|
|
||||||
z-index: 99999;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
||||||
<svg version="1.1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
x="0px" y="0px"
|
|
||||||
viewBox="0 0 256 256"
|
|
||||||
xml:space="preserve"
|
|
||||||
>
|
|
||||||
<g>
|
|
||||||
<polygon points="207.093,30.187 176.907,0 48.907,128 176.907,256 207.093,225.813 109.28,128"
|
|
||||||
fill-opacity="0.5"
|
|
||||||
fill="#000000"
|
|
||||||
stroke="#ffffff"
|
|
||||||
stroke-width="6px"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 522 B |
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg version="1.1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 350.39741 351.03559"
|
|
||||||
y="0px"
|
|
||||||
x="0px"
|
|
||||||
>
|
|
||||||
<path d="m 91.878859,32.707107 83.892861,83.892873 82.61784,-82.617833 58.66388,58.663883 -82.61783,82.61783 83.25469,83.2547 -58.98035,58.98035 -83.2547,-83.25469 L 91.37099,318.32848 32.707107,259.6646 116.79137,175.58033 32.898502,91.687463 Z"
|
|
||||||
fill-opacity="0.5"
|
|
||||||
fill="#000000"
|
|
||||||
stroke="#ffffff"
|
|
||||||
stroke-width="15px"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 609 B |
|
@ -1 +0,0 @@
|
||||||
<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-rolling" style="background: none;"><circle cx="50" cy="50" fill="none" ng-attr-stroke="{{config.color}}" ng-attr-stroke-width="{{config.width}}" ng-attr-r="{{config.radius}}" ng-attr-stroke-dasharray="{{config.dasharray}}" stroke="#ff7c00" stroke-width="10" r="35" stroke-dasharray="164.93361431346415 56.97787143782138" transform="rotate(173.999 50 50)"><animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform></circle></svg>
|
|
Before Width: | Height: | Size: 687 B |
|
@ -1,201 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="background-image-loader">
|
|
||||||
<div class="background-container">
|
|
||||||
<div class="background background-default"></div>
|
|
||||||
<transition name="fade-fast"
|
|
||||||
>
|
|
||||||
<div v-if="loadedImageUrl !== null"
|
|
||||||
class="background background-img background-blur"
|
|
||||||
:style="backgroundBlurStyle"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
<transition name="fade"
|
|
||||||
@beforeEnter="handleBeforeEnter"
|
|
||||||
@afterLeave="handleAfterLeave"
|
|
||||||
>
|
|
||||||
<div class="background background-img"
|
|
||||||
:key="loadedImageUrl"
|
|
||||||
:style="backgroundStyle"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
<slot name="overlay">
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import imageLoader from '~/mixins/imageLoader.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [ imageLoader ],
|
|
||||||
|
|
||||||
props: {
|
|
||||||
imageUrl: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: function () {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
loadedImageUrl: null,
|
|
||||||
isInTransition: false,
|
|
||||||
transitionTimeout: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
backgroundStyle () {
|
|
||||||
let style = {}
|
|
||||||
if (this.loadedImageUrl) {
|
|
||||||
style.backgroundImage = `url(${this.loadedImageUrl})`
|
|
||||||
}
|
|
||||||
|
|
||||||
return style
|
|
||||||
},
|
|
||||||
|
|
||||||
backgroundBlurStyle () {
|
|
||||||
let style = {}
|
|
||||||
if (this.loadedImageUrl) {
|
|
||||||
style.backgroundImage = `url(${this.loadedImageUrl})`
|
|
||||||
}
|
|
||||||
|
|
||||||
return style
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
imageUrl () {
|
|
||||||
if (!this.isInTransition && this.imageUrl) {
|
|
||||||
this.setImage(this.imageUrl)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
this.setImage(this.imageUrl)
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setImage(url) {
|
|
||||||
if (url === null) { return }
|
|
||||||
this.loadImage(url)
|
|
||||||
.then(img => {
|
|
||||||
this.loadedImageUrl = img.src
|
|
||||||
this.$emit('imageLoaded', img)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$emit('imageLoadError', err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
handleBeforeEnter() {
|
|
||||||
// TOOD get the actual value programatically
|
|
||||||
// use ref and ref.style.transitionDuration or whatever
|
|
||||||
let transitionDuration = 4000
|
|
||||||
|
|
||||||
this.isInTransition = true
|
|
||||||
|
|
||||||
clearTimeout(this.transitionTimeout)
|
|
||||||
this.transitionTimeout = setTimeout(() => {
|
|
||||||
this.isInTransition = false
|
|
||||||
if (this.imageUrl !== this.loadedImageUrl) {
|
|
||||||
this.setImage(this.imageUrl)
|
|
||||||
}
|
|
||||||
}, transitionDuration)
|
|
||||||
},
|
|
||||||
|
|
||||||
handleAfterLeave() {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
.background-image-loader {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-img {
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center center;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-default {
|
|
||||||
$shadow-opacity: .6;
|
|
||||||
$shadow-color: $color__neutral-100;
|
|
||||||
background:
|
|
||||||
linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba($shadow-color, $shadow-opacity),
|
|
||||||
rgba($shadow-color, 0) 20%,
|
|
||||||
rgba($shadow-color, 0) 80%,
|
|
||||||
rgba($shadow-color, $shadow-opacity)
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($shadow-color, $shadow-opacity),
|
|
||||||
rgba($shadow-color, 0) 20%,
|
|
||||||
rgba($shadow-color, 0) 80%,
|
|
||||||
rgba($shadow-color, $shadow-opacity)
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to top,
|
|
||||||
$color__neutral-100,
|
|
||||||
$color__neutral-300
|
|
||||||
)
|
|
||||||
;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-blur {
|
|
||||||
filter: blur(30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade {
|
|
||||||
$timing: .7;
|
|
||||||
&-enter-active {
|
|
||||||
transition: opacity 4s * $timing 1s * $timing ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-leave-active {
|
|
||||||
transition: opacity 2s * $timing ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter, &-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-fast {
|
|
||||||
$timing: 1;
|
|
||||||
&-enter-active {
|
|
||||||
transition: opacity 3s * $timing ease-in;
|
|
||||||
transition-delay: .2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,76 +0,0 @@
|
||||||
<template>
|
|
||||||
<BackgroundImageLoader :image-url="imageUrls[activeIndex]"
|
|
||||||
@imageLoaded="handleImageLoaded"
|
|
||||||
>
|
|
||||||
<template slot="overlay">
|
|
||||||
<slot name="overlay"></slot>
|
|
||||||
</template>
|
|
||||||
</BackgroundImageLoader>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import imageLoader from '~/mixins/imageLoader.js'
|
|
||||||
import BackgroundImageLoader from '@/components/BackgroundImageLoader'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
BackgroundImageLoader,
|
|
||||||
},
|
|
||||||
|
|
||||||
mixins: [ imageLoader ],
|
|
||||||
|
|
||||||
props: {
|
|
||||||
imageUrls: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
activeIndex: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default () {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
currentlyLoadingIndex: 1,
|
|
||||||
errorUrls: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
if (this.imageUrls.length > 1) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.preloadImage()
|
|
||||||
}, 100) // timeout so events get fired - don't know why that is yet TODO - investigate properly
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
preloadImage () {
|
|
||||||
this.loadImage(this.imageUrls[this.currentlyLoadingIndex])
|
|
||||||
.then(img => {
|
|
||||||
this.$emit('imageLoaded', img)
|
|
||||||
this.currentlyLoadingIndex++
|
|
||||||
if (this.currentlyLoadingIndex < this.imageUrls.length) {
|
|
||||||
this.preloadImage()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// TODO handle error cases
|
|
||||||
console.log('imageloaded ERROR', err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
handleImageLoaded () {
|
|
||||||
this.$emit('imageLoaded', this.imageUrls[this.activeIndex])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
</style>
|
|
|
@ -1,128 +0,0 @@
|
||||||
<template>
|
|
||||||
<article class="content-page"
|
|
||||||
:class="{ 'is-mounted': isMounted }"
|
|
||||||
>
|
|
||||||
<h1 v-if="heading !== ''" class="page-heading page-title">{{ heading }}</h1>
|
|
||||||
<section class="content-container load-transition">
|
|
||||||
<div v-if="$slots.default" class="content">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<div v-if="$slots.background" class="background">
|
|
||||||
<slot name="background"></slot>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
heading: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isMounted: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.isMounted = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$z-index-bottom: 5;
|
|
||||||
$z-index-top: 10;
|
|
||||||
|
|
||||||
.content-page {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: $site-menu__header-width 0 0 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
padding: 0 0 0 $site-menu__header-width;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
background-color: $color__neutral-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
z-index: $z-index-top;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
justify-content: stretch;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
padding: .5rem 1rem 2rem;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
width: 50%;
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
background-color: rgba(0, 0, 0, .5);
|
|
||||||
}
|
|
||||||
|
|
||||||
color: #fff; // TEMP
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-heading {
|
|
||||||
z-index: $z-index-top + 5;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
z-index: $z-index-bottom;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
opacity: .5;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.load-transition {
|
|
||||||
transition: 2s 2s;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
@at-root .is-mounted & {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,128 +0,0 @@
|
||||||
<template>
|
|
||||||
<ul class="gallery-featured" ref="galleryList">
|
|
||||||
<li v-for="(gallery, index) in galleries"
|
|
||||||
ref="galleries"
|
|
||||||
class="featured-image thumb-overlay selected-indicator"
|
|
||||||
:class="{ 'is-active': index === galleryActive }"
|
|
||||||
:style="{ 'background-image': 'url(' + gallery.featuredImage + ')' }"
|
|
||||||
:key="index"
|
|
||||||
@click="$emit('clicked', index)">
|
|
||||||
<span class="gallery-title">{{ gallery.title }}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
galleries: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
galleryActive: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default () {
|
|
||||||
return 0
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
if (this.galleryActive > 0) {
|
|
||||||
const el = this.$refs.galleries[this.galleryActive]
|
|
||||||
el.parentNode.scrollTop = el.offsetTop
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.featured-image {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $bp__layout) {
|
|
||||||
.featured-image {
|
|
||||||
height: calc(50vh - #{$site-menu__header-height / 2});
|
|
||||||
width: 100vw;
|
|
||||||
flex: 0 0 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-title {
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
max-width: 66%;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
|
|
||||||
font-size: 1.5em;
|
|
||||||
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-overlay {
|
|
||||||
$color: $color__neutral-100; // color of overlay
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color, .9),
|
|
||||||
rgba($color, .5),
|
|
||||||
rgba($color, .5)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
background: linear-gradient(
|
|
||||||
to left,
|
|
||||||
rgba($color, .4),
|
|
||||||
rgba($color, 0),
|
|
||||||
rgba($color, .4)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
.gallery-featured {
|
|
||||||
transition: opacity .3s; // TEMP
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.featured-image {
|
|
||||||
z-index: 10;
|
|
||||||
position: relative;
|
|
||||||
height: 0;
|
|
||||||
padding-top: 100%;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-title {
|
|
||||||
z-index: 15;
|
|
||||||
position: absolute;
|
|
||||||
top: 8px;
|
|
||||||
right: 8px;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 1em;
|
|
||||||
@include font-title();
|
|
||||||
color: $color__neutral-900;
|
|
||||||
|
|
||||||
@media (min-width: $bp__gallery-compact) {
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,328 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="image-viewer"
|
|
||||||
:class="{ 'is-visible': isVisible }">
|
|
||||||
<transition name="trans-bg-image">
|
|
||||||
<div v-if="backgroundImageUrl !== null"
|
|
||||||
:key="backgroundImageUrl"
|
|
||||||
class="viewer-background"
|
|
||||||
:style="backgroundStyle"
|
|
||||||
></div>
|
|
||||||
</transition>
|
|
||||||
<div class="image-container loading-container">
|
|
||||||
<transition name="trans-bg-image">
|
|
||||||
<span v-if="showLoading"
|
|
||||||
class="text-loading animation-pulse"
|
|
||||||
>
|
|
||||||
Loading
|
|
||||||
</span>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
<div class="image-container">
|
|
||||||
<transition name="trans-image" mode="out-in">
|
|
||||||
<img v-if="displayImageUrl !== null"
|
|
||||||
:key="displayImageUrl"
|
|
||||||
class="image image-shadow"
|
|
||||||
:src="displayImageUrl"
|
|
||||||
>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
<div class="close-viewer mobile-only"
|
|
||||||
@click="$emit('close')"
|
|
||||||
>
|
|
||||||
<SVGIcon />
|
|
||||||
</div>
|
|
||||||
<ThumbNav v-if="hasPrev"
|
|
||||||
class="thumb-nav thumb-nav--left mobile-only"
|
|
||||||
direction="left"
|
|
||||||
@navClick="$emit('clickPrev')"/>
|
|
||||||
<ThumbNav v-if="hasNext"
|
|
||||||
class="thumb-nav thumb-nav--right mobile-only"
|
|
||||||
direction="right"
|
|
||||||
@navClick="$emit('clickNext')"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ThumbNav from '@/components/ThumbNav'
|
|
||||||
import imageLoader from '~/mixins/imageLoader.js'
|
|
||||||
import SVGIcon from '@/assets/svg/close-wide.svg'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ThumbNav,
|
|
||||||
SVGIcon,
|
|
||||||
},
|
|
||||||
mixins: [ imageLoader ],
|
|
||||||
props: {
|
|
||||||
isVisible: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
imageUrl: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default () {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hasNext: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
hasPrev: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
loadingImageUrl: 'https://via.placeholder.com/120x120',
|
|
||||||
backgroundImageUrl: null, // blurred vwersion of image that makes up the background
|
|
||||||
displayImageUrl: null, // image being viewed
|
|
||||||
showLoading: true,
|
|
||||||
loadingTimeout: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
backgroundStyle () {
|
|
||||||
return {
|
|
||||||
backgroundImage: 'url(' + this.backgroundImageUrl + ')'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
imageUrl () {
|
|
||||||
this.setImages(this.imageUrl)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
this.setImages(this.imageUrl)
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setImages(url) {
|
|
||||||
this.displayImageUrl = null
|
|
||||||
this.showLoading = false
|
|
||||||
this.loadingTimeout = setTimeout(() => {
|
|
||||||
this.showLoading = true
|
|
||||||
}, 1000)
|
|
||||||
this.loadImage(this.imageUrl)
|
|
||||||
.then(img => {
|
|
||||||
this.displayImageUrl = img.src
|
|
||||||
clearTimeout(this.loadingTimeout)
|
|
||||||
this.showLoading = false
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.backgroundImageUrl = img.src
|
|
||||||
})
|
|
||||||
}, 200)
|
|
||||||
})
|
|
||||||
// TODO catch errors
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.image-viewer {
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
|
||||||
background-size: contain;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image {
|
|
||||||
position: absolute;
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
max-height: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-shadow {
|
|
||||||
box-shadow: 2px 4px 12px -2px rgba($color__neutral-200, .4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-loading {
|
|
||||||
font-size: 2rem;
|
|
||||||
color: $color__neutral-900;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animation-pulse {
|
|
||||||
animation: pulse 3s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $bp__layout) {
|
|
||||||
.close-viewer {
|
|
||||||
z-index: 10;
|
|
||||||
position: absolute;
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
top: calc(#{$site-menu__header-height} + 2px);
|
|
||||||
right: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
transition: opacity .3s .2s;
|
|
||||||
opacity: .2;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: .4;
|
|
||||||
animation: rotate .3s ease-in-out 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-viewer {
|
|
||||||
z-index: 50;
|
|
||||||
|
|
||||||
transition: opacity 1s; //TEMP
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
&.is-visible {
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background-position: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-nav {
|
|
||||||
position: absolute;
|
|
||||||
width: 50%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
|
|
||||||
opacity: .2;
|
|
||||||
|
|
||||||
&--left {
|
|
||||||
left: 0;
|
|
||||||
padding-right: 30%;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--right {
|
|
||||||
right: 0;
|
|
||||||
padding-left: 30%;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
.mobile-only {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-viewer {
|
|
||||||
padding: 1rem 1rem 1rem 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 8px;
|
|
||||||
left: calc(3rem + 8px);
|
|
||||||
right: $gallery-featured-width--compact;
|
|
||||||
bottom: 8px;
|
|
||||||
background-position: top center;
|
|
||||||
|
|
||||||
@media (min-width: $bp__gallery-compact) {
|
|
||||||
right: $gallery-featured-width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewer-background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-position: center center;
|
|
||||||
filter: blur(100px);
|
|
||||||
opacity: .3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-container {
|
|
||||||
padding-bottom: $gallery-thumbs-height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.trans-image {
|
|
||||||
&-enter-active {
|
|
||||||
transition: opacity 1s .4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-leave-active {
|
|
||||||
transition: opacity .5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter, &-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.trans-bg-image {
|
|
||||||
&-enter-active, &-leave-active {
|
|
||||||
transition: opacity .8s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter, &-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0%, 50%, 100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
80% {
|
|
||||||
opacity: 0.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rotate {
|
|
||||||
0%, 100% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
30% {
|
|
||||||
transform: rotate(3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
60% {
|
|
||||||
transform: rotate(-1deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
80% {
|
|
||||||
transform: rotate(1deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,285 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="gallery-page"
|
|
||||||
:class="{'is-mounted': isMounted }"
|
|
||||||
>
|
|
||||||
<GalleryImageViewer class="image-viewer"
|
|
||||||
ref="imageViewer"
|
|
||||||
:is-visible="imageViewerIsVisible"
|
|
||||||
:image-url="viewingImageUrl"
|
|
||||||
:has-next="activeImageIndex < galleries[activeGalleryIndex].images.length - 1"
|
|
||||||
:has-prev="activeImageIndex > 0"
|
|
||||||
@clickPrev="handleClickPrev"
|
|
||||||
@clickNext="handleClickNext"
|
|
||||||
@close="imageViewerIsVisible = false" />
|
|
||||||
<article class="gallery">
|
|
||||||
<h1 class="page-heading page-title"><span class="shadow-deco">{{ title }}</span></h1>
|
|
||||||
<GalleryFeatured class="gallery__featured load-transition load-transition--1"
|
|
||||||
:galleries="galleries"
|
|
||||||
:gallery-active="activeGalleryIndex"
|
|
||||||
@clicked="handleFeaturedClick" />
|
|
||||||
<GalleryThumbs class="gallery__thumbs load-transition load-transition--2"
|
|
||||||
:featured-height="featuredImageHeight"
|
|
||||||
:galleries="galleries"
|
|
||||||
:active-row="activeRow"
|
|
||||||
:active-index="activeImageIndex"
|
|
||||||
:show-image="showImage"
|
|
||||||
@thumbClick="handleThumbClick"/>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import GalleryFeatured from '@/components/GalleryFeatured'
|
|
||||||
import GalleryThumbs from '@/components/GalleryThumbs'
|
|
||||||
import GalleryImageViewer from '@/components/GalleryImageViewer'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
GalleryFeatured,
|
|
||||||
GalleryThumbs,
|
|
||||||
GalleryImageViewer,
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
galleries: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default () {
|
|
||||||
return 'My Galleries'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isMounted: false,
|
|
||||||
featuredImageHeight: '16rem',
|
|
||||||
imageViewerIsVisible: false,
|
|
||||||
activeRow: 0,
|
|
||||||
activeGalleryIndex: 0,
|
|
||||||
activeImageIndex: 0,
|
|
||||||
showImage: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
viewingImageUrl () {
|
|
||||||
if (
|
|
||||||
this.galleries[this.activeGalleryIndex]
|
|
||||||
&& this.galleries[this.activeGalleryIndex].images
|
|
||||||
&& this.galleries[this.activeGalleryIndex].images[this.activeImageIndex]
|
|
||||||
) {
|
|
||||||
return this.galleries[this.activeGalleryIndex].images[this.activeImageIndex].url
|
|
||||||
} else {
|
|
||||||
// TOOD return 404 page - does this do it?
|
|
||||||
throw({ statusCode: 404, message: 'Image Not Found' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
$route () {
|
|
||||||
const query = this.$route.query
|
|
||||||
if (!query || !query.gallery) { return }
|
|
||||||
this.$nextTick(() => {
|
|
||||||
const gallery = parseInt(query.gallery)
|
|
||||||
const image = parseInt(query.image) || 0
|
|
||||||
|
|
||||||
if (gallery !== this.activeGalleryIndex) {
|
|
||||||
this.activeRow = gallery
|
|
||||||
this.activeGalleryIndex = gallery
|
|
||||||
}
|
|
||||||
if (image !== this.activeImageIndex) {
|
|
||||||
this.activeImageIndex = image
|
|
||||||
this.showImage = image
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created () {
|
|
||||||
let query = this.$route.query
|
|
||||||
this.activeGalleryIndex = query.gallery ? parseInt(query.gallery) : 0
|
|
||||||
this.activeRow = this.activeGalleryIndex
|
|
||||||
this.activeImageIndex = query.image ? parseInt(query.image) : 0
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', () => { this.imageViewerIsVisible = false })
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.isMounted = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
handleFeaturedClick (index) {
|
|
||||||
this.activeRow = index
|
|
||||||
this.activeImageIndex = 0
|
|
||||||
this.activeGalleryIndex = index
|
|
||||||
this.setQueryString()
|
|
||||||
},
|
|
||||||
handleThumbClick(gallery, image) {
|
|
||||||
this.activeGalleryIndex = gallery
|
|
||||||
this.activeImageIndex = image
|
|
||||||
this.imageViewerIsVisible = true
|
|
||||||
this.setQueryString()
|
|
||||||
},
|
|
||||||
handleClickNext () {
|
|
||||||
this.activeImageIndex++;
|
|
||||||
},
|
|
||||||
handleClickPrev () {
|
|
||||||
this.activeImageIndex--;
|
|
||||||
},
|
|
||||||
setQueryString () {
|
|
||||||
this.$router.push({
|
|
||||||
path: this.$route.path,
|
|
||||||
query: {
|
|
||||||
gallery: this.activeGalleryIndex,
|
|
||||||
image: this.activeImageIndex,
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.gallery-page {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-viewer {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $bp__layout) {
|
|
||||||
.page-heading {
|
|
||||||
z-index: 5;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
font-size: 2rem;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-page {
|
|
||||||
padding-top: $site-menu__header-height;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery__thumbs {
|
|
||||||
z-index: 10;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery__featured {
|
|
||||||
width: 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-viewer {
|
|
||||||
font-size: 10em;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
.page-heading {
|
|
||||||
position: absolute;
|
|
||||||
font-size: 3.5rem;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
max-width: calc(100% - 3rem - #{$gallery-featured-width--compact});
|
|
||||||
|
|
||||||
@media (min-width: $bp__gallery-compact) {
|
|
||||||
max-width: calc(100% - 2rem - #{$gallery-featured-width});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-only {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-page {
|
|
||||||
padding-left: $site-menu__header-width;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery__featured {
|
|
||||||
order: 2;
|
|
||||||
flex: 0 0 $gallery-featured-width--compact;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
@media (min-width: $bp__gallery-compact) {
|
|
||||||
flex-basis: $gallery-featured-width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery__thumbs {
|
|
||||||
order: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-left: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery__nav {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-viewer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.load-transition {
|
|
||||||
transition: opacity 2s;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
&--1 {
|
|
||||||
transition-delay: 2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--2 {
|
|
||||||
transition-delay: 3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@at-root .is-mounted & {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,464 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="gallery-thumbs__spacer"></div>
|
|
||||||
<ul ref="galleryThumbsList"
|
|
||||||
class="gallery-thumbs__list list-shadow">
|
|
||||||
<li v-for="(gallery, galleryIndex) in galleries"
|
|
||||||
:key="galleryIndex"
|
|
||||||
:class="{ 'is-active': activeRow === galleryIndex }"
|
|
||||||
class="gallery-thumbs__row-container">
|
|
||||||
<ul ref="galleryThumbsRows"
|
|
||||||
class="gallery-thumbs__row"
|
|
||||||
:style="{ 'transform': 'translate3d(' + thumbRowOffsets[galleryIndex] + 'px, 0, 0)' }">
|
|
||||||
<li class="featured-image-spacer mobile-only"
|
|
||||||
:style="{ 'height': featuredHeight }">
|
|
||||||
<ThumbNav class="featured-nav mobile-only"
|
|
||||||
direction="right"
|
|
||||||
@navClick="handleNavClick(galleryIndex, 'right')" />
|
|
||||||
</li>
|
|
||||||
<li v-for="(image, imageIndex) in gallery.images"
|
|
||||||
:ref="galleryIndex == 0 && imageIndex == 0 ? 'thumbContainer' : null"
|
|
||||||
class="thumb-container thumb-overlay selected-indicator"
|
|
||||||
:class="{ 'is-active': imageIndex === activeIndex }"
|
|
||||||
:style="{ 'background-image': 'url(' + image.thumbUrl + ')' }"
|
|
||||||
:key="galleryIndex + '.' + imageIndex + '.' + image.url"
|
|
||||||
@click="$emit('thumbClick', galleryIndex, imageIndex)">
|
|
||||||
<ThumbNav class="thumb-nav thumb-nav--left mobile-only"
|
|
||||||
direction="left"
|
|
||||||
@navClick="handleNavClick(galleryIndex, 'left')" />
|
|
||||||
<ThumbNav v-if="imageIndex < gallery.images.length - 1"
|
|
||||||
class="thumb-nav mobile-only"
|
|
||||||
direction="right"
|
|
||||||
@navClick="handleNavClick(galleryIndex, 'right')" />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<ThumbNav class="thumb-nav thumb-nav--left mobile-hide shadow-deco"
|
|
||||||
:class="{ 'is-active': isLeftDesktopNavActive }"
|
|
||||||
direction="left"
|
|
||||||
@navClick="handleNavClickDesktop(activeRow, 'left')"/>
|
|
||||||
<ThumbNav class="thumb-nav thumb-nav--right mobile-hide shadow-deco"
|
|
||||||
:class="{ 'is-active': isRightDesktopNavActive }"
|
|
||||||
direction="right"
|
|
||||||
@navClick="handleNavClickDesktop(activeRow, 'right')"/>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ThumbNav from '@/components/ThumbNav'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ThumbNav
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
galleries: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
featuredHeight: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
activeRow: {
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
// index of image to be highlighted
|
|
||||||
activeIndex: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default () {
|
|
||||||
return 0
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// scroll this image into view
|
|
||||||
showImage: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default () {
|
|
||||||
return 0
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
thumbRowOffsets: new Array(this.galleries.length),
|
|
||||||
isLeftDesktopNavActive: false,
|
|
||||||
resizeIsThrottling: false,
|
|
||||||
isThumbsScrolling: false,
|
|
||||||
touchDownPosition: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
thumbsVerticalOffset () {
|
|
||||||
let rowHeight = this.$refs.thumbContainer ? this.$refs.thumbContainer[0].clientHeight : 0;
|
|
||||||
return rowHeight * this.activeRow
|
|
||||||
},
|
|
||||||
isRightDesktopNavActive () {
|
|
||||||
return this.thumbRowOffsets[this.activeRow] > 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
activeRow: function(val) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.updateElements(false)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
showImage () {
|
|
||||||
this.scrollToThumb(this.showImage)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', this.handleResize)
|
|
||||||
this.$refs.galleryThumbsList.addEventListener('touchstart', this.handleTouch, { passive: true })
|
|
||||||
this.$refs.galleryThumbsList.addEventListener('touchend', this.handleTouch, { passive: true })
|
|
||||||
this.$refs.galleryThumbsList.addEventListener('wheel', this.handleThumbsScroll, { passive: true })
|
|
||||||
// firefox
|
|
||||||
this.$refs.galleryThumbsList.addEventListener('mousewheel', this.handleThumbsScroll, { passive: true })
|
|
||||||
this.$nextTick(function () {
|
|
||||||
this.updateElements()
|
|
||||||
this.scrollToThumb(this.activeIndex)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// TOOD move logic of getting sizes of elements into getter methods
|
|
||||||
scrollToThumb (thumbIndex) {
|
|
||||||
if (thumbIndex >= this.galleries[this.activeRow].images.length || thumbIndex < 0) { return }
|
|
||||||
const containerWidth = this.$refs.galleryThumbsList.clientWidth
|
|
||||||
const thumbWidth = this.$refs.thumbContainer[0].clientWidth
|
|
||||||
let offset = this.thumbRowOffsets[this.activeRow]
|
|
||||||
const thumbPosition = thumbWidth * (this.galleries[this.activeRow].images.length - thumbIndex)
|
|
||||||
const rowWidth = this.$refs.galleryThumbsRows[this.activeRow].clientWidth
|
|
||||||
|
|
||||||
if (containerWidth < thumbPosition - offset) {
|
|
||||||
this.$set(this.thumbRowOffsets, this.activeRow, thumbPosition - containerWidth + 1)
|
|
||||||
this.isLeftDesktopNavActive = containerWidth + offset > rowWidth
|
|
||||||
} else if (thumbPosition <= offset) {
|
|
||||||
this.$set(this.thumbRowOffsets, this.activeRow, thumbPosition - thumbWidth)
|
|
||||||
this.isLeftDesktopNavActive = containerWidth + offset > rowWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
updateElements (resetOffsets=true) {
|
|
||||||
if (resetOffsets) {this.resetOffsets()}
|
|
||||||
if (this.$refs.galleryThumbsRows[this.activeRow] && this.$refs.galleryThumbsList) {
|
|
||||||
let totalRowWidth = this.$refs.galleryThumbsRows[this.activeRow].clientWidth
|
|
||||||
let visibleRowWidth = this.$refs.galleryThumbsList.clientWidth
|
|
||||||
this.initLeftNav(totalRowWidth, visibleRowWidth)
|
|
||||||
if (visibleRowWidth > totalRowWidth) {
|
|
||||||
this.centerThumbs(totalRowWidth, visibleRowWidth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initLeftNav (totalRowWidth, visibleRowWidth) {
|
|
||||||
let offset = this.thumbRowOffsets[this.activeRow]
|
|
||||||
let maxOffset = totalRowWidth - visibleRowWidth
|
|
||||||
this.isLeftDesktopNavActive = (totalRowWidth > visibleRowWidth)
|
|
||||||
&& (offset < maxOffset)
|
|
||||||
},
|
|
||||||
resetOffsets() {
|
|
||||||
for (let index = 0; index < this.thumbRowOffsets.length; index++) {
|
|
||||||
this.$set(this.thumbRowOffsets, index, 0)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
centerThumbs(rowWidth, visibleWidth) {
|
|
||||||
let offset = (visibleWidth - rowWidth) / -2
|
|
||||||
this.$set(this.thumbRowOffsets, [this.activeRow], offset)
|
|
||||||
},
|
|
||||||
handleNavClick(source, direction) {
|
|
||||||
let offset = this.thumbRowOffsets[source]
|
|
||||||
const thumbContainerWidth = this.$refs.thumbContainer[0].clientWidth
|
|
||||||
if (direction === 'left') {
|
|
||||||
offset += thumbContainerWidth
|
|
||||||
} else if (direction === 'right') {
|
|
||||||
offset -= thumbContainerWidth
|
|
||||||
}
|
|
||||||
offset = offset > 0 ? 0 : offset
|
|
||||||
this.$set(this.thumbRowOffsets, source, offset)
|
|
||||||
},
|
|
||||||
handleNavClickDesktop(source, direction) {
|
|
||||||
let offset = this.thumbRowOffsets[source]
|
|
||||||
let thumbContainerWidth = this.$refs.thumbContainer[0].clientWidth
|
|
||||||
let visibleRowWidth = this.$refs.galleryThumbsList.clientWidth
|
|
||||||
let totalRowWidth = this.$refs.galleryThumbsRows[source].clientWidth
|
|
||||||
let maxOffset = totalRowWidth - visibleRowWidth
|
|
||||||
if (direction === 'left') {
|
|
||||||
offset += thumbContainerWidth
|
|
||||||
} else if (direction === 'right') {
|
|
||||||
offset -= thumbContainerWidth
|
|
||||||
if (offset != 0) {
|
|
||||||
this.isLeftDesktopNavActive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (offset < 0) {
|
|
||||||
offset = 0
|
|
||||||
} else if (offset >= maxOffset) {
|
|
||||||
offset = maxOffset
|
|
||||||
this.isLeftDesktopNavActive = false
|
|
||||||
}
|
|
||||||
this.$set(this.thumbRowOffsets, source, offset)
|
|
||||||
},
|
|
||||||
handleResize() {
|
|
||||||
if (!this.resizeIsThrottling) {
|
|
||||||
this.resizeIsThrottling = true
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.updateElements()
|
|
||||||
this.resizeIsThrottling = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleThumbsScroll(event) {
|
|
||||||
if (event.wheelDeltaX) {
|
|
||||||
if (event.wheelDeltaX > 0) {
|
|
||||||
this.doThumbScroll('right')
|
|
||||||
} else if (event.wheelDeltaX < 0) {
|
|
||||||
this.doThumbScroll('left')
|
|
||||||
}
|
|
||||||
} else if (event.deltaY) {
|
|
||||||
if (event.deltaY > 0) {
|
|
||||||
this.doThumbScroll('left')
|
|
||||||
} else if (event.deltaY < 0) {
|
|
||||||
this.doThumbScroll('right')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleTouch(event) {
|
|
||||||
if (event.type === 'touchstart') {
|
|
||||||
this.touchDownPosition = event.changedTouches[0].clientX
|
|
||||||
} else if (event.type === 'touchmove') {
|
|
||||||
event.preventDefault()
|
|
||||||
} else if (event.type === 'touchend') {
|
|
||||||
let xPos = event.changedTouches[0].clientX
|
|
||||||
let direction = this.touchDownPosition - xPos < 0 ? 'left' : 'right'
|
|
||||||
this.doThumbScroll(direction)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
doThumbScroll(direction) {
|
|
||||||
if (this.isThumbsScrolling) { return }
|
|
||||||
|
|
||||||
this.isThumbsScrolling = true
|
|
||||||
if ((direction === 'left' && this.isLeftDesktopNavActive) || (direction === 'right' && this.isRightDesktopNavActive)) {
|
|
||||||
this.handleNavClickDesktop(this.activeRow, direction)
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.isThumbsScrolling = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.gallery-thumbs__row {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-container {
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center center;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
padding: 0 2px;
|
|
||||||
background-clip: content-box;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $bp__layout) {
|
|
||||||
.gallery-thumbs__spacer {
|
|
||||||
display:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-thumbs__row {
|
|
||||||
position: relative;
|
|
||||||
transition : transform .5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.featured-image-spacer {
|
|
||||||
position: relative;
|
|
||||||
height: calc(50vh - #{$site-menu__header-height / 2}) !important; // must override inline style set with prop
|
|
||||||
width: 100vw;
|
|
||||||
flex: 0 0 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-container {
|
|
||||||
position: relative;
|
|
||||||
height: calc(50vh - #{$site-menu__header-height / 2});
|
|
||||||
width: 100vw;
|
|
||||||
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.featured-nav {
|
|
||||||
z-index: 20;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
justify-content: flex-end;
|
|
||||||
padding-left: 80%;
|
|
||||||
padding-right: 16px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
|
|
||||||
opacity: .3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-nav {
|
|
||||||
z-index: 20;
|
|
||||||
position: absolute;
|
|
||||||
width: 8rem;
|
|
||||||
height: 8rem;
|
|
||||||
bottom: 1rem;
|
|
||||||
right: 8px;
|
|
||||||
padding: 0 1rem 0 3rem;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
|
|
||||||
opacity: .4;
|
|
||||||
|
|
||||||
&--left {
|
|
||||||
left: 8px;
|
|
||||||
right: auto;
|
|
||||||
padding: 0 3rem 0 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
.mobile-only {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-hide {
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.featured-image-spacer {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-container {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
height: $gallery-thumbs-height--compact;
|
|
||||||
width: $gallery-thumbs-height--compact * 1.6;
|
|
||||||
flex: 0 0 $gallery-thumbs-height--compact * 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery__thumbs {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-thumbs__list {
|
|
||||||
position: relative;
|
|
||||||
height: $gallery-thumbs-height--compact;
|
|
||||||
flex: 0 0 $gallery-thumbs-height--compact;
|
|
||||||
bottom: 8px;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
transition: opacity .3s; // TEMP
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-shadow {
|
|
||||||
$color: $color__neutral-100;
|
|
||||||
box-shadow: 0 -3px 33px -9px $color, 0 0 8px -3px $color;
|
|
||||||
background-color: rgba($color, .7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-thumbs__row-container {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
transition: opacity .5s;
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
&.is-active {
|
|
||||||
transition: opacity .5s .3s;
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-thumbs__row {
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
transition: transform .5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-nav {
|
|
||||||
position: absolute;
|
|
||||||
top: 1rem;
|
|
||||||
width: $gallery-thumbs-height--compact - 2rem;
|
|
||||||
height: $gallery-thumbs-height--compact - 2rem;
|
|
||||||
|
|
||||||
transition: opacity .5s;
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
&.is-active {
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--left {
|
|
||||||
left: -.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--right {
|
|
||||||
right: -.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__gallery-compact) {
|
|
||||||
.gallery-thumbs__list {
|
|
||||||
height: $gallery-thumbs-height;
|
|
||||||
flex: 0 0 $gallery-thumbs-height;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-container {
|
|
||||||
height: $gallery-thumbs-height;
|
|
||||||
width: $gallery-thumbs-height * 1.6;
|
|
||||||
flex: 0 0 $gallery-thumbs-height * 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-thumbs__list {
|
|
||||||
height: $gallery-thumbs-height;
|
|
||||||
flex: 0 0 $gallery-thumbs-height;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumb-nav {
|
|
||||||
width: $gallery-thumbs-height - 2rem;
|
|
||||||
height: $gallery-thumbs-height - 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,79 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="VueToNuxtLogo">
|
|
||||||
<div class="Triangle Triangle--two"/>
|
|
||||||
<div class="Triangle Triangle--one"/>
|
|
||||||
<div class="Triangle Triangle--three"/>
|
|
||||||
<div class="Triangle Triangle--four"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.VueToNuxtLogo {
|
|
||||||
display: inline-block;
|
|
||||||
animation: turn 2s linear forwards 1s;
|
|
||||||
transform: rotateX(180deg);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
height: 180px;
|
|
||||||
width: 245px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Triangle {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Triangle--one {
|
|
||||||
border-left: 105px solid transparent;
|
|
||||||
border-right: 105px solid transparent;
|
|
||||||
border-bottom: 180px solid #41b883;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Triangle--two {
|
|
||||||
top: 30px;
|
|
||||||
left: 35px;
|
|
||||||
animation: goright 0.5s linear forwards 3.5s;
|
|
||||||
border-left: 87.5px solid transparent;
|
|
||||||
border-right: 87.5px solid transparent;
|
|
||||||
border-bottom: 150px solid #3b8070;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Triangle--three {
|
|
||||||
top: 60px;
|
|
||||||
left: 35px;
|
|
||||||
animation: goright 0.5s linear forwards 3.5s;
|
|
||||||
border-left: 70px solid transparent;
|
|
||||||
border-right: 70px solid transparent;
|
|
||||||
border-bottom: 120px solid #35495e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Triangle--four {
|
|
||||||
top: 120px;
|
|
||||||
left: 70px;
|
|
||||||
animation: godown 0.5s linear forwards 3s;
|
|
||||||
border-left: 35px solid transparent;
|
|
||||||
border-right: 35px solid transparent;
|
|
||||||
border-bottom: 60px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes turn {
|
|
||||||
100% {
|
|
||||||
transform: rotateX(0deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes godown {
|
|
||||||
100% {
|
|
||||||
top: 180px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes goright {
|
|
||||||
100% {
|
|
||||||
left: 70px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,77 +0,0 @@
|
||||||
<template>
|
|
||||||
<article class="is-mounted no-content-page no-content-bg">
|
|
||||||
<h1 class="no-content-heading page-title">{{ heading }}</h1>
|
|
||||||
<p class="no-content-text">{{ message }}</p>
|
|
||||||
</article>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'NoContent',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
heading: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default () {
|
|
||||||
return 'This page blah blah ...'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.no-content-page {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
padding: $site-menu__header-height * 2 1rem 0;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
padding: 0 0 0 $site-menu__header-width;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-content-bg {
|
|
||||||
$color-inner: $color__primary-500;
|
|
||||||
$color-outer: $color__primary-100;
|
|
||||||
$transparency-outer: .1;
|
|
||||||
$transparency-inner: .05;
|
|
||||||
|
|
||||||
background-color: rgba($color-inner, .1);
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color-inner, $transparency-inner),
|
|
||||||
rgba($color-outer, $transparency-outer)
|
|
||||||
);
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
background:
|
|
||||||
linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color-outer, $transparency-outer),
|
|
||||||
rgba($color-inner, $transparency-inner),
|
|
||||||
rgba($color-outer, $transparency-outer)
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba($color-outer, $transparency-outer),
|
|
||||||
rgba($color-inner, $transparency-inner),
|
|
||||||
rgba($color-outer, $transparency-outer)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-content-heading {
|
|
||||||
margin-bottom: .25em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# COMPONENTS
|
|
||||||
|
|
||||||
**This directory is not required, you can delete it if you don't want to use it.**
|
|
||||||
|
|
||||||
The components directory contains your Vue.js Components.
|
|
||||||
|
|
||||||
_Nuxt.js doesn't supercharge these components._
|
|
|
@ -1,629 +0,0 @@
|
||||||
<template>
|
|
||||||
<section :class="{ 'is-open': isOpen,
|
|
||||||
'is-mounted': isMounted
|
|
||||||
}"
|
|
||||||
class="menu-drawer menu-layout"
|
|
||||||
>
|
|
||||||
<div class="menu-header__item menu-toggle"
|
|
||||||
@click="$emit('toggleMenu')"
|
|
||||||
>
|
|
||||||
<div class="menu-bars"></div>
|
|
||||||
</div>
|
|
||||||
<div class="menu-content">
|
|
||||||
<div class="menu-close"
|
|
||||||
@click="$emit('closeMenu')"
|
|
||||||
>
|
|
||||||
<span class="menu-close__content">close</span>
|
|
||||||
</div>
|
|
||||||
<nav class="menu-content__body">
|
|
||||||
<ul class="site-nav">
|
|
||||||
<li class="site-nav__item"
|
|
||||||
v-for="item in siteNav"
|
|
||||||
:key="item.to"
|
|
||||||
@click="$emit('closeMenu')"
|
|
||||||
>
|
|
||||||
<div v-if="item.bgImgUrl"
|
|
||||||
class="menu-background menu-link-background"
|
|
||||||
:style="{ 'background-image': loadMenuImages ? `url(${item.bgImgUrl})` : 'none' }"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<nuxt-link class="site-nav__link"
|
|
||||||
:to="item.to"
|
|
||||||
:exact="item.to === '/'"
|
|
||||||
>
|
|
||||||
{{ item.text }}
|
|
||||||
</nuxt-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul class="site-nav__footer social-nav">
|
|
||||||
<li v-for="item in socialNav"
|
|
||||||
class="social-nav-item"
|
|
||||||
:key="item.to"
|
|
||||||
>
|
|
||||||
<div class="menu-background social-background">
|
|
||||||
<b-icon class="social-background__icon"
|
|
||||||
:icon="item.icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<a class="social-nav__link"
|
|
||||||
:href="item.to"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<b-icon class="social-nav__icon"
|
|
||||||
:icon="item.icon"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<footer class="menu-content__footer">
|
|
||||||
<p class="footer-attr">Copyright © {{ new Date().getFullYear() }} Marc Leopold Photography</p>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<div class="menu-header"
|
|
||||||
@click="$emit('toggleMenu')"
|
|
||||||
>
|
|
||||||
<div class="menu-header__inner">
|
|
||||||
<span class="menu-header__item site-title">Marc Leopold Photography</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
isOpen: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isMounted: false, //component has loaded
|
|
||||||
loadMenuImages: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
siteNav () {
|
|
||||||
return this.$store.getters['navigation/siteNav']
|
|
||||||
},
|
|
||||||
|
|
||||||
socialNav () {
|
|
||||||
return this.$store.getters['navigation/socialNav']
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
const mq = window.matchMedia("(min-width: 40em)")
|
|
||||||
this.loadMenuImages = mq.matches
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.isMounted = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$transition-timing: .5s;
|
|
||||||
|
|
||||||
.menu-drawer {
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
right: 0;
|
|
||||||
top: -100%;
|
|
||||||
|
|
||||||
font-size: 1rem;
|
|
||||||
|
|
||||||
transition: transform $transition-timing;
|
|
||||||
transform: translate3d(0, $site-menu__header-width, 0);
|
|
||||||
|
|
||||||
&.is-open {
|
|
||||||
transform: translate3d(0, 100%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
width: $site-menu__width;
|
|
||||||
top: 0;
|
|
||||||
right: 100%;
|
|
||||||
transform: translate3d($site-menu__header-width, 0, 0);
|
|
||||||
|
|
||||||
&.is-open {
|
|
||||||
transform: translate3d(100%, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-layout {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-content {
|
|
||||||
$color-bg: $site-menu__color-bg;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
padding: .5rem;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
background-color: $site-menu__color-bg;
|
|
||||||
background: linear-gradient(
|
|
||||||
to right,
|
|
||||||
darken($color-bg, 2%),
|
|
||||||
$color-bg,
|
|
||||||
darken($color-bg, 2%)
|
|
||||||
);
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
padding: .5rem 1rem;
|
|
||||||
background: linear-gradient(
|
|
||||||
to top,
|
|
||||||
darken($color-bg, 2%),
|
|
||||||
$color-bg,
|
|
||||||
darken($color-bg, 2%)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-content__body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1 0 auto;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
align-items: normal;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-content__footer {
|
|
||||||
position: relative;
|
|
||||||
z-index: 10;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1em 0 0;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-header {
|
|
||||||
$color-bg: $color__neutral-300;
|
|
||||||
$color-shade: darken($color__neutral-300, 5);
|
|
||||||
// $color-shade: $color__neutral-200;
|
|
||||||
|
|
||||||
z-index: 20;
|
|
||||||
position: relative;
|
|
||||||
flex: 0 0 $site-menu__header-height;
|
|
||||||
|
|
||||||
color: $color__neutral-900;
|
|
||||||
background-color: $color-bg;
|
|
||||||
background: linear-gradient(
|
|
||||||
to right,
|
|
||||||
$color-bg,
|
|
||||||
$color-shade
|
|
||||||
);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
max-width: $site-menu__header-width;
|
|
||||||
background: linear-gradient(
|
|
||||||
to top,
|
|
||||||
$color-shade,
|
|
||||||
$color-bg,
|
|
||||||
$color-bg,
|
|
||||||
$color-shade
|
|
||||||
);
|
|
||||||
opacity: .97;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-header__inner {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
padding: 0 $site-menu__header-width 0 1rem;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 1.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 100%;
|
|
||||||
width: 100vh;
|
|
||||||
height: $site-menu__header-width;
|
|
||||||
|
|
||||||
transform-origin: 100% 0;
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-title {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
line-height: 1.1;
|
|
||||||
|
|
||||||
transition: opacity 2s 1s;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
@at-root .is-mounted & {
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include font-title($weight: 400);
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
font-size: 1.0rem;
|
|
||||||
letter-spacing: 1.3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-close {
|
|
||||||
z-index: 30;
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
@include font-title();
|
|
||||||
text-transform: lowercase;
|
|
||||||
cursor: pointer;
|
|
||||||
color: $color__neutral-800;
|
|
||||||
|
|
||||||
transition: opacity 0 $transition-timing;
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
@at-root .menu.is-open & {
|
|
||||||
transition: opacity 1s $transition-timing + .2s;
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $bp__layout - .01em) {
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-close__content {
|
|
||||||
display: block;
|
|
||||||
padding: .5rem .75rem 1rem 1rem;
|
|
||||||
|
|
||||||
transition: opacity .2s;
|
|
||||||
opacity: .6;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-nav {
|
|
||||||
font-size: 1.4em;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
font-size: .8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-nav__item {
|
|
||||||
z-index: 1;
|
|
||||||
text-transform: uppercase;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-nav__link {
|
|
||||||
z-index: 5;
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
padding: .2em 0;
|
|
||||||
|
|
||||||
color: $color__neutral-800;
|
|
||||||
transition: opacity .5s;
|
|
||||||
opacity: .9;
|
|
||||||
|
|
||||||
&:link, &:visited {
|
|
||||||
opacity: .9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover, &:active {
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.nuxt-link-active {
|
|
||||||
pointer-events: none;
|
|
||||||
color: $color__neutral-900;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
padding: .5em 0;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
$size: .2rem;
|
|
||||||
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
height: $size;
|
|
||||||
width: $size;
|
|
||||||
right: 100%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
margin-top: .02rem;
|
|
||||||
margin-right: .3rem;
|
|
||||||
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
background-color: $color__neutral-600;
|
|
||||||
|
|
||||||
transition: opacity .5s;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.nuxt-link-active::before {
|
|
||||||
transition: opacity 1s .5s;
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-nav__footer {
|
|
||||||
margin-top: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-nav {
|
|
||||||
z-index: 10;
|
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-nav__link {
|
|
||||||
display: block;
|
|
||||||
height: 2em;
|
|
||||||
width: 5ch;
|
|
||||||
|
|
||||||
transition: opacity .5s;
|
|
||||||
color: $color__neutral-900;
|
|
||||||
|
|
||||||
&:link, &:visited {
|
|
||||||
opacity: .8;
|
|
||||||
color: $color__neutral-900;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover, &:active {
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
width: 3ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-nav__icon.icon {
|
|
||||||
justify-content: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-attr {
|
|
||||||
color: $color__neutral-600;
|
|
||||||
font-size: .7em;
|
|
||||||
@include font-body(600);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-toggle {
|
|
||||||
$width: 2rem;
|
|
||||||
$margin: ($site-menu__header-width - $width) / 2;
|
|
||||||
|
|
||||||
z-index: 30;
|
|
||||||
position: absolute;
|
|
||||||
width: $width;
|
|
||||||
height: $width;
|
|
||||||
right: $margin;
|
|
||||||
bottom: $margin;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
transition: opacity .2s;
|
|
||||||
opacity: .6;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
bottom: auto;
|
|
||||||
top: $margin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-bars {
|
|
||||||
$color: $color__neutral-900;
|
|
||||||
$width: 4px;
|
|
||||||
$padding: 4px; // padding on top/bottom
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
top: $padding;
|
|
||||||
bottom: $padding;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
border-top: $width solid $color;
|
|
||||||
border-bottom: $width solid $color;
|
|
||||||
|
|
||||||
transition: transform .3s .2s ease-out;
|
|
||||||
transform: scale(1, .7);
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: $width;
|
|
||||||
left: 0;
|
|
||||||
top: calc(50% - #{$width / 2});
|
|
||||||
|
|
||||||
background-color: $color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@at-root .menu.is-open & {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-background {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
.menu-background {
|
|
||||||
z-index: 1;
|
|
||||||
display: initial;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
right: $site-menu__header-width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-link-background {
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center center;
|
|
||||||
|
|
||||||
transition: opacity .5s;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
$color: $site-menu__color-bg; //color of gradient overlay
|
|
||||||
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color, 0),
|
|
||||||
rgba($color, .7),
|
|
||||||
rgba($color, .7),
|
|
||||||
rgba($color, 0)
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba($color, 1),
|
|
||||||
rgba($color, .2) 50%,
|
|
||||||
rgba($color, 0) 90%,
|
|
||||||
rgba($color, 1)
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color, 1),
|
|
||||||
rgba($color, 0) 20%,
|
|
||||||
rgba($color, 0) 80%,
|
|
||||||
rgba($color, 1) 90%,
|
|
||||||
rgba($color, 1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-nav__item:hover .menu-link-background {
|
|
||||||
transition: opacity 1s;
|
|
||||||
opacity: .3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-background {
|
|
||||||
filter: blur(3px);
|
|
||||||
transition: opacity .5s;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-background__icon {
|
|
||||||
position: absolute;
|
|
||||||
align-items: flex-end;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-nav-item:hover .social-background {
|
|
||||||
transition: opacity 1s;
|
|
||||||
opacity: .15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
.menu-content .social-nav__icon i::before {
|
|
||||||
font-size: 1.1em;
|
|
||||||
filter: blur(20xp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-background__icon i.mdi {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
font-size: 19em;
|
|
||||||
margin-bottom: -.05em;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
margin-bottom: -.19em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($site-menu__color-bg, .8),
|
|
||||||
rgba($site-menu__color-bg, 0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,73 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="wrapper"
|
|
||||||
@click.stop="$emit('navClick')"
|
|
||||||
>
|
|
||||||
<div class="svg-container"
|
|
||||||
:class="{ 'is-reversed': direction === 'right' }"
|
|
||||||
>
|
|
||||||
<SVGIcon class="svg-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SVGIcon from '@/assets/svg/chevron-left.svg'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
SVGIcon,
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
direction: {
|
|
||||||
type: String,
|
|
||||||
default: function () {
|
|
||||||
return 'right'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.svg-container {
|
|
||||||
transition: opacity .3s, transform .5s;
|
|
||||||
opacity: .6;
|
|
||||||
transform: none;
|
|
||||||
|
|
||||||
@at-root .wrapper:hover & {
|
|
||||||
opacity: .6;
|
|
||||||
transform: translate3d(-3px, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@at-root .wrapper:hover &.is-reversed {
|
|
||||||
transform: translate3d(3px, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.svg-icon {
|
|
||||||
width: 100%;
|
|
||||||
min-width: 4rem;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 4rem;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-reversed .svg-icon {
|
|
||||||
transform: scaleX(-1);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,7 +0,0 @@
|
||||||
# LAYOUTS
|
|
||||||
|
|
||||||
**This directory is not required, you can delete it if you don't want to use it.**
|
|
||||||
|
|
||||||
This directory contains your Application Layouts.
|
|
||||||
|
|
||||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).
|
|
|
@ -1,97 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="page-container">
|
|
||||||
<div class="page">
|
|
||||||
<div class="page-overlay"
|
|
||||||
:class="{ 'is-active': isMenuOpen }"
|
|
||||||
@click="closeMenu"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<nuxt />
|
|
||||||
</div>
|
|
||||||
<SiteMenu class="menu"
|
|
||||||
:is-open="isMenuOpen"
|
|
||||||
@toggleMenu="toggleMenu"
|
|
||||||
@closeMenu="closeMenu"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SiteMenu from '~/components/SiteMenu'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
SiteMenu
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isMenuOpen: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggleMenu () {
|
|
||||||
this.isMenuOpen = !this.isMenuOpen
|
|
||||||
},
|
|
||||||
closeMenu () {
|
|
||||||
this.isMenuOpen = false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.page-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page {
|
|
||||||
z-index: $z-index__page;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-overlay {
|
|
||||||
$color: $color__primary-100;
|
|
||||||
z-index: $z-index__page-overlay;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
background-color: rgba($color, .6);
|
|
||||||
background: linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba($color, 1),
|
|
||||||
rgba($color, 1) $site-menu__width + 3rem,
|
|
||||||
rgba($color, .8),
|
|
||||||
rgba($color, 1)
|
|
||||||
);
|
|
||||||
box-shadow: 0 0 70px 40px $color inset;
|
|
||||||
|
|
||||||
transition: opacity .5s .2s;
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
&.is-active {
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
z-index: $z-index__menu;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,73 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="error-page">
|
|
||||||
<h1 class="heading" v-if="error.statusCode === 404">Page not found!</h1>
|
|
||||||
<h1 class="heading" v-else>An error occurred!</h1>
|
|
||||||
<nav>
|
|
||||||
<ul class="site-nav">
|
|
||||||
<li class="site-nav__item"
|
|
||||||
v-for="item in siteNav"
|
|
||||||
:key="item.to"
|
|
||||||
@click="$emit('closeMenu')"
|
|
||||||
>
|
|
||||||
<nuxt-link :to="item.to" >
|
|
||||||
{{ item.text }}
|
|
||||||
</nuxt-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
error: {
|
|
||||||
type: Object,
|
|
||||||
default: function () {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
siteNav () {
|
|
||||||
return this.$store.getters['navigation/siteNav']
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.error-page {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
padding: calc(1rem + #{$site-menu__header-height}) 1rem;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
padding: 6rem 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
font-size: 2rem;
|
|
||||||
color: $color__accent-danger-700;
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-nav {
|
|
||||||
font-size: 1.3rem;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-nav__item {
|
|
||||||
margin-bottom: .2em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
# MIDDLEWARE
|
|
||||||
|
|
||||||
**This directory is not required, you can delete it if you don't want to use it.**
|
|
||||||
|
|
||||||
This directory contains your application middleware.
|
|
||||||
The middleware lets you define custom function to be ran before rendering a page or a group of pages (layouts).
|
|
||||||
|
|
||||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).
|
|
|
@ -1,16 +0,0 @@
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
loadImage(url) {
|
|
||||||
return new Promise( (resolve, reject) => {
|
|
||||||
const img = new Image()
|
|
||||||
|
|
||||||
img.addEventListener('load', e => resolve(img));
|
|
||||||
img.addEventListener('error', () => {
|
|
||||||
reject(new Error(`Failed to load image URL: ${url}`));
|
|
||||||
});
|
|
||||||
|
|
||||||
img.src = url;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
131
nuxt.config.js
|
@ -1,131 +0,0 @@
|
||||||
const pkg = require('./package')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
mode: 'universal',
|
|
||||||
|
|
||||||
server: {
|
|
||||||
port: 3003,
|
|
||||||
host: '0.0.0.0'
|
|
||||||
},
|
|
||||||
serverMiddleware: [
|
|
||||||
'~/api/contact',
|
|
||||||
],
|
|
||||||
/*
|
|
||||||
** Headers of the page
|
|
||||||
*/
|
|
||||||
head: {
|
|
||||||
title: pkg.name,
|
|
||||||
titleTemplate: 'Marc Leopold | %s',
|
|
||||||
meta: [
|
|
||||||
{ charset: 'utf-8' },
|
|
||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
|
||||||
|
|
||||||
{ hid: 'description', name: 'description', content: 'Photographer, Marc Leopold has images in numerous collections and publications. Here is a glimpse of his work, an insight into his philosophy and motivations.' },
|
|
||||||
|
|
||||||
{ property: 'og:image', content: 'https://marcleopold.isnet.uk/img/open-graph/marc-leopold-ss.png'},
|
|
||||||
{ property: 'og:image:width', content: '1200'},
|
|
||||||
{ property: 'og:image:height', content: '600'},
|
|
||||||
{ property: 'og:image:type', content: 'image/png' },
|
|
||||||
{ property: 'og:title', content: 'Marc Leopold Photography' },
|
|
||||||
{ property: 'og:url', content: 'https://marcleopold.isnet.uk' },
|
|
||||||
{ property: 'og:site_name', content: 'Marc Leopold Photography' },
|
|
||||||
{ property: 'og:type', content: 'website'},
|
|
||||||
{ property: 'og:description', content: 'Marc Leopold is a Chicago born photographer whose images have appeared in many collections and publications. His website is a showcase of his outstanding work and an insight into his philosophy and motivations.' },
|
|
||||||
{ name: 'twitter:card', content: 'summary' },
|
|
||||||
{ name: 'twitter:title', content: 'Marc Leopold Photography' },
|
|
||||||
{ name: 'twitter:creator', content: '@studiovxweb' },
|
|
||||||
],
|
|
||||||
link: [
|
|
||||||
{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' },
|
|
||||||
{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' },
|
|
||||||
{ rel: 'icon', type: 'image/png', sizes: '96x96', href: '/favicon-96x96.png' },
|
|
||||||
{ rel: 'stylesheet',
|
|
||||||
href: 'https://fonts.googleapis.com/css?family=' +
|
|
||||||
'Montserrat:400,600|' +
|
|
||||||
'Raleway:400,600|' +
|
|
||||||
'Satisfy'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Customize the progress-bar color
|
|
||||||
*/
|
|
||||||
loading: {
|
|
||||||
color: '#fff',
|
|
||||||
height: '1px',
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Global CSS
|
|
||||||
*/
|
|
||||||
css: [
|
|
||||||
'@/assets/scss/style.scss'
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Plugins to load before mounting the App
|
|
||||||
*/
|
|
||||||
plugins: [
|
|
||||||
{ src: '~/plugins/Vuelidate' }
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Nuxt.js modules
|
|
||||||
*/
|
|
||||||
modules: [
|
|
||||||
// Doc: https://github.com/nuxt-community/axios-module#usage
|
|
||||||
'@nuxtjs/axios',
|
|
||||||
// Doc: https://buefy.github.io/#/documentation
|
|
||||||
'nuxt-buefy',
|
|
||||||
[
|
|
||||||
'nuxt-sass-resources-loader',
|
|
||||||
[
|
|
||||||
'@/assets/scss/_globals.scss'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'@nuxtjs/proxy',
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Axios module configuration
|
|
||||||
*/
|
|
||||||
axios: {
|
|
||||||
// See https://github.com/nuxt-community/axios-module#options
|
|
||||||
proxy: true,
|
|
||||||
baseURL: 'http://192.168.0.5:3003',
|
|
||||||
debug: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
proxy: {
|
|
||||||
'/api/v1/': 'http://192.168.0.5:8101'
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Build configuration
|
|
||||||
*/
|
|
||||||
build: {
|
|
||||||
/*
|
|
||||||
** You can extend webpack config here
|
|
||||||
*/
|
|
||||||
extend(config, ctx) {
|
|
||||||
// vue-svg-loader
|
|
||||||
const svgRule = config.module.rules.find(rule => rule.test.test('.svg'))
|
|
||||||
svgRule.test = /\.(png|jpe?g|gif|webp)$/
|
|
||||||
config.module.rules.push({
|
|
||||||
test: /\.svg$/,
|
|
||||||
loader: 'vue-svg-loader',
|
|
||||||
})
|
|
||||||
|
|
||||||
// Run ESLint on save
|
|
||||||
if (ctx.isDev && ctx.isClient) {
|
|
||||||
config.module.rules.push({
|
|
||||||
enforce: 'pre',
|
|
||||||
test: /\.(js|vue)$/,
|
|
||||||
loader: 'eslint-loader',
|
|
||||||
exclude: /(node_modules)/
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
38
package.json
|
@ -1,38 +0,0 @@
|
||||||
{
|
|
||||||
"name": "MarcLeopold",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Marc Leopold Website",
|
|
||||||
"author": "studio v/x",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "nuxt",
|
|
||||||
"build": "nuxt build",
|
|
||||||
"start": "nuxt start",
|
|
||||||
"generate": "nuxt generate",
|
|
||||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
|
|
||||||
"precommit": "npm run lint"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@nuxtjs/axios": "^5.3.6",
|
|
||||||
"@nuxtjs/proxy": "^1.3.1",
|
|
||||||
"cross-env": "^5.2.0",
|
|
||||||
"express": "^4.16.4",
|
|
||||||
"nodemailer": "^5.1.1",
|
|
||||||
"nuxt": "^2.0.0",
|
|
||||||
"nuxt-buefy": "^0.2.1",
|
|
||||||
"nuxt-sass-resources-loader": "^2.0.5",
|
|
||||||
"validator": "^10.11.0",
|
|
||||||
"vuelidate": "^0.7.4",
|
|
||||||
"xss-filters": "^1.2.7"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"babel-eslint": "^8.2.1",
|
|
||||||
"eslint": "^5.0.1",
|
|
||||||
"eslint-loader": "^2.0.0",
|
|
||||||
"eslint-plugin-vue": "^4.0.0",
|
|
||||||
"node-sass": "^4.11.0",
|
|
||||||
"nodemon": "^1.11.0",
|
|
||||||
"sass-loader": "^7.1.0",
|
|
||||||
"vue-svg-loader": "^0.11.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
# PAGES
|
|
||||||
|
|
||||||
This directory contains your Application Views and Routes.
|
|
||||||
The framework reads all the `*.vue` files inside this directory and create the router of your application.
|
|
||||||
|
|
||||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).
|
|
152
pages/about.vue
|
@ -1,152 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentPage :heading="title" class="about">
|
|
||||||
<div class="about-content" :class="{ 'no-content-container': isNoContent }">
|
|
||||||
<h2 class="heading heading-top">{{ title }}</h2>
|
|
||||||
<div v-html="body"></div>
|
|
||||||
</div>
|
|
||||||
<BackgroundImageLoader slot="background"
|
|
||||||
class="background"
|
|
||||||
:image-url="imageUrl"
|
|
||||||
>
|
|
||||||
<div slot="overlay" class="background-tint background-overlay"></div>
|
|
||||||
</BackgroundImageLoader>
|
|
||||||
</ContentPage>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ContentPage from '@/components/ContentPage'
|
|
||||||
import BackgroundImageLoader from '@/components/BackgroundImageLoader'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'AboutPage',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
ContentPage,
|
|
||||||
BackgroundImageLoader,
|
|
||||||
},
|
|
||||||
|
|
||||||
head () {
|
|
||||||
return {
|
|
||||||
title: this.title,
|
|
||||||
meta: [{
|
|
||||||
hid: 'description',
|
|
||||||
name: 'description',
|
|
||||||
content: 'All about the work of photographer Marc Leopold, his philosophy and inspirations.'
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async asyncData ({ $axios }) {
|
|
||||||
try {
|
|
||||||
const isNoContent = false
|
|
||||||
let { title, imageUrl, body } = await $axios.$get('/api/v1/about')
|
|
||||||
if (!body || body.length < 1) {
|
|
||||||
throw new Error('No body in response')
|
|
||||||
} else if (!imageUrl || imageUrl.length < 1) {
|
|
||||||
imageUrl = '/img/default-about.jpg'
|
|
||||||
}
|
|
||||||
return { title, imageUrl, body, isNoContent }
|
|
||||||
} catch {
|
|
||||||
return {
|
|
||||||
title: 'Coming Soon',
|
|
||||||
imageUrl: '/img/default-about.jpg',
|
|
||||||
body: `<div class="no-services">
|
|
||||||
<p class="no-conttent-text">Please check back, I will be updating this page shortly.</p>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
isNoContent: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.about {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-content {
|
|
||||||
color: $color__neutral-800;
|
|
||||||
text-shadow: 0 0 3px #000;
|
|
||||||
|
|
||||||
@media (min-width: 60em) {
|
|
||||||
padding: 1rem 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-content /deep/ blockquote {
|
|
||||||
font-size: 1.4em;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
padding: 1.5rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-content /deep/ blockquote p {
|
|
||||||
color: $color__neutral-700;
|
|
||||||
text-shadow: 0 0 3px #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading {
|
|
||||||
color: $color__neutral-900;
|
|
||||||
@include font-title(400);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading-top {
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-content /deep/ ul {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
list-style: none;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.2em;
|
|
||||||
line-height: 1.2;
|
|
||||||
margin-bottom: 4.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-content /deep/ li {
|
|
||||||
position: relative;
|
|
||||||
padding: 1.25rem 0 1.25rem;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 2rem;
|
|
||||||
height: 1px;
|
|
||||||
left: 50%;
|
|
||||||
bottom: 0;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
|
|
||||||
background-color: rgba($color__neutral-500, .6);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-bottom: 0;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-content /deep/ h2 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-content /deep/ .background-overlay {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,322 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentPage :heading="title">
|
|
||||||
|
|
||||||
<form class="contact-form">
|
|
||||||
<div class="form-background"></div>
|
|
||||||
<div class="form-content">
|
|
||||||
<b-field label="Name"
|
|
||||||
:type="attemptedSubmit && form.name.length < 1 ? 'is-danger' : ''"
|
|
||||||
:message="attemptedSubmit && form.name.length < 1 ? 'Please include your name.' : ''"
|
|
||||||
>
|
|
||||||
<b-input v-model.trim="form.name"
|
|
||||||
@input="$v.form.name.$touch()"
|
|
||||||
placeholder="Your name ...">
|
|
||||||
</b-input>
|
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<b-field label="Email"
|
|
||||||
:type="attemptedSubmit && form.email.length < 1 ? 'is-danger' : ''"
|
|
||||||
:message="attemptedSubmit && form.email.length < 1 ? 'Please include your email address.' : ''"
|
|
||||||
>
|
|
||||||
<b-input v-model="form.email"
|
|
||||||
type="email"
|
|
||||||
placeholder="Your email address ...">
|
|
||||||
</b-input>
|
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<b-field label="Subject">
|
|
||||||
<b-input v-model="form.subject"
|
|
||||||
placeholder="Your subject ...">
|
|
||||||
</b-input>
|
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<b-field label="Message"
|
|
||||||
:type="attemptedSubmit && form.message.length < 1 ? 'is-danger' : ''"
|
|
||||||
:message="attemptedSubmit && form.message.length < 1 ? 'Please include a message.' : ''"
|
|
||||||
>
|
|
||||||
<b-input v-model="form.message"
|
|
||||||
placeholder="Your message ..."
|
|
||||||
type="textarea">
|
|
||||||
</b-input>
|
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<div class="form-footer">
|
|
||||||
<button class="btn-submit"
|
|
||||||
type="button"
|
|
||||||
@click.prevent="onSubmit"
|
|
||||||
:disabled="isDisabled">
|
|
||||||
Send
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<ul class="social-nav">
|
|
||||||
<li v-for="item in socialNav"
|
|
||||||
:key="item.to"
|
|
||||||
class="social-nav__item"
|
|
||||||
>
|
|
||||||
<a class="social-nav__link social-link"
|
|
||||||
:href="item.to"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<b-icon class="social-nav__icon"
|
|
||||||
:icon="item.icon"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<BackgroundImageLoader slot="background"
|
|
||||||
class="background"
|
|
||||||
:image-url="imageUrl"
|
|
||||||
>
|
|
||||||
<div slot="overlay" class="background-tint background-overlay"></div>
|
|
||||||
</BackgroundImageLoader>
|
|
||||||
</ContentPage>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ContentPage from '@/components/ContentPage'
|
|
||||||
import { required, email } from 'vuelidate/lib/validators'
|
|
||||||
import BackgroundImageLoader from '@/components/BackgroundImageLoader'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ContactPage',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
ContentPage,
|
|
||||||
BackgroundImageLoader,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
attemptedSubmit: false,
|
|
||||||
form: {
|
|
||||||
name: "",
|
|
||||||
email: "",
|
|
||||||
subject: "",
|
|
||||||
message: "",
|
|
||||||
},
|
|
||||||
socialNav: [
|
|
||||||
{ 'to': 'https://www.instagram.com', 'text': 'Instagram', icon: 'instagram' },
|
|
||||||
{ 'to': 'https://www.facebook.com', 'text': 'Facebook', icon: 'facebook' },
|
|
||||||
{ 'to': 'https://twitter.com', 'text': 'Twitter', icon: 'twitter' },
|
|
||||||
{ 'to': 'https://uk.linkedin.com', 'text': 'LinkedIn', icon: 'linkedin' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
head () {
|
|
||||||
return {
|
|
||||||
title: this.title,
|
|
||||||
meta: [{
|
|
||||||
hid: 'description',
|
|
||||||
name: 'description',
|
|
||||||
content: 'Contact the photographer Marc Leopold with any queries for a prompt response.'
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
isDisabled () {
|
|
||||||
return this.attemptedSubmit && this.$v.form.$invalid
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
validations: {
|
|
||||||
form: {
|
|
||||||
name: {
|
|
||||||
required,
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
required,
|
|
||||||
email
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
required,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onSubmit () {
|
|
||||||
this.attemptedSubmit = true
|
|
||||||
if (this.$v.form.$invalid) {
|
|
||||||
this.$toast.open({
|
|
||||||
message: 'Please correct errors before submitting.',
|
|
||||||
type: 'is-danger'
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.$toast.open('Submitting your message ...')
|
|
||||||
this.submitForm()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async submitForm() {
|
|
||||||
try {
|
|
||||||
await this.$axios({
|
|
||||||
method: 'post',
|
|
||||||
baseURL: 'http://192.168.0.5:3003',
|
|
||||||
url: '/api/contact',
|
|
||||||
proxy: false,
|
|
||||||
debug: true,
|
|
||||||
data: {
|
|
||||||
name: this.form.name,
|
|
||||||
email: this.form.email,
|
|
||||||
msg: this.form.message
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
this.$toast.open({
|
|
||||||
message: 'sorry, there was a problem submitting your message.',
|
|
||||||
type: 'is-danger'
|
|
||||||
})
|
|
||||||
console.error(e)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.$toast.open({
|
|
||||||
message: 'Thank you, your message has been sent.',
|
|
||||||
type: 'is-success'
|
|
||||||
})
|
|
||||||
this.form.message = ''
|
|
||||||
this.form.subject = ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
async asyncData ({ $axios }) {
|
|
||||||
try {
|
|
||||||
let { imageUrl, title } = await $axios.$get('/api/v1/page/about')
|
|
||||||
if (!imageUrl) {
|
|
||||||
throw new Error('empty imageUrl')
|
|
||||||
}
|
|
||||||
if (!title || title === '') {
|
|
||||||
title = 'Contact Me'
|
|
||||||
}
|
|
||||||
return { imageUrl, title }
|
|
||||||
} catch {
|
|
||||||
return {
|
|
||||||
imageUrl: '/img/default-contact.jpg',
|
|
||||||
title: 'Contact Me',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
|
|
||||||
.contact-form {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
padding: 1rem 1rem 2rem;
|
|
||||||
border-radius: 2px;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(#fff, .95);
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom right,
|
|
||||||
rgba(#fff, .95),
|
|
||||||
rgba($color__neutral-800, .97)
|
|
||||||
),
|
|
||||||
url(/img/default-contact.jpg);
|
|
||||||
background-size: cover;
|
|
||||||
background-position: top left;
|
|
||||||
transform: scaleX(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-content {
|
|
||||||
z-index: 5;
|
|
||||||
max-width: 30em;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-overlay {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-submit {
|
|
||||||
font-size: 1.2em;
|
|
||||||
border: 2px solid $color__neutral-300;
|
|
||||||
padding: .2em .8em;
|
|
||||||
@include font-title(600);
|
|
||||||
color: $color__neutral-500;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
transition: opacity .3s;
|
|
||||||
opacity: .7;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
opacity: .1;
|
|
||||||
cursor: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-footer {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding-top: 2rem;
|
|
||||||
margin-top: 2rem;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 90%;
|
|
||||||
height: 1px;
|
|
||||||
top: 0;
|
|
||||||
left: 5%;
|
|
||||||
background-color: $color__neutral-800;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-nav {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-nav__item {
|
|
||||||
margin: 0 .2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-link {
|
|
||||||
transition: opacity .3s;
|
|
||||||
|
|
||||||
&:link, &:visited {
|
|
||||||
color: $color__neutral-100;
|
|
||||||
opacity: .6;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover, &:active {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
<template>
|
|
||||||
<GalleryPage v-if="galleries && galleries.length > 0" :galleries="galleries">
|
|
||||||
</GalleryPage>
|
|
||||||
<NoContent v-else :heading="title"
|
|
||||||
message="My galleries are being prepared for upload, please check back soon."
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import GalleryPage from '@/components/GalleryPage'
|
|
||||||
import NoContent from '@/components/NoContent'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'GalleriesPage',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
GalleryPage,
|
|
||||||
NoContent,
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
galleries: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
head () {
|
|
||||||
return {
|
|
||||||
// title: 'My Galleries',
|
|
||||||
title: this.title,
|
|
||||||
meta: [{
|
|
||||||
hid: 'description',
|
|
||||||
name: 'description',
|
|
||||||
content: 'A showcase of the work of photographer Marc Leopold.'
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async asyncData ({ $axios }) {
|
|
||||||
try {
|
|
||||||
let { galleries, title } = await $axios.$get('/api/v1/galleries')
|
|
||||||
if (title === '') {
|
|
||||||
title = 'My Galleries'
|
|
||||||
}
|
|
||||||
return { galleries, title }
|
|
||||||
} catch {
|
|
||||||
return {
|
|
||||||
galleries: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
232
pages/index.vue
|
@ -1,232 +0,0 @@
|
||||||
<template>
|
|
||||||
<section class="home">
|
|
||||||
<div class="background background-img">
|
|
||||||
<BackgroundImageLoader slot="background"
|
|
||||||
:image-url="currentImageUrl"
|
|
||||||
@imageLoaded="handleImageLoaded"
|
|
||||||
@imageLoadError="handleImageLoadError"
|
|
||||||
/>
|
|
||||||
<div class="background background-overlay"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<transition name="fade">
|
|
||||||
<h1 v-if="showHeading" class="heading">Marc Leopold Photography</h1>
|
|
||||||
</transition>
|
|
||||||
<transition name="fade" mode="out-in">
|
|
||||||
<p class="tagline"
|
|
||||||
:key="currentTaglineIndex">
|
|
||||||
{{ tagline }}
|
|
||||||
</p>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ContentPage from '@/components/ContentPage'
|
|
||||||
import BackgroundImageLoader from '@/components/BackgroundImageLoader'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'HomePage',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
ContentPage,
|
|
||||||
BackgroundImageLoader
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showHeading: false,
|
|
||||||
currentImageIndex: -1,
|
|
||||||
currentTaglineIndex: -1,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
currentImageUrl () {
|
|
||||||
let url = null
|
|
||||||
if (this.currentImageIndex > -1) {
|
|
||||||
url = this.bgImages[this.currentImageIndex]
|
|
||||||
}
|
|
||||||
return url
|
|
||||||
},
|
|
||||||
|
|
||||||
tagline () {
|
|
||||||
return this.currentTaglineIndex > -1 ? this.taglines[this.currentTaglineIndex].text : ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
head () {
|
|
||||||
return {
|
|
||||||
title: 'Home',
|
|
||||||
meta: [{
|
|
||||||
hid: 'description',
|
|
||||||
name: 'description',
|
|
||||||
content: 'Photographer, Marc Leopold has images in numerous collections and publications. Here is a glimpse of his work, an insight into his philosophy and motivations.'
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
if (this.bgImages.length > 0) {
|
|
||||||
this.setNextIndex()
|
|
||||||
this.showHeading = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async asyncData({ $axios }) {
|
|
||||||
try {
|
|
||||||
const { bgImages, taglines } = await $axios.$get('/api/v1/home')
|
|
||||||
if (bgImages.length < 1) {
|
|
||||||
throw new Error('bgImages empty')
|
|
||||||
}
|
|
||||||
return { bgImages, taglines }
|
|
||||||
} catch {
|
|
||||||
return {
|
|
||||||
bgImages: ['/img/default-home.jpg'],
|
|
||||||
taglines: [''],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setNextIndex () {
|
|
||||||
this.currentImageIndex =
|
|
||||||
this.currentImageIndex < this.bgImages.length - 1 ? this.currentImageIndex + 1 : 0
|
|
||||||
},
|
|
||||||
|
|
||||||
nextTagline () {
|
|
||||||
this.currentTaglineIndex =
|
|
||||||
this.currentTaglineIndex < this.taglines.length - 1 ? this.currentTaglineIndex + 1 : 0
|
|
||||||
},
|
|
||||||
|
|
||||||
handleImageLoaded () {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
this.nextTagline()
|
|
||||||
}, 700)
|
|
||||||
window.setTimeout(() => {
|
|
||||||
this.setNextIndex()
|
|
||||||
}, 6000)
|
|
||||||
},
|
|
||||||
|
|
||||||
handleImageLoadError () {
|
|
||||||
this.setNextIndex()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
$heading-height: 4em;
|
|
||||||
$tagline-height: 2.5em;
|
|
||||||
$padding: 1rem;
|
|
||||||
|
|
||||||
.home {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
padding: $site-menu__header-height + $padding $padding $padding;
|
|
||||||
|
|
||||||
font-size: .5rem;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
padding: $padding $padding $padding $site-menu__header-width + $padding;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-img {
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-overlay {
|
|
||||||
$color: $color__neutral-100;
|
|
||||||
|
|
||||||
background-color: rgba($color, .4);
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom left,
|
|
||||||
rgba($color, .1),
|
|
||||||
rgba($color, .5)
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba($color, .9),
|
|
||||||
rgba($color, .7) $heading-height * 1.5,
|
|
||||||
rgba($color, .4) $heading-height * 3,
|
|
||||||
rgba($color, 0) $heading-height * 6,
|
|
||||||
rgba($color, 0)
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to top,
|
|
||||||
rgba($color, .6),
|
|
||||||
rgba($color, 0) $tagline-height * 4,
|
|
||||||
rgba($color, 0)
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba($color, .4),
|
|
||||||
rgba($color, 0) 20%,
|
|
||||||
rgba($color, 0) 80%,
|
|
||||||
rgba($color, .4)
|
|
||||||
),
|
|
||||||
radial-gradient(
|
|
||||||
at 100% 100%,
|
|
||||||
rgba($color, .4) 0,
|
|
||||||
rgba($color, 0) 40%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 80rem;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
padding: 1rem 0 0;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
padding: 2rem 3rem 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading {
|
|
||||||
color: $color__neutral-900;
|
|
||||||
font-size: $heading-height;
|
|
||||||
@include font-title(400);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tagline {
|
|
||||||
color: $color__neutral-600;
|
|
||||||
font-size: $tagline-height;
|
|
||||||
text-align: right;
|
|
||||||
@include font-cursive;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade {
|
|
||||||
$timing: .7;
|
|
||||||
&-enter-active {
|
|
||||||
transition: opacity 5s * $timing .5s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-leave-active {
|
|
||||||
transition: opacity 2s * $timing ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter, &-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,271 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentPage :heading="title">
|
|
||||||
<BackgroundImagePreloader v-if="showPreloaderBackground && backgroundImageUrls.length > 0"
|
|
||||||
slot="background"
|
|
||||||
class="background-preloader"
|
|
||||||
:image-urls="backgroundImageUrls"
|
|
||||||
:active-index="activeIndex"
|
|
||||||
>
|
|
||||||
<div slot="overlay" class="background-tint background-overlay"></div>
|
|
||||||
</BackgroundImagePreloader>
|
|
||||||
<BackgroundImageLoader v-else slot="background"
|
|
||||||
class="background"
|
|
||||||
:image-url="imageUrl"
|
|
||||||
>
|
|
||||||
<div slot="overlay" class="background-tint background-overlay"></div>
|
|
||||||
</BackgroundImageLoader>
|
|
||||||
|
|
||||||
<ul v-if="services.length > 0" class="services-list">
|
|
||||||
<li v-for="(service, index) in services"
|
|
||||||
:key="index"
|
|
||||||
class="services-list__item"
|
|
||||||
:style="{ 'z-index': services.length - index + 1}"
|
|
||||||
@mouseover="handleMouseOver(index)"
|
|
||||||
>
|
|
||||||
<div class="background background-image"
|
|
||||||
:style="{ 'background-image': `url(${service.imageUrl})` }"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="background background-overlay"></div>
|
|
||||||
<div class="services-list__content">
|
|
||||||
<h2 class="services-list__heading service-heading">{{ service.heading }}</h2>
|
|
||||||
<div class="services-list__body" v-html="service.html">
|
|
||||||
</div>
|
|
||||||
<a class="btn-link services-list__gallery-link" :href="service.linkUrl">My {{ service.heading }}</a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div v-else class="no-content-container">
|
|
||||||
<h2>Coming Soon</h2>
|
|
||||||
<p class="no-content-text">I will be updating soon, please check back for information on the services I provide.</p>
|
|
||||||
</div>
|
|
||||||
</ContentPage>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ContentPage from '@/components/ContentPage'
|
|
||||||
import BackgroundImagePreloader from '@/components/BackgroundImagePreloader'
|
|
||||||
import BackgroundImageLoader from '@/components/BackgroundImageLoader'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ServicesPage',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
ContentPage,
|
|
||||||
BackgroundImagePreloader,
|
|
||||||
BackgroundImageLoader,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showPreloaderBackground: false,
|
|
||||||
activeIndex: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
head () {
|
|
||||||
return {
|
|
||||||
title: this.title,
|
|
||||||
meta: [{
|
|
||||||
hid: 'description',
|
|
||||||
name: 'description',
|
|
||||||
content: 'An overview of the services provided by the photographer marc Leopold.'
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
backgroundImageUrls () {
|
|
||||||
return this.services.map(el => el.backgroundImageUrl)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
const mq = window.matchMedia("(min-width: 40em)")
|
|
||||||
this.showPreloaderBackground = mq.matches
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
handleMouseOver(index) {
|
|
||||||
this.activeIndex = index
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
async asyncData ({ $axios }) {
|
|
||||||
try {
|
|
||||||
const { services } = await $axios.$get('api/v1/services')
|
|
||||||
let { imageUrl, title } = await $axios.$get('api/v1/page/services')
|
|
||||||
|
|
||||||
if (!imageUrl) {
|
|
||||||
imageUrl = '/img/default-services.jpg'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!title || title === '') {
|
|
||||||
title = 'My Services'
|
|
||||||
}
|
|
||||||
|
|
||||||
return { services, imageUrl, title }
|
|
||||||
} catch {
|
|
||||||
return {
|
|
||||||
services: [],
|
|
||||||
imageUrl: '/img/default-services.jpg',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.services-list {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__item {
|
|
||||||
$min-height: 32rem;
|
|
||||||
$overlap: 4rem;
|
|
||||||
$padding-vertical: 2rem;
|
|
||||||
$color-overlay: $color__neutral-200;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
min-height: $min-height;
|
|
||||||
padding: $overlap + $padding-vertical 2rem $overlap;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: -1 * $overlap;
|
|
||||||
|
|
||||||
background-color: $color__neutral-200;
|
|
||||||
font-size: 1rem;
|
|
||||||
|
|
||||||
.background-overlay {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(odd) {
|
|
||||||
clip-path: polygon(0 0, 100% 0, 100% calc(100% - #{$overlap}), 0 100%);
|
|
||||||
|
|
||||||
.background-overlay {
|
|
||||||
background: linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba($color-overlay, .1),
|
|
||||||
rgba($color-overlay, 1) 75%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__heading {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__body {
|
|
||||||
align-self: flex-end;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(even) {
|
|
||||||
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - #{$overlap}));
|
|
||||||
.background-overlay {
|
|
||||||
background: linear-gradient(
|
|
||||||
to left,
|
|
||||||
rgba($color-overlay, .1),
|
|
||||||
rgba($color-overlay, 1) 75%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-image {
|
|
||||||
left: auto;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__gallery-link {
|
|
||||||
left: auto;
|
|
||||||
right: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__body {
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
min-height: $min-height - $overlap;
|
|
||||||
padding-top: $padding-vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-bottom: $padding-vertical;
|
|
||||||
clip-path: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__img {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
height: 20em;
|
|
||||||
width: auto;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
opacity: .4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
min-height: 20em;
|
|
||||||
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__body {
|
|
||||||
width: 100%;
|
|
||||||
color: $color__neutral-800;
|
|
||||||
|
|
||||||
@media (min-width: $bp__layout) {
|
|
||||||
width: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 70em) {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-list__gallery-link {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 3rem;
|
|
||||||
left: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
z-index: -1;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-image {
|
|
||||||
// opacity: .3;
|
|
||||||
background-size: cover;
|
|
||||||
filter: grayscale(.2);
|
|
||||||
width: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-heading {
|
|
||||||
@include font-title();
|
|
||||||
color: $color__neutral-900;
|
|
||||||
font-size: 2.0em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# PLUGINS
|
|
||||||
|
|
||||||
**This directory is not required, you can delete it if you don't want to use it.**
|
|
||||||
|
|
||||||
This directory contains your Javascript plugins that you want to run before mounting the root Vue.js application.
|
|
||||||
|
|
||||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).
|
|
|
@ -1,3 +0,0 @@
|
||||||
import Vue from 'vue'
|
|
||||||
import Vuelidate from 'vuelidate'
|
|
||||||
Vue.use(Vuelidate)
|
|
|
@ -1,10 +0,0 @@
|
||||||
# STATIC
|
|
||||||
|
|
||||||
**This directory is not required, you can delete it if you don't want to use it.**
|
|
||||||
|
|
||||||
This directory contains your static files.
|
|
||||||
Each file inside this directory is mapped to `/`.
|
|
||||||
|
|
||||||
Example: `/static/robots.txt` is mapped as `/robots.txt`.
|
|
||||||
|
|
||||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).
|
|
Before Width: | Height: | Size: 515 B |
Before Width: | Height: | Size: 720 B |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 515 B |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 677 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 193 KiB |
Before Width: | Height: | Size: 194 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 648 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 240 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 115 KiB |
|
@ -1,10 +0,0 @@
|
||||||
# STORE
|
|
||||||
|
|
||||||
**This directory is not required, you can delete it if you don't want to use it.**
|
|
||||||
|
|
||||||
This directory contains your Vuex Store files.
|
|
||||||
Vuex Store option is implemented in the Nuxt.js framework.
|
|
||||||
|
|
||||||
Creating a file in this directory activate the option in the framework automatically.
|
|
||||||
|
|
||||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).
|
|
|
@ -1,29 +0,0 @@
|
||||||
export const state = () => ({})
|
|
||||||
|
|
||||||
export const actions = {
|
|
||||||
async nuxtServerInit ({ commit }, { $axios }) {
|
|
||||||
try {
|
|
||||||
const { siteNav, socialNav } = await $axios.$get('/api/v1/navigation')
|
|
||||||
if (siteNav.length < 1) {
|
|
||||||
throw new Error('siteNav empty')
|
|
||||||
}
|
|
||||||
commit('navigation/updateSiteNav', siteNav)
|
|
||||||
commit('navigation/updateSocialNav', socialNav)
|
|
||||||
} catch {
|
|
||||||
commit('navigation/updateSiteNav', [
|
|
||||||
{ "to": "/", "text": "Home", "bgImgUrl": "/img/devices--bw.jpg"},
|
|
||||||
{ "to": "/galleries", "text": "Galleries", "bgImgUrl": "/img/photo-box--bw.jpg" },
|
|
||||||
{ "to": "/services", "text": "Services", "bgImgUrl": "/img/camera--bw.jpg" },
|
|
||||||
{ "to": "/about", "text": "About Me", "bgImgUrl": "/img/silhouette--dark.jpg" },
|
|
||||||
{ "to": "/contact", "text": "Contact Me", "bgImgUrl": "/img/mail--bw.jpg" }
|
|
||||||
])
|
|
||||||
commit('navigation/updateSocialNav', [
|
|
||||||
{ "to": "https://www.instagram.com", "text": "Instagram", "icon": "instagram" },
|
|
||||||
{ "to": "https://www.facebook.com", "text": "Facebook", "icon": "facebook" },
|
|
||||||
{ "to": "https://twitter.com", "text": "Twitter", "icon": "twitter" },
|
|
||||||
{ "to": "https://uk.linkedin.com", "text": "LinkedIn", "icon": "linkedin" }
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
export const state = () => ({
|
|
||||||
siteNav: [],
|
|
||||||
socialNav: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
export const getters = {
|
|
||||||
siteNav: state => {
|
|
||||||
return state.siteNav
|
|
||||||
},
|
|
||||||
|
|
||||||
socialNav: state => {
|
|
||||||
return state.socialNav
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mutations = {
|
|
||||||
updateSiteNav (state, navItems) {
|
|
||||||
state.siteNav = navItems
|
|
||||||
},
|
|
||||||
|
|
||||||
updateSocialNav (state, socialItems) {
|
|
||||||
state.socialNav = socialItems
|
|
||||||
},
|
|
||||||
}
|
|