Skip to content

Instantly share code, notes, and snippets.

@viniazvd
Last active May 18, 2020 19:36
Show Gist options
  • Save viniazvd/a7719e339925c7d3af87df0d2a3c0f1e to your computer and use it in GitHub Desktop.
Save viniazvd/a7719e339925c7d3af87df0d2a3c0f1e to your computer and use it in GitHub Desktop.
<template>
<div ref="steppers" class="c-steppers" :style="lineSizes">
<div
v-for="(step, index) in steps"
:key="index"
:class="stepClasses(index)"
:style="{ width: `${100 / steps.length}%` }"
>
<c-flag
class="icon"
circled-border
:icon-size="22"
:size="iconSize"
:grey="index > active"
:success="index < active"
:primary="index === active"
:icon="index < active ? 'check' : step.icon"
:style="{ '--line': 100 / steps.length }"
/>
<span v-if="!isMobile" class="label">{{ step.label }}</span>
</div>
</div>
</template>
<script>
import { MediaQuery } from '../../mixins'
export default {
name: 'CSteppers',
mixins: [ MediaQuery ],
props: {
steps: {
type: Array,
default: () => ([
{
icon: 'user',
label: 'Foto de perfil'
}
])
},
active: { type: Number, default: 0 },
stepSize: {
type: Object,
default: () => ({ mobile: 40, desktop: 50 })
}
},
mounted () {
this.setLineSizes()
// future improvement proposal
// this.observer = new MutationObserver(this.setLineSizes)
// this.observer.observe(this.$refs.steppers, { childList: true })
// window.addEventListener('resize', this.setLineSizes)
// this.$refs.steppers.addEventListener('scroll', this.setLineSizes)
},
data () {
return {
sizes: { left: '', width: '' }
// observer: null, // future improvement proposal
}
},
computed: {
// step size of the icon (mobile or desktop)
iconSize () {
const { mobile, desktop } = this.stepSize
return this.isMobile ? mobile : desktop
},
lineSizes () {
return {
'--left': this.sizes.left,
'--width': this.sizes.width,
'--top': (this.iconSize / 2) - 5 // 5 = border diff
}
}
},
methods: {
stepClasses (index) {
return [
'step',
{
'--complete': index < this.active,
'--current': index === this.active,
'--incomplete': index > this.active,
'--is-last': index === this.steps.length - 1
}
]
},
setLineSizes () {
if (!this.$refs.steppers) return {}
setTimeout(() => {
const { width } = this.$refs.steppers.getBoundingClientRect()
this.sizes.left = (this.iconSize - 5) + 'px' // 5 = border diff
this.sizes.width = ((width / this.steps.length) - this.iconSize) + 'px'
}, 800)
}
},
// future improvement proposal
// beforeDestroy () {
// this.observer.disconnect()
// window.removeEventListener('resize', this.setLineSizes)
// this.$refs.steppers.removeEventListener('scroll', this.setLineSizes)
// }
}
</script>
<style lang="scss">
.c-steppers {
width: 100%;
position: relative;
display: flex;
justify-content: space-between;
& > .step {
border: 0;
outline: none;
position: relative;
background: transparent;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
& > .icon {
position: relative;
&:before { background: map-get($text-color, base-10); }
&:after {
height: 1px;
content: "";
top: var(--top);
position: absolute;
left: calc(var(--left));
width: calc(var(--width));
background: $base-border-color;
}
}
& > .label {
font-size: 11px;
margin-top: 10px;
line-height: 14px;
letter-spacing: 0;
text-transform: uppercase;
font-weight: $title-font-weight;
font-family: $title-font-family;
-webkit-font-smoothing: antialiased;
}
&.--current {
& > .label { color: map-get($text-color, base-80); }
}
&.--complete {
& > .label { color: map-get($text-color, base-50); }
}
&.--incomplete {
& > .label { color: map-get($text-color, base-30); }
& > .icon { background: map-get($text-color, base-10); }
}
&.--is-last > .icon::after { display: none; }
}
}
</style>
<template>
<span :class="classes" :style="styles">
<c-icon :size="iconSize" v-bind="$attrs" />
</span>
</template>
<script>
import CIcon from '../CIcon'
/**
* A very simple component meant to be used similarly to a tag.
* Accepts all the same props as CIcon.
*/
export default {
name: 'CFlag',
components: { CIcon },
props: {
/**
* The size of the flag.
*/
size: {
type: [String, Number],
default: 40,
validator: size => typeof +size === 'number' && +size > 0
},
/**
* The size of the icon.
*/
iconSize: {
type: [String, Number],
validator: size => typeof +size === 'number' && +size > 0
},
/**
* Changes the bg-color to our primary color.
*/
primary: Boolean,
/**
* Changes the bg-color to our success color (usually green).
*/
success: Boolean,
/**
* Changes the bg-color to our error color (usually red).
*/
error: Boolean,
/**
* Changes the bg-color to a gray(ish).
*/
grey: Boolean,
/**
* Changes the bg-color to a white.
*/
white: Boolean,
/*
* Make the button to have a border circled around it
*/
circledBorder: Boolean
},
computed: {
classes () {
return ['c-flag',
{
'-primary': this.primary,
'-success': this.success,
'-error': this.error,
'-grey': this.grey,
'-white': this.white,
'-circled-border': this.circledBorder
}
]
},
styles () {
const size = this.size + 'px'
return {
width: size,
height: size,
minWidth: size,
minHeight: size
}
}
}
}
</script>
<style lang="scss">
.c-flag {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
padding: 6px;
border-radius: 100%;
& > .c-icon {
fill: #FFF;
filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, .3));
}
&.-primary {
background: set-linear-gradient(125deg);
box-shadow: 0 2px 20px -2px rgba($primary-color, .5);
}
&.-success {
background: set-linear-gradient(125deg, $positive-color-map);
box-shadow: 0 2px 20px -2px rgba($positive-color, .5);
}
&.-error {
background: set-linear-gradient(125deg, $negative-color-map);
box-shadow: 0 2px 20px -2px rgba($negative-color, .5);
}
&.-grey {
z-index: 0;
position: relative;
background: rgba(18, 30, 72, 0.2);
box-shadow: 0 5px 15px -2px map-get($text-color, base-10);
&::before {
top: 60%;
left: 50%;
content: '';
z-index: -1;
width: 30px;
height: 30px;
filter: blur(5px);
position: absolute;
border-radius: 25px;
transform: translate(-50%, -50%);
background: map-get($text-color, base-05);
}
& > .c-icon { filter: unset; }
}
&.-white {
box-shadow: unset;
background: rgba(255,255,255,0.3);
box-shadow: 0 2px 15px 0px map-get($text-color, base-10);
}
&.-circled-border {
border: 5px solid #fff;
// height: 50px !important;
// min-width: 50px !important;
// min-height: 50px !important;
// border-radius: 50px !important;
box-shadow: 0 0 0 1px rgba(18, 30, 72, 0.1);
}
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment