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: 'Animated Logo', link: '/components/animated-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);
|
||||
margin: 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 {
|
||||
@ -30,6 +51,69 @@ a.uncolored {
|
||||
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 {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
|
||||
@ -36,6 +36,8 @@ html {
|
||||
|
||||
--color-brand: var(--color-green);
|
||||
--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: inset 0px -2px 2px hsla(221, 39%, 11%, 0.05);
|
||||
@ -70,6 +72,8 @@ html {
|
||||
|
||||
--color-brand: var(--color-green);
|
||||
--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: 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 Chips } from './base/Chips.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 TextLogo } from './brand/TextLogo.vue'
|
||||
export { default as Pagination } from './base/Pagination.vue'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user