Skip to content

Instantly share code, notes, and snippets.

@7studio
Created December 18, 2019 08:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 7studio/ed5ad858ea7d52792a6d71b2193d6a7e to your computer and use it in GitHub Desktop.
Save 7studio/ed5ad858ea7d52792a6d71b2193d6a7e to your computer and use it in GitHub Desktop.
Timeline
<template>
<div class="ckpt-Timeline">
<dl>
<dt class="cds-u-hidden">Start date</dt>
<dd class="ckpt-Timeline-item ckpt-Timeline-item--start">
<span class="ckpt-Timeline-date">{{ start | getFullYear }}</span>
</dd>
<dt class="cds-u-hidden">End date</dt>
<dd class="ckpt-Timeline-item ckpt-Timeline-item--end">
<span class="ckpt-Timeline-date">{{ end | getFullYear }}</span>
</dd>
</dl>
<ul>
<slot />
</ul>
<div
:style="{left: `${todayPosition}%`, transform: 'translateX(-50%)'}"
class="ckpt-Timeline-today"
aria-hidden="true" />
</div>
</template>
<script>
export default {
name: "CkptTimeline",
components: {
},
inheritAttrs: false,
props: {
start: {
type: Date,
required: true,
},
end: {
type: Date,
required: true,
}
},
data() {
return {}
},
computed: {
startTimestamp() {
return this.getTimestamp(this.start);
},
endTimestamp() {
return this.getTimestamp(this.end);
},
todayPosition() {
return ((this.getTimestamp(new Date) - this.startTimestamp) * 100) / (this.endTimestamp - this.startTimestamp);
}
},
methods: {
getTimestamp(date) {
return Math.round( date.getTime() / 1000 );
}
},
filters: {
getFullYear: (date) => {
if (!date) return '';
return date.getFullYear();
},
getMonthAndFullYear: (date) => {
if (!date) return '';
return `${date.getMonth() + 1}/${date.getFullYear()}`;
}
}
}
</script>
<style lang="scss">
.ckpt-Timeline {
// This
$this: &;
// Styles
align-items: center;
background: linear-gradient(to right, rgba(0, 174, 199, .3), rgba(0, 174, 199, .3)) 50% / calc(100% - 16px) 14px no-repeat;
display: flex;
height: 3.9rem;
position: relative;
&::before {
border-top: 1px dashed #9aa5b8;
content: "";
display: block;
flex: 1 1 100%;
height: 0;
/*outline: 6px solid red;*/
transform: translateY(1px);
}
&-item {
align-items: center;
cursor: pointer;
display: flex;
flex-flow: column;
left: -8px;
position: absolute;
top: 1.3em;
white-space: nowrap;
&::before {
background-color: #fff;
border: 2px solid #00aec7;
border-radius: 50%;
box-sizing: content-box;
content: "";
height: 16px;
width: 16px;
}
&--start {
align-items: flex-start;
cursor: default;
top: 0;
}
&--end {
align-items: flex-end;
cursor: default;
left: auto;
right: 0;
top: 0;
}
&--planned {
&::before {
border-style: dashed;
}
#{$this}-label,
#{$this}-date {
opacity: .5;
}
}
}
&-today {
align-items: center;
display: flex;
flex-flow: column;
height: 100%;
justify-content: flex-end;
left: 0;
position: absolute;
top: 0;
width: 0;
white-space: nowrap;
&::before {
background-color: red;
border: 2px solid red;
border-radius: 50%;
box-sizing: content-box;
content: "";
height: 8px;
margin: 4px;
width: 8px;
}
&::after {
color: red;
content: "Today";
font-size: 12px;
line-height: 1.3rem;
}
}
&-inner {
align-items: center;
display: flex;
flex-flow: column;
transform: translateY(-100%) translateY(1.3em);
&::before {
background-color: #fff;
border: 2px solid #00aec7;
border-radius: 50%;
box-sizing: content-box;
content: "";
height: 16px;
visibility: hidden;
width: 16px;
}
// Transitions
&-enter-active,
&-leave-active {
opacity: 1;
pointer-events: none;
transform: translateY(-100%) translateY(1.3em) scale(1);
transition: opacity .3s ease
, transform .3s ease;
}
&-enter,
&-leave-to {
opacity: 0;
pointer-events: none;
transform: translateY(-100%) translateY(1.3em) scale(0);
}
}
&-date {
order: -2;
}
&-label {
font-weight: 500;
}
}
</style>
<template>
<!-- v-if="position > 0 && position < 100" -->
<li
:style="{left: `${position}%`, transform: 'translateX(-50%)'}"
:class="classNames"
class="ckpt-Timeline-item"
@mouseenter="hover = true"
@mouseleave="hover = false"
>
<transition name="ckpt-Timeline-inner">
<div
v-show="hover"
ref="inner"
class="ckpt-Timeline-inner"
>
<b class="ckpt-Timeline-label">{{ label }}</b>
<span class="ckpt-Timeline-date">{{ start | getFullYear }}</span>
</div>
</transition>
</li>
</template>
<script>
export default {
name: "CkptTimelineItem",
inheritAttrs: false,
props: {
label: {
type: String,
default: ''
},
start: {
type: Date,
required: true,
},
end: {
type: Date,
required: true,
},
status: {
type: String,
default: ''
}
},
data() {
return {
hover: false
}
},
computed: {
classNames() {
return [
`ckpt-Timeline-item--${this.status.toLocaleLowerCase().replace(/\s/g, '')}`
];
},
position() {
return ((this.getTimestamp(this.start) - this.$parent.startTimestamp) * 100) / (this.$parent.endTimestamp - this.$parent.startTimestamp);
}
},
methods: {
getTimestamp(date) {
return Math.round( date.getTime() / 1000 );
}
},
filters: {
getFullYear: (date) => {
if (!date) return '';
return date.getFullYear();
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment