Skip to content

Commit

Permalink
feat(input-select): add SInputSelect component
Browse files Browse the repository at this point in the history
  • Loading branch information
kiaking committed Feb 21, 2022
1 parent 871e8b1 commit 1d0608e
Showing 1 changed file with 235 additions and 0 deletions.
235 changes: 235 additions & 0 deletions lib/components/SInputSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
<template>
<SInputBase
class="SInputSelect"
:class="classes"
:label="label"
:note="note"
:help="help"
:error-message="errorMessage ?? true"
:validation="validation"
>
<div class="box" :class="{ focus: isFocused }">
<select
class="select"
:class="{ 'is-not-selected': isNotSelected }"
:disabled="disabled"
@focus="focus"
@blur="blur"
@change="emitChange"
>
<option
v-if="placeholder || nullable"
:value="JSON.stringify({ value: null })"
:selected="isNotSelected"
:disabled="!nullable"
>
{{ placeholder || 'Please select' }}
</option>
<option
v-for="(option, index) in options"
:key="index"
:style="{ display: option.disabled ? 'none' : undefined }"
:value="JSON.stringify(option)"
:selected="isSelectedOption(option)"
>
{{ option.label }}
</option>
</select>
<div class="icon" role="button">
<SIconChevronUp class="icon-svg up" />
<SIconChevronDown class="icon-svg down" />
</div>
</div>
</SInputBase>
</template>
<script setup lang="ts">
import { PropType, ref, computed } from 'vue'
import { Validation, Validatable } from '../composables/Validation'
import SIconChevronUp from './icons/SIconChevronUp.vue'
import SIconChevronDown from './icons/SIconChevronDown.vue'
import SInputBase from './SInputBase.vue'
type Size = 'mini' | 'small' | 'medium'
interface Option {
label: string
value: boolean | number | string
disabled?: boolean
}
const props = defineProps({
size: { type: String as PropType<Size>, default: 'small' },
label: { type: String, default: null },
note: { type: String, default: null },
help: { type: String, default: null },
placeholder: { type: String, default: null },
options: { type: Array as PropType<Option[]>, required: true },
disabled: { type: Boolean, default: false },
nullable: { type: Boolean, default: false },
errorMessage: { type: Boolean, default: true },
modelValue: { type: [String, Number, Boolean] as PropType<string | number | boolean | null>, default: null },
validation: { type: Object as PropType<Validatable>, default: null }
})
const emit = defineEmits(['update:modelValue'])
const isFocused = ref(false)
const classes = computed(() => [
props.size ?? 'small',
{ disabled: props.disabled ?? false }
])
const isNotSelected = computed(() => {
return props.modelValue === undefined || props.modelValue === null || props.modelValue === ''
})
function isSelectedOption(option: Option): boolean {
return option.value === props.modelValue
}
function focus() {
isFocused.value = true
}
function blur() {
isFocused.value = false
}
function emitChange(e: any): void {
props.validation?.$touch()
const option = JSON.parse(e.target.value)
emit('update:modelValue', option.value)
}
</script>
<style scoped lang="postcss">
.SInputSelect.mini {
.box {
height: 32px;
}
.select {
padding: 3px 30px 3px 12px;
line-height: 24px;
font-size: 14px;
}
.icon {
top: 3px;
right: 8px;
}
}
.SInputSelect.small {
.box {
height: 40px;
}
.select {
padding: 7px 30px 5px 12px;
line-height: 24px;
font-size: 16px;
}
.icon {
top: 7px;
right: 10px;
}
}
.SInputSelect.medium {
.box {
height: 48px;
}
.select {
padding: 11px 44px 11px 16px;
line-height: 24px;
font-size: 16px;
}
.icon {
top: 11px;
right: 12px;
}
}
.SInputSelect.disabled {
.box {
background-color: var(--c-bg-mute);
}
.box:hover .select {
cursor: not-allowed;
}
}
.SInputSelect.has-error {
.box {
border-color: var(--c-danger);
}
.select {
border-color: var(--c-danger);
}
}
.box {
position: relative;
border: 1px solid var(--input-border);
border-radius: 4px;
width: 100%;
color: var(--input-text);
cursor: pointer;
transition: border-color .25s, background-color .25s;
&:hover,
&.focus {
border-color: var(--input-border);
}
&:focus:not(:focus-visible) {
border-color: var(--input-border);
outline: 0;
}
}
.select {
position: relative;
z-index: 20;
display: block;
border: 0;
border-radius: 4px;
width: 100%;
background-color: transparent;
cursor: pointer;
&.select.is-not-selected {
color: var(--input-placeholder);
font-weight: 500;
}
}
.icon {
position: absolute;
z-index: 10;
cursor: pointer;
}
.icon-svg {
display: block;
width: 14px;
height: 14px;
fill: var(--input-placeholder);
}
.icon-svg.up {
margin-bottom: -4px;
}
</style>

0 comments on commit 1d0608e

Please sign in to comment.