mirror of
https://github.com/a2nt/webpack-bootstrap-ui-kit.git
synced 2024-10-22 11:05:45 +02:00
IMPR: reorganize files
This commit is contained in:
parent
6f8236ec0d
commit
642b4b7f4c
79
src/js/_ajax/_lazy-images.js
Normal file
79
src/js/_ajax/_lazy-images.js
Normal file
@ -0,0 +1,79 @@
|
||||
// browser tab visibility state detection
|
||||
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
export default ((W) => {
|
||||
const NAME = '_main.lazy-images';
|
||||
const D = document;
|
||||
const BODY = D.body;
|
||||
|
||||
const API_STATIC = document.querySelector('meta[name="api_static_domain"]');
|
||||
const API_STATIC_URL = API_STATIC ?
|
||||
API_STATIC.getAttribute('content') :
|
||||
`${window.location.protocol}//${window.location.host}`;
|
||||
|
||||
console.log(`${NAME} [static url]: ${API_STATIC_URL}`);
|
||||
|
||||
const loadLazyImages = () => {
|
||||
console.log(`${NAME}: Load lazy images`);
|
||||
|
||||
D.querySelectorAll(`[data-lazy-src]`).forEach((el) => {
|
||||
el.classList.remove('empty');
|
||||
el.classList.add('loading');
|
||||
el.classList.remove('loading__network-error');
|
||||
|
||||
const attr = el.getAttribute('data-lazy-src');
|
||||
const imageUrl = attr.startsWith('http') ? attr : API_STATIC_URL + attr;
|
||||
|
||||
// offline response will be served by caching service worker
|
||||
axios
|
||||
.get(imageUrl, {
|
||||
responseType: 'blob',
|
||||
})
|
||||
.then((response) => {
|
||||
const reader = new FileReader(); // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/FileReader
|
||||
reader.readAsDataURL(response.data);
|
||||
reader.onload = () => {
|
||||
const imageDataUrl = reader.result;
|
||||
el.setAttribute('src', imageDataUrl);
|
||||
el.classList.remove('loading');
|
||||
el.classList.add('loading__success');
|
||||
};
|
||||
})
|
||||
.catch((e) => {
|
||||
//el.setAttribute('src', imageUrl);
|
||||
|
||||
if (e.response) {
|
||||
switch (e.response.status) {
|
||||
case 404:
|
||||
msg = 'Not Found.';
|
||||
break;
|
||||
case 500:
|
||||
msg = 'Server issue, please try again latter.';
|
||||
break;
|
||||
default:
|
||||
msg = 'Something went wrong.';
|
||||
break;
|
||||
}
|
||||
|
||||
console.error(`${NAME} [${imageUrl}]: ${msg}`);
|
||||
} else if (e.request) {
|
||||
msg = 'No response received';
|
||||
console.error(`${NAME} [${imageUrl}]: ${msg}`);
|
||||
} else {
|
||||
console.error(`${NAME} [${imageUrl}]: ${e.message}`);
|
||||
}
|
||||
|
||||
el.classList.remove('loading');
|
||||
el.classList.add('loading__network-error');
|
||||
el.classList.add('empty');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
W.addEventListener(`${Events.LODEDANDREADY}`, loadLazyImages);
|
||||
W.addEventListener(`${Events.AJAX}`, loadLazyImages);
|
||||
})(window);
|
227
src/js/_ajax/_links.js
Normal file
227
src/js/_ajax/_links.js
Normal file
@ -0,0 +1,227 @@
|
||||
// browser tab visibility state detection
|
||||
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
import Page from './models/_page.jsx';
|
||||
|
||||
import {
|
||||
getParents,
|
||||
} from '../_main/_funcs';
|
||||
|
||||
import {
|
||||
Collapse,
|
||||
} from 'bootstrap';
|
||||
|
||||
import SpinnerUI from '../_main/_loading-spinner';
|
||||
|
||||
const MainUILinks = ((W) => {
|
||||
const NAME = '_main.links';
|
||||
const D = document;
|
||||
const BODY = D.body;
|
||||
|
||||
class MainUILinks {
|
||||
window
|
||||
static init() {
|
||||
const ui = this;
|
||||
ui.GraphPage = null;
|
||||
|
||||
console.log(`${NAME}: init`);
|
||||
|
||||
ui.loaded();
|
||||
|
||||
// history state switch
|
||||
W.addEventListener('popstate', (e) => {
|
||||
ui.popState(e);
|
||||
});
|
||||
}
|
||||
|
||||
static loaded() {
|
||||
const ui = this;
|
||||
|
||||
D.querySelectorAll('.graphql-page').forEach((el, i) => {
|
||||
const el_id = el.getAttribute('href');
|
||||
el.setAttribute(`data-${ui.name}-id`, el_id);
|
||||
|
||||
el.addEventListener('click', ui.loadClick);
|
||||
});
|
||||
}
|
||||
|
||||
static setActiveLinks(link) {
|
||||
const ui = this;
|
||||
D.querySelectorAll(`[data-${ui.name}-id="${link}"]`).forEach(
|
||||
(el) => {
|
||||
el.classList.add('active');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static reset() {
|
||||
// reset focus
|
||||
D.activeElement.blur();
|
||||
|
||||
// remove active and loading classes
|
||||
D.querySelectorAll('.graphql-page,.nav-item').forEach((el2) => {
|
||||
el2.classList.remove('active', 'loading');
|
||||
});
|
||||
}
|
||||
|
||||
static popState(e) {
|
||||
const ui = this;
|
||||
|
||||
SpinnerUI.show();
|
||||
|
||||
if (e.state && e.state.page) {
|
||||
console.log(`${NAME}: [popstate] load`);
|
||||
const state = JSON.parse(e.state.page);
|
||||
|
||||
state.current = null;
|
||||
state.popstate = true;
|
||||
|
||||
ui.reset();
|
||||
ui.setActiveLinks(e.state.link);
|
||||
|
||||
if (!ui.GraphPage) {
|
||||
console.log(
|
||||
`${NAME}: [popstate] GraphPage is missing. Have to render it first`,
|
||||
);
|
||||
|
||||
ui.GraphPage = ReactDOM.render(
|
||||
<Page />,
|
||||
document.getElementById('MainContent'),
|
||||
);
|
||||
}
|
||||
|
||||
ui.GraphPage.setState(state);
|
||||
SpinnerUI.hide();
|
||||
|
||||
window.dispatchEvent(new Event(Events.AJAX));
|
||||
} else if (e.state && e.state.landing) {
|
||||
console.log(`${NAME}: [popstate] go to landing`);
|
||||
W.location.href = e.state.landing;
|
||||
} else {
|
||||
console.warn(`${NAME}: [popstate] state is missing`);
|
||||
console.log(e);
|
||||
SpinnerUI.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// link specific event {this} = current event, not MainUILinks
|
||||
static loadClick(e) {
|
||||
console.groupCollapsed(`${NAME}: load on click`);
|
||||
e.preventDefault();
|
||||
|
||||
const ui = MainUILinks;
|
||||
const el = e.currentTarget;
|
||||
|
||||
SpinnerUI.show();
|
||||
|
||||
ui.reset();
|
||||
el.classList.add('loading');
|
||||
el.classList.remove('response-404', 'response-500', 'response-523');
|
||||
BODY.classList.add('ajax-loading');
|
||||
|
||||
// hide parent mobile nav
|
||||
const navs = getParents(el, '.collapse');
|
||||
if (navs.length) {
|
||||
navs.forEach((nav) => {
|
||||
const collapseInst = Collapse.getInstance(nav);
|
||||
if (collapseInst) {
|
||||
collapseInst.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// hide parent dropdown
|
||||
/*const dropdowns = getParents(el, '.dropdown-menu');
|
||||
if (dropdowns.length) {
|
||||
const DropdownInst = Dropdown.getInstance(dropdowns[0]);
|
||||
DropdownInst.hide();
|
||||
}*/
|
||||
|
||||
if (!ui.GraphPage) {
|
||||
ui.GraphPage = ReactDOM.render(
|
||||
<Page />,
|
||||
document.getElementById('MainContent'),
|
||||
);
|
||||
}
|
||||
|
||||
const link = el.getAttribute('href') || el.getAttribute('data-href');
|
||||
|
||||
|
||||
ui.GraphPage.state.current = el;
|
||||
|
||||
ui.GraphPage.load(link)
|
||||
.then((response) => {
|
||||
BODY.classList.remove('ajax-loading');
|
||||
el.classList.remove('loading');
|
||||
el.classList.add('active');
|
||||
|
||||
D.loading_apollo_link = null;
|
||||
|
||||
if (ui.GraphPage.state.Link) {
|
||||
window.history.pushState({
|
||||
page: JSON.stringify(ui.GraphPage.state),
|
||||
link: el.getAttribute(`data-${ui.name}-id`),
|
||||
},
|
||||
ui.GraphPage.state.Title,
|
||||
ui.GraphPage.state.Link,
|
||||
);
|
||||
|
||||
ui.setActiveLinks(ui.GraphPage.state.Link)
|
||||
}
|
||||
|
||||
SpinnerUI.hide();
|
||||
|
||||
window.dispatchEvent(new Event(Events.AJAX));
|
||||
console.groupEnd(`${NAME}: load on click`);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(`${NAME}: loading error`);
|
||||
console.log(e);
|
||||
|
||||
/*BODY.classList.remove('ajax-loading');
|
||||
el.classList.remove('loading');*/
|
||||
el.classList.add('error', `response-${e.status}`);
|
||||
/*switch (e.status) {
|
||||
case 500:
|
||||
break;
|
||||
case 404:
|
||||
el.classList.add('not-found');
|
||||
break;
|
||||
case 523:
|
||||
el.classList.add('unreachable');
|
||||
break;
|
||||
}*/
|
||||
|
||||
//SpinnerUI.hide();
|
||||
|
||||
//window.dispatchEvent(new Event(Events.AJAX));
|
||||
console.groupEnd(`${NAME}: load on click`);
|
||||
|
||||
console.log(`${NAME}: reloading page ${link}`);
|
||||
|
||||
// fallback loading
|
||||
W.location.href = link;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
W.addEventListener(`${Events.LOADED}`, () => {
|
||||
MainUILinks.init();
|
||||
});
|
||||
|
||||
W.addEventListener(`${Events.AJAX}`, () => {
|
||||
MainUILinks.loaded();
|
||||
});
|
||||
|
||||
// fallback
|
||||
/*W.addEventListener(`${Events.APOLLO_ERROR}`, (e) => {
|
||||
console.error(`${NAME}: [APOLLO_ERROR] loading failure, reloading the page`);
|
||||
//W.dispatchEvent(new Event(Events.OFFLINE));
|
||||
if (D.loading_apollo_link) {
|
||||
W.location.href = D.loading_apollo_link;
|
||||
}
|
||||
});*/
|
||||
})(window);
|
||||
|
||||
export default MainUILinks;
|
104
src/js/_ajax/_online.js
Normal file
104
src/js/_ajax/_online.js
Normal file
@ -0,0 +1,104 @@
|
||||
// ping online/offline state switch and detection
|
||||
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
export default ((W) => {
|
||||
const NAME = '_main.online';
|
||||
const D = document;
|
||||
const BODY = D.body;
|
||||
|
||||
let pingInterval;
|
||||
const PING_META = D.querySelector('meta[name="ping"]');
|
||||
|
||||
let update_online_status_lock = false;
|
||||
const UPDATE_ONLINE_STATUS = (online) => {
|
||||
if (update_online_status_lock) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_online_status_lock = true;
|
||||
if (online) {
|
||||
if (BODY.classList.contains('is-offline')) {
|
||||
console.log(`${NAME}: back Online`);
|
||||
W.dispatchEvent(new Event(Events.BACKONLINE));
|
||||
} else {
|
||||
console.log(`${NAME}: Online`);
|
||||
W.dispatchEvent(new Event(Events.ONLINE));
|
||||
}
|
||||
|
||||
BODY.classList.add('is-online');
|
||||
BODY.classList.remove('is-offline');
|
||||
|
||||
if (PING_META && !pingInterval) {
|
||||
console.log(`${NAME}: SESSION_PING is active`);
|
||||
pingInterval = setInterval(SESSION_PING, 300000); // 5 min in ms
|
||||
}
|
||||
} else {
|
||||
console.log(`${NAME}: Offline`);
|
||||
|
||||
BODY.classList.add('is-offline');
|
||||
BODY.classList.remove('is-online');
|
||||
|
||||
clearInterval(pingInterval);
|
||||
pingInterval = null;
|
||||
|
||||
W.dispatchEvent(new Event(Events.OFFLINE));
|
||||
}
|
||||
|
||||
update_online_status_lock = false;
|
||||
};
|
||||
|
||||
// session ping
|
||||
let session_ping_lock = false;
|
||||
const SESSION_PING = () => {
|
||||
if (session_ping_lock || BODY.classList.contains('is-offline')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const PING_URL = PING_META.getAttribute('content');
|
||||
|
||||
console.log(`${NAME}: session ping`);
|
||||
session_ping_lock = true;
|
||||
|
||||
axios
|
||||
.post(PING_URL, {})
|
||||
.then((resp) => {
|
||||
session_ping_lock = false;
|
||||
UPDATE_ONLINE_STATUS(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
console.warn(`${NAME}: SESSION_PING failed`);
|
||||
|
||||
session_ping_lock = false;
|
||||
UPDATE_ONLINE_STATUS(false);
|
||||
});
|
||||
};
|
||||
|
||||
// current browser online state
|
||||
|
||||
|
||||
const navigatorStateUpdate = () => {
|
||||
if (typeof navigator.onLine !== 'undefined') {
|
||||
if (!navigator.onLine) {
|
||||
UPDATE_ONLINE_STATUS(false);
|
||||
} else {
|
||||
UPDATE_ONLINE_STATUS(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
W.addEventListener(`${Events.OFFLINE}`, () => {
|
||||
UPDATE_ONLINE_STATUS(false);
|
||||
});
|
||||
|
||||
W.addEventListener(`${Events.ONLINE}`, () => {
|
||||
UPDATE_ONLINE_STATUS(true);
|
||||
});
|
||||
|
||||
W.addEventListener(`${Events.LOADED}`, navigatorStateUpdate);
|
||||
W.addEventListener(`${Events.AJAX}`, navigatorStateUpdate);
|
||||
})(window);
|
54
src/js/_main/_css-screen-size.js
Normal file
54
src/js/_main/_css-screen-size.js
Normal file
@ -0,0 +1,54 @@
|
||||
// browser tab visibility state detection
|
||||
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
|
||||
export default ((W) => {
|
||||
const NAME = '_main.css-screen-size';
|
||||
const D = document;
|
||||
const BODY = D.body;
|
||||
|
||||
const detectCSSScreenSize = () => {
|
||||
const el = D.createElement('div');
|
||||
el.className = 'env-test';
|
||||
BODY.appendChild(el);
|
||||
|
||||
const envs = [...Consts.ENVS].reverse();
|
||||
let curEnv = envs.shift();
|
||||
BODY.classList.remove(...envs);
|
||||
|
||||
for (let i = 0; i < envs.length; ++i) {
|
||||
const env = envs[i];
|
||||
el.classList.add(`d-${env}-none`);
|
||||
|
||||
if (W.getComputedStyle(el).display === 'none') {
|
||||
curEnv = env;
|
||||
BODY.classList.add(`${curEnv}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let landscape = true;
|
||||
if (W.innerWidth > W.innerHeight) {
|
||||
BODY.classList.add('landscape');
|
||||
BODY.classList.remove('portrait');
|
||||
} else {
|
||||
landscape = false;
|
||||
|
||||
BODY.classList.add('portrait');
|
||||
BODY.classList.remove('landscape');
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${NAME}: screen size detected ${curEnv} | landscape ${landscape}`,
|
||||
);
|
||||
|
||||
BODY.removeChild(el);
|
||||
|
||||
return curEnv;
|
||||
};
|
||||
|
||||
W.addEventListener(`${Events.LOADED}`, detectCSSScreenSize);
|
||||
|
||||
W.addEventListener(`${Events.RESIZE}`, detectCSSScreenSize);
|
||||
})(window);
|
35
src/js/_main/_funcs.js
Normal file
35
src/js/_main/_funcs.js
Normal file
@ -0,0 +1,35 @@
|
||||
const funcs = {};
|
||||
|
||||
/*!
|
||||
* Get all of an element's parent elements up the DOM tree
|
||||
* (c) 2019 Chris Ferdinandi, MIT License, https://gomakethings.com
|
||||
* @param {Node} elem The element
|
||||
* @param {String} selector Selector to match against [optional]
|
||||
* @return {Array} The parent elements
|
||||
*/
|
||||
|
||||
funcs.getParents = (elem, selector) => {
|
||||
// Setup parents array
|
||||
const parents = [];
|
||||
let el = elem;
|
||||
// Get matching parent elements
|
||||
while (el && el !== document) {
|
||||
// If using a selector, add matching parents to array
|
||||
// Otherwise, add all parents
|
||||
if (selector) {
|
||||
if (el.matches(selector)) {
|
||||
parents.push(el);
|
||||
}
|
||||
} else {
|
||||
parents.push(el);
|
||||
}
|
||||
|
||||
// Jump to the next parent node
|
||||
el = el.parentNode;
|
||||
}
|
||||
|
||||
return parents;
|
||||
};
|
||||
|
||||
module.exports = funcs;
|
||||
module.exports.default = funcs;
|
7
src/js/_main/_index.js
Normal file
7
src/js/_main/_index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
|
||||
import './_visibility';
|
||||
import './_touch';
|
||||
import './_css-screen-size';
|
||||
import './_main';
|
21
src/js/_main/_loading-spinner.js
Normal file
21
src/js/_main/_loading-spinner.js
Normal file
@ -0,0 +1,21 @@
|
||||
// browser tab visibility state detection
|
||||
|
||||
import Events from '../_events';
|
||||
|
||||
const NAME = '_main.loading-spinner';
|
||||
const D = document;
|
||||
const BODY = D.body;
|
||||
const SPINNER = D.getElementById('PageLoading');
|
||||
|
||||
class SpinnerUI {
|
||||
static show() {
|
||||
console.log(`${NAME}: show`);
|
||||
SPINNER.classList.remove('d-none');
|
||||
}
|
||||
static hide() {
|
||||
console.log(`${NAME}: hide`);
|
||||
SPINNER.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
export default SpinnerUI;
|
85
src/js/_main/_main.js
Normal file
85
src/js/_main/_main.js
Normal file
@ -0,0 +1,85 @@
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
import SpinnerUI from './_loading-spinner';
|
||||
|
||||
const MainUI = ((W) => {
|
||||
const NAME = '_main';
|
||||
const D = document;
|
||||
const BODY = D.body;
|
||||
|
||||
console.info(
|
||||
`%cUI Kit ${UINAME} ${UIVERSION}`,
|
||||
'color:yellow;font-size:14px',
|
||||
);
|
||||
console.info(
|
||||
`%c${UIMetaNAME} ${UIMetaVersion}`,
|
||||
'color:yellow;font-size:12px',
|
||||
);
|
||||
console.info(
|
||||
`%chttps://github.com/a2nt/webpack-bootstrap-ui-kit by ${UIAUTHOR}`,
|
||||
'color:yellow;font-size:10px',
|
||||
);
|
||||
|
||||
console.info(`%cENV: ${process.env.NODE_ENV}`, 'color:green;font-size:10px');
|
||||
console.groupCollapsed('Events');
|
||||
Object.keys(Events).forEach((k) => {
|
||||
console.info(`${k}: ${Events[k]}`);
|
||||
});
|
||||
console.groupEnd('Events');
|
||||
|
||||
console.groupCollapsed('Consts');
|
||||
Object.keys(Consts).forEach((k) => {
|
||||
console.info(`${k}: ${Consts[k]}`);
|
||||
});
|
||||
console.groupEnd('Events');
|
||||
|
||||
console.groupCollapsed('Init');
|
||||
console.time('init');
|
||||
|
||||
class MainUI {
|
||||
// first time the website initialization
|
||||
static init() {
|
||||
const ui = this;
|
||||
|
||||
// store landing page state
|
||||
W.history.replaceState(
|
||||
{
|
||||
landing: W.location.href,
|
||||
},
|
||||
D.title,
|
||||
W.location.href,
|
||||
);
|
||||
//
|
||||
|
||||
ui.loaded();
|
||||
}
|
||||
|
||||
// init AJAX components
|
||||
static loaded() {
|
||||
const ui = this;
|
||||
console.log(`${NAME}: loaded`);
|
||||
}
|
||||
}
|
||||
|
||||
W.addEventListener(`${Events.LOADED}`, () => {
|
||||
MainUI.init();
|
||||
|
||||
BODY.classList.add('loaded');
|
||||
SpinnerUI.hide();
|
||||
|
||||
console.groupEnd('init');
|
||||
console.timeEnd('init');
|
||||
|
||||
W.dispatchEvent(new Event(Events.LODEDANDREADY));
|
||||
});
|
||||
|
||||
W.addEventListener(`${Events.AJAX}`, () => {
|
||||
MainUI.loaded();
|
||||
});
|
||||
|
||||
W.MainUI = MainUI;
|
||||
|
||||
return MainUI;
|
||||
})(window);
|
||||
|
||||
export default MainUI;
|
70
src/js/_main/_touch.js
Normal file
70
src/js/_main/_touch.js
Normal file
@ -0,0 +1,70 @@
|
||||
// touch/mouse detection
|
||||
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
|
||||
export default ((W) => {
|
||||
const NAME = '_main.touch';
|
||||
const D = document;
|
||||
const BODY = D.body;
|
||||
|
||||
let prev_touch_event_name;
|
||||
let touch_timeout;
|
||||
const SET_TOUCH_SCREEN = (bool, event_name) => {
|
||||
if (touch_timeout || event_name === prev_touch_event_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bool) {
|
||||
console.log(`${NAME}: Touch screen enabled`);
|
||||
|
||||
BODY.classList.add('is-touch');
|
||||
BODY.classList.remove('is-mouse');
|
||||
|
||||
W.dispatchEvent(new Event(Events.TOUCHENABLE));
|
||||
} else {
|
||||
console.log(`${NAME}: Touch screen disabled`);
|
||||
|
||||
BODY.classList.add('is-mouse');
|
||||
BODY.classList.remove('is-touch');
|
||||
|
||||
W.dispatchEvent(new Event(Events.TOUCHDISABLED));
|
||||
}
|
||||
|
||||
prev_touch_event_name = event_name;
|
||||
// prevent firing touch and mouse events together
|
||||
if (!touch_timeout) {
|
||||
touch_timeout = setTimeout(() => {
|
||||
clearTimeout(touch_timeout);
|
||||
touch_timeout = null;
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
SET_TOUCH_SCREEN(
|
||||
'ontouchstart' in W ||
|
||||
navigator.MaxTouchPoints > 0 ||
|
||||
navigator.msMaxTouchPoints > 0 ||
|
||||
W.matchMedia('(hover: none)').matches,
|
||||
'init',
|
||||
);
|
||||
|
||||
D.addEventListener('touchend', (e) => {
|
||||
let touch = false;
|
||||
if (e.type !== 'click') {
|
||||
touch = true;
|
||||
}
|
||||
|
||||
SET_TOUCH_SCREEN(touch, 'click-touchend');
|
||||
});
|
||||
|
||||
// disable touch on mouse events
|
||||
D.addEventListener('click', (e) => {
|
||||
let touch = false;
|
||||
if (e.type !== 'click') {
|
||||
touch = true;
|
||||
}
|
||||
|
||||
SET_TOUCH_SCREEN(touch, 'click-touchend');
|
||||
});
|
||||
})(window);
|
34
src/js/_main/_visibility.js
Normal file
34
src/js/_main/_visibility.js
Normal file
@ -0,0 +1,34 @@
|
||||
// browser tab visibility state detection
|
||||
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
|
||||
export default ((W) => {
|
||||
const NAME = '_main.visibility';
|
||||
const D = document;
|
||||
const BODY = D.body;
|
||||
|
||||
// update visibility state
|
||||
// get browser window visibility preferences
|
||||
// Opera 12.10, Firefox >=18, Chrome >=31, IE11
|
||||
const HiddenName = 'hidden';
|
||||
const VisibilityChangeEvent = 'visibilitychange';
|
||||
|
||||
D.addEventListener(VisibilityChangeEvent, () => {
|
||||
if (D.visibilityState === HiddenName) {
|
||||
console.log(`${NAME}: Tab: hidden`);
|
||||
|
||||
BODY.classList.add('is-hidden');
|
||||
BODY.classList.remove('is-focused');
|
||||
|
||||
W.dispatchEvent(new Event(Events.TABHIDDEN));
|
||||
} else {
|
||||
console.log(`${NAME}: Tab: focused`);
|
||||
|
||||
BODY.classList.add('is-focused');
|
||||
BODY.classList.remove('is-hidden');
|
||||
|
||||
W.dispatchEvent(new Event(Events.TABFOCUSED));
|
||||
}
|
||||
});
|
||||
})(window);
|
86
src/js/_ui/_carousel.js
Normal file
86
src/js/_ui/_carousel.js
Normal file
@ -0,0 +1,86 @@
|
||||
import Events from '../_events';
|
||||
import Carousel from 'bootstrap/js/src/carousel';
|
||||
|
||||
const CarouselUI = ((window) => {
|
||||
const NAME = 'js-carousel';
|
||||
|
||||
const init = () => {
|
||||
console.log(`${NAME}: init`);
|
||||
|
||||
document.querySelectorAll(`.${NAME}`).forEach((el, i) => {
|
||||
const carousel = new Carousel(el);
|
||||
// create next/prev arrows
|
||||
if (el.dataset.bsArrows) {
|
||||
const next = document.createElement('button');
|
||||
next.classList.add('carousel-control-next');
|
||||
next.setAttribute('type', 'button');
|
||||
next.setAttribute('aria-label', 'Next Slide');
|
||||
next.setAttribute('data-bs-target', el.getAttribute('id'));
|
||||
next.setAttribute('data-bs-slide', 'next');
|
||||
next.addEventListener('click', (e) => {
|
||||
carousel.next();
|
||||
});
|
||||
next.innerHTML = '<span class="carousel-control-next-icon" aria-hidden="true"></span><span class="visually-hidden">Next</span>';
|
||||
el.appendChild(next);
|
||||
|
||||
const prev = document.createElement('button');
|
||||
prev.setAttribute('type', 'button');
|
||||
prev.setAttribute('aria-label', 'Previous Slide');
|
||||
prev.classList.add('carousel-control-prev');
|
||||
prev.setAttribute('data-bs-target', el.getAttribute('id'));
|
||||
prev.setAttribute('data-bs-slide', 'prev');
|
||||
prev.addEventListener('click', (e) => {
|
||||
carousel.prev();
|
||||
});
|
||||
prev.innerHTML = '<span class="carousel-control-prev-icon" aria-hidden="true"></span><span class="visually-hidden">Previous</span>';
|
||||
el.appendChild(prev);
|
||||
}
|
||||
|
||||
if (el.dataset.bsIndicators) {
|
||||
const indicators = document.createElement('div');
|
||||
indicators.classList.add('carousel-indicators');
|
||||
const items = el.querySelectorAll('.carousel-item');
|
||||
let i = 0;
|
||||
while (i < items.length) {
|
||||
const ind = document.createElement('button');
|
||||
ind.setAttribute('type', 'button');
|
||||
ind.setAttribute('aria-label', `Slide to #${ i + 1}`);
|
||||
if (i == 0) {
|
||||
ind.classList.add('active');
|
||||
}
|
||||
ind.setAttribute('data-bs-target', el.getAttribute('id'));
|
||||
ind.setAttribute('data-bs-slide-to', i);
|
||||
|
||||
ind.addEventListener('click', (e) => {
|
||||
const target = e.target;
|
||||
carousel.to(target.getAttribute('data-bs-slide-to'));
|
||||
indicators.querySelectorAll('.active').forEach((ind2) => {
|
||||
ind2.classList.remove('active');
|
||||
});
|
||||
target.classList.add('active');
|
||||
});
|
||||
|
||||
indicators.appendChild(ind);
|
||||
i++;
|
||||
}
|
||||
|
||||
el.appendChild(indicators);
|
||||
el.addEventListener('slide.bs.carousel', (e) => {
|
||||
el.querySelectorAll('.carousel-indicators .active').forEach((ind2) => {
|
||||
ind2.classList.remove('active');
|
||||
});
|
||||
el.querySelectorAll(`.carousel-indicators [data-bs-slide-to="${ e.to }"]`).forEach((ind2) => {
|
||||
ind2.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
el.classList.add(`${NAME}-active`);
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener(`${Events.LODEDANDREADY}`, init);
|
||||
window.addEventListener(`${Events.AJAX}`, init);
|
||||
})(window);
|
||||
|
||||
export default CarouselUI;
|
95
src/js/_ui/_instagram.feed.js
Normal file
95
src/js/_ui/_instagram.feed.js
Normal file
@ -0,0 +1,95 @@
|
||||
// api-less instagram feed
|
||||
|
||||
// Visitor network maybe temporary banned by Instagram because of too many requests from external websites
|
||||
// so it isn't very stable implementation. You should have something for the fall-back.
|
||||
|
||||
import Events from '../_events';
|
||||
import Consts from '../_consts';
|
||||
import InstagramFeed from '@jsanahuja/instagramfeed/src/InstagramFeed';
|
||||
|
||||
export default ((window) => {
|
||||
const NAME = 'js-instagramfeed';
|
||||
const BODY = document.body;
|
||||
|
||||
const ig_media_preview = (base64data) => {
|
||||
const jpegtpl =
|
||||
'/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEABsaGikdKUEmJkFCLy8vQkc/Pj4/R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0cBHSkpNCY0PygoP0c/NT9HR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR//AABEIABQAKgMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AA==',
|
||||
t = atob(base64data),
|
||||
p = t.slice(3).split(''),
|
||||
o = t
|
||||
.substring(0, 3)
|
||||
.split('')
|
||||
.map((e) => {
|
||||
return e.charCodeAt(0);
|
||||
}),
|
||||
c = atob(jpegtpl).split('');
|
||||
c[162] = String.fromCharCode(o[1]);
|
||||
c[160] = String.fromCharCode(o[2]);
|
||||
return base64data ?
|
||||
`data:image/jpeg;base64,${btoa(c.concat(p).join(''))}` :
|
||||
null;
|
||||
};
|
||||
|
||||
const loadFeed = () => {
|
||||
console.log(`${NAME}: loading`);
|
||||
|
||||
document.querySelectorAll(`.${NAME}`).forEach((el, i) => {
|
||||
const ID = `InstagramFeed${i}`;
|
||||
const dataset = el.dataset;
|
||||
|
||||
el.classList.add(`${NAME}-loading`);
|
||||
el.classList.remove(`${NAME}-loaded`, `${NAME}-error`);
|
||||
|
||||
new InstagramFeed({
|
||||
username: dataset['username'],
|
||||
tag: dataset['tag'] || null,
|
||||
display_profile: dataset['display-profile'],
|
||||
display_biography: dataset['display-biography'],
|
||||
display_gallery: dataset['display-gallery'],
|
||||
display_captions: dataset['display-captions'],
|
||||
cache_time: dataset['cache_time'] || 360,
|
||||
items: dataset['items'] || 12,
|
||||
styling: false,
|
||||
lazy_load: true,
|
||||
callback: (data) => {
|
||||
console.log(`${NAME}: data response received`);
|
||||
|
||||
const list = document.createElement('div');
|
||||
list.classList.add(`${NAME}-list`, 'row');
|
||||
el.appendChild(list);
|
||||
|
||||
data['edge_owner_to_timeline_media']['edges'].forEach(
|
||||
(el, i) => {
|
||||
const item = el['node'];
|
||||
const preview = ig_media_preview(item['media_preview']);
|
||||
|
||||
list.innerHTML +=
|
||||
`<div class="a col ${NAME}-item"` +
|
||||
` data-gallery="${NAME}-${ID}" data-href="${item['display_url']}" data-toggle="lightbox" data-force="image">` +
|
||||
`<img id="${NAME}-${ID}-${item['id']}" src="${item['display_url']}" alt="${item['accessibility_caption']}"` +
|
||||
`style="background:url(${preview})" />` +
|
||||
'</div>';
|
||||
},
|
||||
);
|
||||
|
||||
el.classList.remove(`${NAME}-loading`);
|
||||
el.classList.add(`${NAME}-loaded`);
|
||||
|
||||
window.dispatchEvent(new Event('MetaWindowindow.initLinks'));
|
||||
window.dispatchEvent(new Event(`${NAME}.loaded`));
|
||||
},
|
||||
on_error: (e) => {
|
||||
console.error(`${NAME}: ${e}`);
|
||||
|
||||
el.classList.remove(`${NAME}-loading`);
|
||||
el.classList.add(`${NAME}-error`);
|
||||
|
||||
window.dispatchEvent(new Event(`${NAME}.error`));
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener(`${Events.LODEDANDREADY}`, loadFeed);
|
||||
window.addEventListener(`${Events.AJAX}`, loadFeed);
|
||||
})(window);
|
57
src/js/test-build.js
Normal file
57
src/js/test-build.js
Normal file
@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
import '../scss/test-build.scss';
|
||||
import '@a2nt/meta-lightbox-js/src/js/test-build';
|
||||
|
||||
import Events from './_events';
|
||||
import MainUI from './_main/_index';
|
||||
|
||||
/*
|
||||
* AJAX functionality
|
||||
*/
|
||||
import './_ajax/_links';
|
||||
import './_ajax/_online';
|
||||
import './_ajax/_lazy-images';
|
||||
|
||||
import './_layout';
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// mocking service worker
|
||||
const regeneratorRuntime = require('regenerator-runtime');
|
||||
const {
|
||||
worker,
|
||||
} = require('../mocks/browser');
|
||||
worker.start({
|
||||
serviceWorker: {
|
||||
url: '_graphql/mockServiceWorker.js',
|
||||
options: {
|
||||
scope: '/',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// caching service worker (set injectClient: false at webpack.config.serve.js)
|
||||
/*if ('serviceWorker' in navigator) {
|
||||
const baseHref = (document.getElementsByTagName('base')[0] || {}).href;
|
||||
const version = (document.querySelector('meta[name="swversion"]') || {})
|
||||
.content;
|
||||
if (baseHref) {
|
||||
navigator.serviceWorker
|
||||
.register(`${baseHref}app_sw.js?v=${version}`)
|
||||
.then(() => {
|
||||
console.log('SW: Registered');
|
||||
});
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
function importAll(r) {
|
||||
return r.keys().map(r);
|
||||
}
|
||||
|
||||
const images = importAll(
|
||||
require.context('../img/', false, /\.(png|jpe?g|svg)$/),
|
||||
);
|
||||
const fontAwesome = importAll(
|
||||
require.context('font-awesome', false, /\.(otf|eot|svg|ttf|woff|woff2)$/),
|
||||
);
|
30
src/scss/_layout/_forms/_basics.scss
Normal file
30
src/scss/_layout/_forms/_basics.scss
Normal file
@ -0,0 +1,30 @@
|
||||
.field {
|
||||
flex-direction: row;
|
||||
|
||||
&__label {
|
||||
padding-right: $form-spacer-x;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
&+.field__content {
|
||||
padding-left: $form-spacer-x;
|
||||
}
|
||||
}
|
||||
|
||||
.field__content {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&.CompositeField {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.field.password {
|
||||
.show-password {
|
||||
position: absolute;
|
||||
top: 0.5em;
|
||||
right: 0.5em;
|
||||
color: $input-color;
|
||||
}
|
||||
}
|
1
src/scss/_layout/_forms/_index.scss
Normal file
1
src/scss/_layout/_forms/_index.scss
Normal file
@ -0,0 +1 @@
|
||||
@import './_basics';
|
5
src/scss/_layout/_index.scss
Normal file
5
src/scss/_layout/_index.scss
Normal file
@ -0,0 +1,5 @@
|
||||
@import '../_variables';
|
||||
@import '../_animations';
|
||||
|
||||
@import './_main';
|
||||
@import './_forms';
|
32
src/scss/_layout/_main/_alerts.scss
Normal file
32
src/scss/_layout/_main/_alerts.scss
Normal file
@ -0,0 +1,32 @@
|
||||
@import '../../_variables';
|
||||
|
||||
#SiteWideAlerts {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 99999;
|
||||
|
||||
.btn-close {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-offline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.is-online {
|
||||
.alert-offline {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.is-offline {
|
||||
.alert-offline {
|
||||
display: flex;
|
||||
}
|
||||
}
|
179
src/scss/_layout/_main/_base.scss
Normal file
179
src/scss/_layout/_main/_base.scss
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* some basic styles
|
||||
*/
|
||||
|
||||
@import '../../_variables';
|
||||
@import '../../_animations';
|
||||
|
||||
html,
|
||||
body {
|
||||
min-height: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// sticky footer
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
--body-gutter-x: #{inspect($body-gutter-x)};
|
||||
--body-gutter-y: #{inspect($body-gutter-y)};
|
||||
--body-double-gutter-x: #{inspect($body-double-gutter-x)};
|
||||
--body-double-gutter-y: #{inspect($body-double-gutter-y)};
|
||||
--body-gutter-reduced-x: #{inspect($body-gutter-reduced-x)};
|
||||
--body-gutter-reduced-y: #{inspect($body-gutter-reduced-y)};
|
||||
--body-gutter-reduced-d-x: #{inspect($body-gutter-reduced-d-x)};
|
||||
--body-gutter-reduced-d-y: #{inspect($body-gutter-reduced-d-y)};
|
||||
|
||||
.wrapper {
|
||||
flex: 1 0 auto;
|
||||
margin-bottom: $element-spacer-y;
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex-shrink: 0;
|
||||
margin-top: $element-spacer-y;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $extra-large-screen) {
|
||||
|
||||
html,
|
||||
body {
|
||||
font-size: .9vw !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 80vw;
|
||||
}
|
||||
}
|
||||
|
||||
// don't let images be wider than the parent layer
|
||||
div,
|
||||
a,
|
||||
span,
|
||||
button,
|
||||
i {
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
iframe,
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
ul,
|
||||
table,
|
||||
p {
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.a {
|
||||
cursor: pointer;
|
||||
color: $link-color;
|
||||
text-decoration: $link-decoration;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: $link-hover-decoration;
|
||||
color: $link-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
// exclude bootstrap-table
|
||||
[data-toggle='table'] {
|
||||
|
||||
&:hover,
|
||||
&.active,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
[data-toggle='collapse'] {
|
||||
&[aria-expanded='true'] {
|
||||
.accordion-icon {
|
||||
&:before {
|
||||
content: '\f068';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// transactions
|
||||
.transition,
|
||||
a,
|
||||
a *,
|
||||
.a,
|
||||
.a *,
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea,
|
||||
.btn,
|
||||
.btn *,
|
||||
.dropdown,
|
||||
.row,
|
||||
.alert,
|
||||
.alert *,
|
||||
.message,
|
||||
[data-toggle],
|
||||
[data-toggle] * {
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
|
||||
.a,
|
||||
a,
|
||||
[data-toggle],
|
||||
button,
|
||||
.btn {
|
||||
|
||||
&:hover,
|
||||
&.active,
|
||||
&[aria-expanded='true'] {
|
||||
|
||||
>.fa,
|
||||
>.far,
|
||||
>.fas,
|
||||
>.fab,
|
||||
&.fa,
|
||||
&.far,
|
||||
&.fas,
|
||||
&.fab {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&[aria-expanded='true'] {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
|
||||
&:hover,
|
||||
&.active,
|
||||
&[aria-expanded='true'] {
|
||||
|
||||
>.fa,
|
||||
>.far,
|
||||
>.fas,
|
||||
>.fab,
|
||||
&.fa,
|
||||
&.far,
|
||||
&.fas,
|
||||
&.fab {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
src/scss/_layout/_main/_index.scss
Normal file
9
src/scss/_layout/_main/_index.scss
Normal file
@ -0,0 +1,9 @@
|
||||
@import '../../_variables';
|
||||
@import '../../_animations';
|
||||
|
||||
@import './_base';
|
||||
@import './_main';
|
||||
@import './_alerts';
|
||||
|
||||
// states
|
||||
@import './_states';
|
176
src/scss/_layout/_main/_main.scss
Normal file
176
src/scss/_layout/_main/_main.scss
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* some basic styles
|
||||
*/
|
||||
|
||||
.meta-MetaWindow {
|
||||
z-index: 1031;
|
||||
|
||||
.meta-nav {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 0.8s linear infinite;
|
||||
}
|
||||
|
||||
// navs
|
||||
.navbar-toggler {
|
||||
transition: transform ease 0.4s;
|
||||
}
|
||||
|
||||
.navbar-toggler-icon {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.nav-item,
|
||||
.nav-link {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
button.nav-link {
|
||||
border: 0;
|
||||
outline: 0;
|
||||
text-transform: inherit;
|
||||
letter-spacing: inherit;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
&[aria-expanded='true'] {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
position: relative;
|
||||
padding-right: 1.5em;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
right: 0.5em;
|
||||
bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-nav .dropdown-toggle.nav-link {
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
|
||||
.dropdown.show .dropdown-toggle::after,
|
||||
.dropdown-toggle.active-dropdown::after,
|
||||
.dropdown-toggle.active::after {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
will-change: max-height, display;
|
||||
overflow: hidden;
|
||||
transition: none;
|
||||
|
||||
&.show {
|
||||
animation: expand 2s;
|
||||
animation-fill-mode: both;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.dropdown-list {
|
||||
@extend .list-unstyled;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.field {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: $form-spacer-y 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-toolbar {
|
||||
margin-top: $form-spacer-y;
|
||||
}
|
||||
|
||||
// rewrite btn opacity on hover
|
||||
.btn {
|
||||
|
||||
&:hover,
|
||||
&.active,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// SS-messages
|
||||
|
||||
.alert+.alert {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
/*.message {
|
||||
@extend .alert;
|
||||
|
||||
@extend .alert-info;
|
||||
|
||||
display: block;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.message.validation,
|
||||
.message.required,
|
||||
.message.error {
|
||||
@extend .alert;
|
||||
|
||||
@extend .alert-danger;
|
||||
}
|
||||
|
||||
.message.required,
|
||||
.message.error {
|
||||
@extend .alert;
|
||||
|
||||
@extend .alert-danger;
|
||||
}*/
|
||||
|
||||
.list-group-item.active {
|
||||
|
||||
a,
|
||||
.a {
|
||||
color: $list-group-active-color;
|
||||
}
|
||||
}
|
||||
|
||||
[aria-expanded='true'] {
|
||||
.fa-bars {
|
||||
&:before {
|
||||
content: '\f00d';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsSidebarUI {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.jsSidebarUI__inner {
|
||||
position: relative;
|
||||
will-change: position, top;
|
||||
}
|
5
src/scss/_layout/_main/_states/_index.scss
Normal file
5
src/scss/_layout/_main/_states/_index.scss
Normal file
@ -0,0 +1,5 @@
|
||||
@import '../../../_variables';
|
||||
@import '../../../_animations';
|
||||
|
||||
@import './_mobile';
|
||||
@import './_network';
|
43
src/scss/_layout/_main/_states/_mobile.scss
Normal file
43
src/scss/_layout/_main/_states/_mobile.scss
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Mobile/Desktop states
|
||||
*/
|
||||
|
||||
// display dropdown on hover + focus
|
||||
@media (min-width: $full-body-min-width) {
|
||||
.dropdown-hover {
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
.dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// custom toggler for mobile view
|
||||
.dropdown {
|
||||
>.dropdown-toggle-sm {
|
||||
@media (min-width: $full-body-min-width) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
>.dropdown-toggle-fl {
|
||||
display: none;
|
||||
|
||||
@media (min-width: $full-body-min-width) {
|
||||
display: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@media not all and (hover: none) {
|
||||
>.dropdown-toggle-touch {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
>.dropdown-toggle-notouch {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
39
src/scss/_layout/_main/_states/_network.scss
Normal file
39
src/scss/_layout/_main/_states/_network.scss
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Network States
|
||||
*/
|
||||
|
||||
.loading {
|
||||
animation: fade 0.5s linear infinite;
|
||||
}
|
||||
|
||||
.graphql-page {
|
||||
&.response-404 {
|
||||
filter: grayscale(1);
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.is-offline {
|
||||
iframe {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.graphql-page {
|
||||
&.response-523 {
|
||||
filter: grayscale(1);
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.ajax-loading {
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
|
||||
#Header {
|
||||
position: relative;
|
||||
z-index: 2001;
|
||||
}
|
||||
}
|
177
src/scss/_layout/_test.scss
Normal file
177
src/scss/_layout/_test.scss
Normal file
@ -0,0 +1,177 @@
|
||||
.sidebar__col {
|
||||
position: relative;
|
||||
margin-top: $element-reduced-spacer-y;
|
||||
margin-bottom: $element-reduced-spacer-y;
|
||||
}
|
||||
.content-holder__sidebar {
|
||||
> .container {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#SiteWideMessage {
|
||||
text-align: center;
|
||||
.alert {
|
||||
margin-bottom: 0;
|
||||
.btn-close {
|
||||
margin-top: -0.5rem;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Header {
|
||||
background-color: $header-bg;
|
||||
color: $header-color;
|
||||
|
||||
a {
|
||||
color: $header-link;
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.logo {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.tagline {
|
||||
display: inline-block;
|
||||
font-size: 1.2rem;
|
||||
margin-left: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
#Navigation {
|
||||
font-size: 1.5rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.25rem;
|
||||
width: 100%;
|
||||
background: $header-bg;
|
||||
|
||||
.navbar-toggler {
|
||||
color: $main-nav-link-color;
|
||||
font-size: $main-nav-toggler-size;
|
||||
}
|
||||
|
||||
.nav-item,
|
||||
.nav-link {
|
||||
flex-direction: column;
|
||||
/*@media (min-width: $full-body-min-width) {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}*/
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: $main-nav-link-color;
|
||||
background: $main-nav-link-bg;
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
&.active {
|
||||
background: $main-nav-link-hover-bg;
|
||||
color: $main-nav-link-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
> .nav-link {
|
||||
background: $main-nav-link-hover-bg;
|
||||
color: $main-nav-link-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item .nav-dropdown {
|
||||
.fa-chevron-right
|
||||
//&:after
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border-color: $main-nav-dropdown-bg;
|
||||
background: $main-nav-dropdown-bg;
|
||||
margin-top: 0;
|
||||
border-top: 0;
|
||||
min-width: 100%;
|
||||
.nav-item-link {
|
||||
color: $main-nav-dropdown-color;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
&.active,
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: $main-nav-dropdown-hover-bg;
|
||||
.nav-item-link {
|
||||
color: $main-nav-dropdown-hover-color;
|
||||
}
|
||||
}
|
||||
.nav-item-link {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $full-body-min-width) {
|
||||
.navbar-nav > .nav-item {
|
||||
padding-right: 2rem;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
.dropdown-item .nav-item-link {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*#MainContent {
|
||||
padding-top: 2 * $element-reduced-spacer-y;
|
||||
padding-bottom: 2 * $element-reduced-spacer-y;
|
||||
}*/
|
||||
|
||||
#PageBreadcumbs {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#Footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: $footer-bg;
|
||||
color: $footer-color;
|
||||
|
||||
> .wrapper {
|
||||
padding-top: $element-reduced-spacer-y;
|
||||
padding-bottom: $element-reduced-spacer-y;
|
||||
}
|
||||
|
||||
a,
|
||||
.a {
|
||||
color: $footer-link;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-top: $element-reduced-spacer-y;
|
||||
padding-bottom: $element-reduced-spacer-y;
|
||||
background-color: $footer-footer-bg;
|
||||
|
||||
.copyright {
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
39
src/scss/_libs/_bootstrap-table.scss
Normal file
39
src/scss/_libs/_bootstrap-table.scss
Normal file
@ -0,0 +1,39 @@
|
||||
@import "~bootstrap-table/src/bootstrap-table.scss";
|
||||
|
||||
.bootstrap-table {
|
||||
.fixed-table-container {
|
||||
.table {
|
||||
thead th {
|
||||
.both, .asc, .desc {
|
||||
background-image: none;
|
||||
|
||||
&:after {
|
||||
margin-left: .5em;
|
||||
content: '';
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
|
||||
.asc:after {
|
||||
content: "\f0de";
|
||||
}
|
||||
|
||||
.desc:after {
|
||||
content: "\f0dd";
|
||||
}
|
||||
|
||||
.both:after {
|
||||
content: "\f0dc";
|
||||
}
|
||||
|
||||
.th-inner.sortable {
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
src/scss/_libs/_bootstrap.scss
Normal file
51
src/scss/_libs/_bootstrap.scss
Normal file
@ -0,0 +1,51 @@
|
||||
// Bootstrap
|
||||
// Configuration
|
||||
@import '~bootstrap/scss/functions';
|
||||
@import '~bootstrap/scss/variables';
|
||||
@import '~bootstrap/scss/mixins';
|
||||
@import '~bootstrap/scss/utilities';
|
||||
|
||||
// Layout & components
|
||||
@import '~bootstrap/scss/root';
|
||||
@import '~bootstrap/scss/reboot';
|
||||
@import '~bootstrap/scss/type';
|
||||
@import '~bootstrap/scss/containers';
|
||||
@import '~bootstrap/scss/grid';
|
||||
@import '~bootstrap/scss/tables';
|
||||
@import '~bootstrap/scss/forms';
|
||||
@import '~bootstrap/scss/buttons';
|
||||
@import '~bootstrap/scss/transitions';
|
||||
|
||||
// Optional
|
||||
//@import '~bootstrap/scss/images';
|
||||
@import '~bootstrap/scss/dropdown';
|
||||
@import '~bootstrap/scss/nav';
|
||||
@import '~bootstrap/scss/navbar';
|
||||
@import '~bootstrap/scss/breadcrumb';
|
||||
@import '~bootstrap/scss/pagination';
|
||||
@import '~bootstrap/scss/alert';
|
||||
@import '~bootstrap/scss/close';
|
||||
|
||||
/*@import '~bootstrap/scss/button-group';
|
||||
@import '~bootstrap/scss/card';
|
||||
@import '~bootstrap/scss/accordion';
|
||||
@import '~bootstrap/scss/badge';
|
||||
@import '~bootstrap/scss/progress';
|
||||
@import '~bootstrap/scss/list-group';
|
||||
@import '~bootstrap/scss/toasts';
|
||||
@import '~bootstrap/scss/modal';
|
||||
@import '~bootstrap/scss/tooltip';
|
||||
@import '~bootstrap/scss/popover';
|
||||
@import '~bootstrap/scss/spinners';*/
|
||||
|
||||
// Helpers
|
||||
@import '~bootstrap/scss/helpers';
|
||||
|
||||
// Utilities
|
||||
@import '~bootstrap/scss/utilities/api';
|
||||
|
||||
@import '../_ui/carousel';
|
||||
|
||||
.navbar {
|
||||
justify-content: flex-end;
|
||||
}
|
3
src/scss/_libs/_fontawesome.scss
Normal file
3
src/scss/_libs/_fontawesome.scss
Normal file
@ -0,0 +1,3 @@
|
||||
$fa-font-path: "~font-awesome/fonts";
|
||||
|
||||
@import "~font-awesome/scss/font-awesome";
|
57
src/scss/_libs/_silverstripe.scss
Normal file
57
src/scss/_libs/_silverstripe.scss
Normal file
@ -0,0 +1,57 @@
|
||||
.message {
|
||||
@extend .alert !optional;
|
||||
|
||||
&.warning {
|
||||
@extend .alert-warning !optional;
|
||||
}
|
||||
|
||||
&.error {
|
||||
@extend .alert-danger !optional;
|
||||
}
|
||||
}
|
||||
|
||||
.embed-responsive-4by3,
|
||||
.embed-responsive-16by9 {
|
||||
position: relative;
|
||||
padding-top: 56.25%;
|
||||
|
||||
iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.embed-responsive-4by3 {
|
||||
padding-top: 75%;
|
||||
}
|
||||
|
||||
#ForgotPassword {
|
||||
margin: 1rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#BetterNavigator {
|
||||
display: none;
|
||||
top: 50% !important;
|
||||
margin-top: -41px;
|
||||
|
||||
&.open {
|
||||
top: 0 !important;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
a,
|
||||
button,
|
||||
div,
|
||||
i,
|
||||
span {
|
||||
background-size: auto;
|
||||
}
|
||||
|
||||
@media (min-width: map-get($grid-breakpoints, 'md')) {
|
||||
display: block;
|
||||
}
|
||||
}
|
8
src/scss/_libs/_silverstripe.shop.scss
Normal file
8
src/scss/_libs/_silverstripe.shop.scss
Normal file
@ -0,0 +1,8 @@
|
||||
.cart-footer {
|
||||
margin-top: $grid-gutter-height / 2;
|
||||
}
|
||||
|
||||
.address-panel,
|
||||
.account-nav {
|
||||
margin-bottom: $grid-gutter-height / 2;
|
||||
}
|
93
src/scss/_ui/_carousel.scss
Normal file
93
src/scss/_ui/_carousel.scss
Normal file
@ -0,0 +1,93 @@
|
||||
@import '~bootstrap/scss/carousel';
|
||||
|
||||
/*
|
||||
* Bootstrap carousel improvement
|
||||
*/
|
||||
|
||||
/*.carousel-item {
|
||||
&.active {
|
||||
display: flex !important;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}*/
|
||||
|
||||
$carousel-title-color: $white !default;
|
||||
$carousel-slide-min-height: 4rem !default;
|
||||
$carousel-text-shadow: 1px 1px $black !default;
|
||||
$carousel-controls-font-size: 3rem;
|
||||
$carousel-controls-zindex: 11 !default;
|
||||
$carousel-controls-shadow: 1px 1px $black !default;
|
||||
$carousel-controls-hover-bg: transparentize($black, 0.4) !default;
|
||||
$carousel-slide-img-loading-max-height: 25vh !default;
|
||||
|
||||
.carousel-slide {
|
||||
min-height: $carousel-slide-min-height;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
>.container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.video {
|
||||
width: 100%;
|
||||
|
||||
iframe {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
.img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
img.loading {
|
||||
max-height: $carousel-slide-img-loading-max-height;
|
||||
}
|
||||
}
|
||||
|
||||
.carousel-control-prev,
|
||||
.carousel-control-next {
|
||||
z-index: $carousel-controls-zindex;
|
||||
font-size: $carousel-controls-font-size;
|
||||
text-shadow: $carousel-controls-shadow;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: $carousel-controls-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.carousel-indicators li {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.carousel-title {
|
||||
color: $carousel-title-color;
|
||||
}
|
||||
|
||||
.carousel-title,
|
||||
.carousel-content {
|
||||
text-shadow: $carousel-text-shadow;
|
||||
}
|
||||
|
||||
.carousel-caption {
|
||||
right: 0;
|
||||
left: auto;
|
||||
width: 50%;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.slide-link__media {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
34
src/scss/_ui/_flyout.scss
Normal file
34
src/scss/_ui/_flyout.scss
Normal file
@ -0,0 +1,34 @@
|
||||
$flyout-height-padding: 1rem;
|
||||
$flyout-width-padding: 2rem;
|
||||
$flyout-padding: $flyout-height-padding $flyout-width-padding;
|
||||
$flyout-bg: #000 !default;
|
||||
$flyout-color: #fff !default;
|
||||
$flyout-title-color: #fff !default;
|
||||
$flyout-transition: right 2s;
|
||||
|
||||
.flyout-FlyoutUI {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
transform: translateY(-50%);
|
||||
transition: $flyout-transition;
|
||||
right: -100%;
|
||||
top: 50%;
|
||||
background: $flyout-bg;
|
||||
color: $flyout-color;
|
||||
padding: $flyout-padding;
|
||||
|
||||
&__active {
|
||||
display: block;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
color: $flyout-title-color;
|
||||
}
|
||||
|
||||
&__close {
|
||||
position: absolute;
|
||||
top: $flyout-height-padding;
|
||||
right: $flyout-width-padding;
|
||||
}
|
||||
}
|
9
src/scss/_ui/_form.stepped.scss
Normal file
9
src/scss/_ui/_form.stepped.scss
Normal file
@ -0,0 +1,9 @@
|
||||
.form-stepped {
|
||||
.step {
|
||||
display: none !important;
|
||||
|
||||
&.active {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
}
|
14
src/scss/_ui/_lightbox.scss
Normal file
14
src/scss/_ui/_lightbox.scss
Normal file
@ -0,0 +1,14 @@
|
||||
@import '../_variables';
|
||||
|
||||
/*$lightbox-breakpoint: map-get($grid-breakpoints, 'sm') !default;
|
||||
$lightbox-link-hover-color: $link-hover-color !default;
|
||||
|
||||
@import '~@a2nt/meta-lightbox/src/scss/app';
|
||||
|
||||
.lightbox-overlay-custom {
|
||||
@extend .meta-lightbox-overlay;
|
||||
@extend .meta-lightbox-theme-default;
|
||||
@extend .meta-lightbox-effect-fade;
|
||||
// meta-lightbox-open
|
||||
}
|
||||
*/
|
53
src/scss/_ui/_mailchimp.scss
Normal file
53
src/scss/_ui/_mailchimp.scss
Normal file
@ -0,0 +1,53 @@
|
||||
#mc_embed_signup,
|
||||
.mc_embed_signup {
|
||||
padding: 2rem;
|
||||
.mc-field-group {
|
||||
@extend .form-group;
|
||||
}
|
||||
input[type='text'],
|
||||
input[type='email'] {
|
||||
@extend .form-control;
|
||||
}
|
||||
input[type='submit'] {
|
||||
@extend .btn;
|
||||
@extend .btn-primary;
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
display: block;
|
||||
}
|
||||
.clear {
|
||||
float: none;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
@extend .form-check;
|
||||
ul,
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
input[type='checkbox'] {
|
||||
@extend .form-check-input;
|
||||
}
|
||||
|
||||
label {
|
||||
@extend .form-check-label;
|
||||
}
|
||||
}
|
||||
|
||||
.mce_inline_error,
|
||||
#mce-success-response,
|
||||
#mce-error-response {
|
||||
margin-top: 1rem;
|
||||
@extend .alert;
|
||||
}
|
||||
|
||||
#mce-success-response {
|
||||
@extend .alert-success;
|
||||
}
|
||||
|
||||
.mce_inline_error,
|
||||
#mce-error-response {
|
||||
@extend .alert-danger;
|
||||
}
|
||||
}
|
134
src/scss/_ui/_map.api.scss
Normal file
134
src/scss/_ui/_map.api.scss
Normal file
@ -0,0 +1,134 @@
|
||||
@import '../_variables';
|
||||
@import '../_animations';
|
||||
|
||||
//@import "~mapbox-gl/src/css/mapbox-gl.css";
|
||||
$map-height: 30rem !default;
|
||||
|
||||
$map-marker-color: $primary !default;
|
||||
$map-marker-size: 30px !default;
|
||||
|
||||
$map-popup-font-size: 0.8rem !default;
|
||||
$map-popup-width: 16rem !default;
|
||||
$map-popup-height: 7rem !default;
|
||||
$map-popup-bg: $white !default;
|
||||
$map-popup-color: $body-color !default;
|
||||
|
||||
.mapAPI-map {
|
||||
height: $map-height;
|
||||
//margin-bottom: $grid-gutter-element-height;
|
||||
}
|
||||
|
||||
.mapboxgl {
|
||||
&-popup {
|
||||
width: $map-popup-width;
|
||||
height: $map-popup-height;
|
||||
font-size: $map-popup-font-size;
|
||||
line-height: 1.2em;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
&-popup-anchor-bottom,
|
||||
&-popup-anchor-bottom-left,
|
||||
&-popup-anchor-bottom-right {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
&-popup-content {
|
||||
min-width: $map-popup-width;
|
||||
background: $map-popup-bg;
|
||||
color: $map-popup-color;
|
||||
position: relative;
|
||||
pointer-events: auto;
|
||||
padding: 0.8rem;
|
||||
border-radius: 0.25rem;
|
||||
min-height: 5rem;
|
||||
box-shadow: 0 0.1rem 0.8rem 0 rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
&-popup-close-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 2rem;
|
||||
padding: 0.5rem;
|
||||
border-top-right-radius: 0.25rem;
|
||||
z-index: 2;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: $primary;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&-popup-tip {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 0.8rem solid transparent;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&-popup-anchor-bottom &-popup-tip {
|
||||
border-top-color: $map-popup-bg;
|
||||
align-self: center;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&-marker {
|
||||
width: $map-marker-size;
|
||||
height: $map-marker-size;
|
||||
font-size: $map-marker-size;
|
||||
line-height: 1em;
|
||||
color: $map-marker-color;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
|
||||
.marker-icon,
|
||||
.fas,
|
||||
.fab,
|
||||
.far {
|
||||
animation: pulse 0.8s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
&-cluster {
|
||||
background: $info;
|
||||
color: color-yiq($info);
|
||||
border-radius: 100%;
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
animation: pulse 0.8s linear infinite;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 140%;
|
||||
height: 140%;
|
||||
|
||||
transform: translate(-50%, -50%);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
background: $info;
|
||||
opacity: 0.2;
|
||||
border-radius: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
width: 180%;
|
||||
height: 180%;
|
||||
}
|
||||
}
|
||||
}
|
55
src/scss/_ui/_multislider.scss
Normal file
55
src/scss/_ui/_multislider.scss
Normal file
@ -0,0 +1,55 @@
|
||||
@import '../_variables';
|
||||
$grid-gutter-element-height: 2rem !default;
|
||||
|
||||
.jsMultiSlider {
|
||||
position: relative;
|
||||
display: flex;
|
||||
margin-bottom: $grid-gutter-element-height/2;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 100%;
|
||||
|
||||
&-active {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.slide {
|
||||
position: relative;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.jsMultiSlider-container {
|
||||
position: relative;
|
||||
margin-bottom: $grid-gutter-element-height/2;
|
||||
|
||||
.slider-actions {
|
||||
font-size: 2rem;
|
||||
.act {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
&-slider-prev {
|
||||
}
|
||||
&-slider-next {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsMultiSlider-slides-container {
|
||||
overflow: hidden;
|
||||
margin: 0 2rem;
|
||||
|
||||
> .slider-nav {
|
||||
position: relative;
|
||||
}
|
||||
}
|
3
src/scss/test-build.scss
Normal file
3
src/scss/test-build.scss
Normal file
@ -0,0 +1,3 @@
|
||||
@import 'app';
|
||||
@import '_layout/_test';
|
||||
@import '~bootstrap/scss/accordion';
|
Loading…
Reference in New Issue
Block a user