Slider & Text Input (#12)
* WIP Slider * Text Input component * Finalized * Finishing touches * remove styles * Fix sizes and colors * Fix text input being too restrictive * Fix enter being ignored * Fixed duplicate export
This commit is contained in:
parent
74773ade62
commit
ce38d4ff6b
@ -28,6 +28,8 @@ export default {
|
|||||||
{ text: 'Pagination', link: '/components/pagination' },
|
{ text: 'Pagination', link: '/components/pagination' },
|
||||||
{ text: 'Animated Logo', link: '/components/animated-logo' },
|
{ text: 'Animated Logo', link: '/components/animated-logo' },
|
||||||
{ text: 'Text Logo', link: '/components/text-logo' },
|
{ text: 'Text Logo', link: '/components/text-logo' },
|
||||||
|
{ text: 'Slider', link: '/components/slider' },
|
||||||
|
{ text: 'Text Inputs', link: '/components/text-inputs' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
9
docs/components/slider.md
Normal file
9
docs/components/slider.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Slider
|
||||||
|
|
||||||
|
<DemoContainer>
|
||||||
|
<Slider :min="1000" :max="10000" :step="1000"/>
|
||||||
|
</DemoContainer>
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<Slider :min="1000" :max="10000" :step="1000"/>
|
||||||
|
```
|
||||||
34
docs/components/text-inputs.md
Normal file
34
docs/components/text-inputs.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Text Inputs
|
||||||
|
<DemoContainer>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Text input"
|
||||||
|
/>
|
||||||
|
</DemoContainer>
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Text input"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
<DemoContainer>
|
||||||
|
<div class="iconified-input">
|
||||||
|
<SearchIcon/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Text input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</DemoContainer>
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<div class="iconified-input">
|
||||||
|
<SearchIcon/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Text input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
@ -15,6 +15,27 @@ body {
|
|||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
// Font Sizes
|
||||||
|
--font-size-xxs: 0.625rem; //10px
|
||||||
|
--font-size-xs: 0.75rem; //12px
|
||||||
|
--font-size-sm: 0.875rem; //14px
|
||||||
|
--font-size-nm: 1rem; //16px
|
||||||
|
--font-size-md: 1.125rem; //18px
|
||||||
|
--font-size-lg: 1.25rem; //20px
|
||||||
|
--font-size-xl: 1.5rem; //24px
|
||||||
|
--font-size-2xl: 2rem; //32px
|
||||||
|
--font-size-3xl: 3rem; //48px
|
||||||
|
|
||||||
|
// Font Weights
|
||||||
|
--font-weight-regular: 400;
|
||||||
|
--font-weight-medium: 500;
|
||||||
|
--font-weight-bold: 700;
|
||||||
|
--font-weight-extrabold: 800;
|
||||||
|
|
||||||
|
--font-weight-text: var(--font-weight-medium);
|
||||||
|
--font-weight-heading: var(--font-weight-extrabold);
|
||||||
|
--font-weight-title: var(--font-weight-extrabold);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@ -30,6 +51,69 @@ a.uncolored {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
// safari iOS rounds inputs by default
|
||||||
|
// set the appearance to none to prevent this
|
||||||
|
appearance: none !important;
|
||||||
|
background: var(--color-button-bg);
|
||||||
|
color: var(--color-base);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
|
||||||
|
transition: box-shadow 0.1s ease-in-out;
|
||||||
|
min-height: 40px;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:focus-visible {
|
||||||
|
box-shadow: inset 0 0 0 transparent, 0 0 0 0.25rem var(--color-brand-shadow);
|
||||||
|
color: var(--color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&[disabled] {
|
||||||
|
opacity: 0.6;
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus::placeholder {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--color-contrast);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconified-input {
|
||||||
|
align-items: center;
|
||||||
|
display: inline-flex;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding-left: 2.25rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within svg {
|
||||||
|
color: var(--color-base);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(input) {
|
||||||
|
position: absolute;
|
||||||
|
left: 0.75rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
width: 1.25rem;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
|
|||||||
@ -36,6 +36,8 @@ html {
|
|||||||
|
|
||||||
--color-brand: var(--color-green);
|
--color-brand: var(--color-green);
|
||||||
--color-brand-highlight: rgba(0, 175, 92, 0.25);
|
--color-brand-highlight: rgba(0, 175, 92, 0.25);
|
||||||
|
--color-brand-shadow: rgba(0, 175, 92, 0.7);
|
||||||
|
--color-brand-inverted: #ffffff;
|
||||||
|
|
||||||
--shadow-inset-lg: inset 0px -2px 2px hsla(221, 39%, 11%, 0.1);
|
--shadow-inset-lg: inset 0px -2px 2px hsla(221, 39%, 11%, 0.1);
|
||||||
--shadow-inset: inset 0px -2px 2px hsla(221, 39%, 11%, 0.05);
|
--shadow-inset: inset 0px -2px 2px hsla(221, 39%, 11%, 0.05);
|
||||||
@ -70,6 +72,8 @@ html {
|
|||||||
|
|
||||||
--color-brand: var(--color-green);
|
--color-brand: var(--color-green);
|
||||||
--color-brand-highlight: rgba(27, 217, 106, 0.25);
|
--color-brand-highlight: rgba(27, 217, 106, 0.25);
|
||||||
|
--color-brand-shadow: rgba(27, 217, 106, 0.7);
|
||||||
|
--color-brand-inverted: #000;
|
||||||
|
|
||||||
--shadow-inset-lg: inset 0px -2px 2px hsla(221, 39%, 11%, 0.1);
|
--shadow-inset-lg: inset 0px -2px 2px hsla(221, 39%, 11%, 0.1);
|
||||||
--shadow-inset: inset 0px -2px 2px hsla(221, 39%, 11%, 0.05);
|
--shadow-inset: inset 0px -2px 2px hsla(221, 39%, 11%, 0.05);
|
||||||
|
|||||||
179
lib/components/base/Slider.vue
Normal file
179
lib/components/base/Slider.vue
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<template>
|
||||||
|
<div class="root-container">
|
||||||
|
<div class="slider-component">
|
||||||
|
<div class="slide-container">
|
||||||
|
<input
|
||||||
|
ref="input"
|
||||||
|
v-model="currentValue"
|
||||||
|
type="range"
|
||||||
|
:min="min"
|
||||||
|
:max="max"
|
||||||
|
:step="step"
|
||||||
|
class="slider"
|
||||||
|
:style="{
|
||||||
|
'--current-value': currentValue,
|
||||||
|
'--min-value': min,
|
||||||
|
'--max-value': max
|
||||||
|
}"
|
||||||
|
@input="onInput($refs.input.value)"
|
||||||
|
>
|
||||||
|
<div class="slider-range">
|
||||||
|
<span>
|
||||||
|
{{ min }}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{{ max }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
ref="value"
|
||||||
|
:value="currentValue"
|
||||||
|
type="text"
|
||||||
|
class="slider-input"
|
||||||
|
@change="onInput($refs.value.value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Slider",
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
default: 100
|
||||||
|
},
|
||||||
|
step: {
|
||||||
|
type: Number,
|
||||||
|
default: 10
|
||||||
|
},
|
||||||
|
forceStep: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['input'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
sliderWidth: 0,
|
||||||
|
objectPosition: 0,
|
||||||
|
startOffset: 0,
|
||||||
|
currentValue: Math.max(this.min, this.value).toString()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
inputValueValid: {
|
||||||
|
get() {
|
||||||
|
return this.$refs.value.value;
|
||||||
|
},
|
||||||
|
set(newValue) {
|
||||||
|
const parsedValue = parseInt(newValue);
|
||||||
|
if (parsedValue < this.min) {
|
||||||
|
this.currentValue = this.min.toString()
|
||||||
|
} else if (parsedValue > this.max) {
|
||||||
|
this.currentValue = this.max.toString()
|
||||||
|
} else if (!parsedValue) {
|
||||||
|
this.currentValue = this.min.toString()
|
||||||
|
} else {
|
||||||
|
this.currentValue = (parsedValue - (this.forceStep ? parsedValue % this.step : 0)).toString()
|
||||||
|
}
|
||||||
|
this.$refs.value.value = this.currentValue;
|
||||||
|
this.$emit('input', parseInt(this.currentValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onInput(value) {
|
||||||
|
this.inputValueValid = parseInt(value);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.root-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-container .slider {
|
||||||
|
width: 12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-component .slide-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-component .slide-container .slider {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
height: .25rem;
|
||||||
|
background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
var(--color-brand),
|
||||||
|
var(--color-brand) calc((var(--current-value) - var(--min-value)) / (var(--max-value) - var(--min-value)) * 100%),
|
||||||
|
var(--color-base) calc((var(--current-value) - var(--min-value)) / (var(--max-value) - var(--min-value)) * 100%),
|
||||||
|
var(--color-base) 100%
|
||||||
|
);
|
||||||
|
background-size: 100% 100%;
|
||||||
|
outline: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-component .slide-container .slider::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: .75rem;
|
||||||
|
height: .75rem;
|
||||||
|
background: var(--color-brand);
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-component .slide-container .slider::-moz-range-thumb {
|
||||||
|
width: .75rem;
|
||||||
|
height: .75rem;
|
||||||
|
background: var(--color-brand);
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-component .slide-container .slider:hover::-webkit-slider-thumb {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
transition: .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-component .slide-container .slider:hover::-moz-range-thumb {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
transition: .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-input {
|
||||||
|
width: 6rem;
|
||||||
|
margin-left: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-range {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -5,6 +5,7 @@ export { default as Card } from './base/Card.vue'
|
|||||||
export { default as Checkbox } from './base/Checkbox.vue'
|
export { default as Checkbox } from './base/Checkbox.vue'
|
||||||
export { default as Chips } from './base/Chips.vue'
|
export { default as Chips } from './base/Chips.vue'
|
||||||
export { default as Page } from './base/Page.vue'
|
export { default as Page } from './base/Page.vue'
|
||||||
|
export { default as Slider } from './base/Slider.vue'
|
||||||
export { default as AnimatedLogo } from './brand/AnimatedLogo.vue'
|
export { default as AnimatedLogo } from './brand/AnimatedLogo.vue'
|
||||||
export { default as TextLogo } from './brand/TextLogo.vue'
|
export { default as TextLogo } from './brand/TextLogo.vue'
|
||||||
export { default as Pagination } from './base/Pagination.vue'
|
export { default as Pagination } from './base/Pagination.vue'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user