🚧 Basic settings
parent
000e52e121
commit
5362713eec
@ -1,162 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {html, LitElement} from 'lit-element';
|
||||
import {countdownSplit} from '../lib/time.js';
|
||||
import styles from './santa-countdown.css';
|
||||
import {_msg} from '../magic.js';
|
||||
import {ifDefined} from 'lit-html/directives/if-defined';
|
||||
|
||||
|
||||
function pad(x) {
|
||||
if (x == null) {
|
||||
return '';
|
||||
}
|
||||
x = ~~x || 0;
|
||||
return x < 10 ? `0${x}` : x;
|
||||
}
|
||||
|
||||
|
||||
export class SantaCountdownElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
until: {type: Number}, // The specific Date to count to.
|
||||
|
||||
_currentSplit: {type: Object},
|
||||
_previousSplit: {type: Object},
|
||||
_hideOffset: {type: Number},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._currentSplit = {count: 0};
|
||||
this._previousSplit = {count: 0};
|
||||
|
||||
this.until = 0;
|
||||
this._interval = 0;
|
||||
this._hideOffset = 0;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [styles];
|
||||
}
|
||||
|
||||
_tick() {
|
||||
this._previousSplit = this._currentSplit;
|
||||
|
||||
const now = +new Date;
|
||||
const s = countdownSplit(this.until - now);
|
||||
this._currentSplit = s;
|
||||
|
||||
if (!window.Intl || !window.Intl.RelativeTimeFormat) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = ['', 'seconds', 'minutes', 'hours', 'days'][s.count] || '';
|
||||
if (!key) {
|
||||
this.removeAttribute('aria-label');
|
||||
} else {
|
||||
const formatter = new Intl.RelativeTimeFormat();
|
||||
const label = formatter.format(s[key], key);
|
||||
this.setAttribute('aria-label', label + '\n' + _msg`countdownlabel`);
|
||||
}
|
||||
}
|
||||
|
||||
update(changedProperties) {
|
||||
if (changedProperties.has('until')) {
|
||||
const now = +new Date;
|
||||
|
||||
if (this.until < now) {
|
||||
window.clearInterval(this._interval);
|
||||
this._interval = 0;
|
||||
this._currentSplit = {count: 0};
|
||||
} else if (!this._interval) {
|
||||
this._tick();
|
||||
this._interval = window.setInterval(() => this._tick(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
super.update(changedProperties);
|
||||
}
|
||||
|
||||
_animationEnd(event) {
|
||||
const node = event.target.closest('.counter-box');
|
||||
|
||||
this._previousSplit = {...this._previousSplit, [node.getAttribute('data-key')]: undefined};
|
||||
}
|
||||
|
||||
render() {
|
||||
const prev = this._previousSplit;
|
||||
const split = this._currentSplit;
|
||||
const isHuge = (split.days >= 300);
|
||||
|
||||
const classFor = (x) => {
|
||||
return (split[x] !== prev[x] && prev[x] !== undefined) ? 'anim' : '';
|
||||
};
|
||||
|
||||
// Generates the order in which these elements should be hidden.
|
||||
const hide = (value) => {
|
||||
if (!(value < 4 && isHuge)) {
|
||||
const out = value - split.count + 4;
|
||||
if (out <= 4) {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
return ifDefined(undefined);
|
||||
};
|
||||
|
||||
return html`
|
||||
<main @animationend=${this._animationEnd} class=${split.count ? '' : 'done'}>
|
||||
<svg viewBox="0 0 11 44" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMaxYMid meet">
|
||||
<path d="M11,0 C9.18902037,0.133131303 7.51223617,0.599091955 6.23781871,1.99697318 C3.68903501,4.19364368 4.09147493,7.85478539 4.42683908,10.7836552 C4.56098328,12.2481264 4.6951348,13.779115 4.62805904,15.4432835 C4.56098328,19.3706399 2.81707942,21.5007204 0,22.033295 C2.81707942,22.565797 4.56098328,24.6958775 4.62805904,28.6233065 C4.6951348,30.2874024 4.56098328,31.8184636 4.42683908,33.2163448 C4.09147493,36.1452146 3.75610345,39.8063563 6.23781871,42.0030268 C7.51223617,43.400908 9.18902037,43.93341 11,44 L11,0 Z"/>
|
||||
</svg>
|
||||
|
||||
<div class="inner">
|
||||
<div class="counter-box ${classFor('days')} ${split.days >= 100 ? 'large' : ''}" data-key="days" data-order=${hide(4)}>
|
||||
<div class="holder active">${pad(split.days)}</div>
|
||||
<div class="holder prev">${pad(prev.days)}</div>
|
||||
<h2>${_msg`countdown_days`}</h2>
|
||||
</div>
|
||||
<div class="counter-box ${classFor('hours')}" data-key="hours" data-order=${hide(3)}>
|
||||
<div class="holder active">${pad(split.hours)}</div>
|
||||
<div class="holder prev">${pad(prev.hours)}</div>
|
||||
<h2>${_msg`countdown_hours`}</h2>
|
||||
</div>
|
||||
<div class="counter-box ${classFor('minutes')}" data-key="minutes" data-order=${hide(2)}>
|
||||
<div class="holder active">${pad(split.minutes)}</div>
|
||||
<div class="holder prev">${pad(prev.minutes)}</div>
|
||||
<h2>${_msg`countdown_minutes`}</h2>
|
||||
</div>
|
||||
<div class="counter-box ${classFor('seconds')}" data-key="seconds" data-order=${hide(1)}>
|
||||
<div class="holder active">${pad(split.seconds)}</div>
|
||||
<div class="holder prev">${pad(prev.seconds)}</div>
|
||||
<h2>${_msg`countdown_seconds`}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svg viewBox="0 0 11 44" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
|
||||
<g transform="translate(5.5, 22) scale(-1, 1) translate(-5.5, -22)">
|
||||
<path d="M11,0 C9.18902037,0.133131303 7.51223617,0.599091955 6.23781871,1.99697318 C3.68903501,4.19364368 4.09147493,7.85478539 4.42683908,10.7836552 C4.56098328,12.2481264 4.6951348,13.779115 4.62805904,15.4432835 C4.56098328,19.3706399 2.81707942,21.5007204 0,22.033295 C2.81707942,22.565797 4.56098328,24.6958775 4.62805904,28.6233065 C4.6951348,30.2874024 4.56098328,31.8184636 4.42683908,33.2163448 C4.09147493,36.1452146 3.75610345,39.8063563 6.23781871,42.0030268 C7.51223617,43.400908 9.18902037,43.93341 11,44 L11,0 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('santa-countdown', SantaCountdownElement);
|
@ -1,225 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
$tick-time: 0.6s; // time for number to tick over
|
||||
$bar-color: #ff0160;
|
||||
$mobile-width: 768px;
|
||||
$height: 60px;
|
||||
$mobile-height: 52px;
|
||||
$flourish-indent: 6px;
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
will-change: transform;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
:host([hidden]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
main {
|
||||
font-family: 'Roboto', 'Arial', Sans-Serif;
|
||||
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
|
||||
// nb: we expand #counter up/down so that its layer can include the animation, otherwise the
|
||||
// outer element (or at worst, the page) needs rerendering.
|
||||
$overflow: 100px / 2; // TODO(samthor): We assume box is max 100px.
|
||||
margin: -($overflow) 0;
|
||||
padding: $overflow 0;
|
||||
z-index: 100;
|
||||
position: relative;
|
||||
height: $mobile-height;
|
||||
|
||||
@media (min-width: $mobile-width) {
|
||||
height: $height;
|
||||
}
|
||||
|
||||
filter: drop-shadow(3px 8px 0 rgba(0, 0, 0, 0.1));
|
||||
|
||||
&.done {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: var(--color-bar, #ff3333);
|
||||
height: 100%;
|
||||
margin: 0 (-$flourish-indent - 1); // extra pixel for safari
|
||||
position: relative;
|
||||
|
||||
height: $mobile-height;
|
||||
|
||||
@media (min-width: $mobile-width) {
|
||||
height: $height;
|
||||
}
|
||||
}
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
z-index: -1;
|
||||
background: var(--color-bar, #ff3333);;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: $flourish-indent;
|
||||
right: $flourish-indent;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// draws the line between counter-box elements which are not hidden
|
||||
.counter-box[data-order] + .counter-box {
|
||||
margin-left: 1px;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: 7px;
|
||||
bottom: 7px;
|
||||
width: 1px;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.counter-box {
|
||||
width: 40px;
|
||||
position: relative;
|
||||
|
||||
&.large {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
&.huge {
|
||||
min-width: 48px;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.holder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 22px;
|
||||
font-family: 'Lobster';
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
line-height: $mobile-height - 8px;
|
||||
margin-bottom: 8px;
|
||||
text-shadow: none;
|
||||
position: absolute;
|
||||
transform: translateY(0);
|
||||
will-change: transform;
|
||||
z-index: 100;
|
||||
animation-duration: $tick-time;
|
||||
|
||||
// `text-align: center` seems not to work on -ve letter-spacing
|
||||
letter-spacing: -1px;
|
||||
|
||||
&.prev {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.anim {
|
||||
.holder.active {
|
||||
animation-name: counter-step-active;
|
||||
}
|
||||
.holder.prev {
|
||||
animation-name: counter-step-prev;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $mobile-width) {
|
||||
width: 44px;
|
||||
|
||||
.holder {
|
||||
font-size: 26px;
|
||||
line-height: $height - 8px;
|
||||
}
|
||||
|
||||
&.large {
|
||||
width: 52px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 8px;
|
||||
line-height: 8px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.counter-box:not([data-order]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 386px) {
|
||||
.counter-box[data-order="3"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 422px) {
|
||||
.counter-box[data-order="2"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 458px) {
|
||||
.counter-box[data-order="1"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes counter-step-active {
|
||||
0% {
|
||||
transform: translateY(50%);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes counter-step-prev {
|
||||
0% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-50%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|