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:
Adrian O.V 2023-03-06 17:26:10 -05:00 committed by GitHub
parent 74773ade62
commit ce38d4ff6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 313 additions and 0 deletions

View File

@ -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' },
],
},
],

View File

@ -0,0 +1,9 @@
# Slider
<DemoContainer>
<Slider :min="1000" :max="10000" :step="1000"/>
</DemoContainer>
```vue
<Slider :min="1000" :max="10000" :step="1000"/>
```

View 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>
```

View File

@ -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;

View File

@ -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);

View 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>

View File

@ -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'