feat(input-number): add styles and some features for input-number

This commit is contained in:
hrsonion 2019-07-14 14:21:21 +08:00
parent 305d629c62
commit 3c36d36473
3 changed files with 356 additions and 59 deletions

View File

@ -14,31 +14,52 @@
Basic Usage
</div>
<div class="n-doc-section__view">
{{ value }}
<n-input-number v-model="value" />
{{ value1 }}
<n-input-number
v-model="value1"
/>
</div>
<div class="n-doc-section__source">
<textarea><n-input-number v-model="value" /></textarea>
<textarea v-pre><n-input-number
v-model="value1"
/>
<script>
export default {
data () {
return {
value1: null
}
}
}
</script>
</textarea>
</div>
</div>
<div class="n-doc-section">
<div class="n-doc-section__header">
Min and Max
Disabled
</div>
<div class="n-doc-section__view">
{{ value }}
<n-input-number
v-model="value"
:min="-5"
:max="5"
v-model="value2"
disabled
/>
</div>
<div class="n-doc-section__source">
<textarea><n-input-number
v-model="value"
:min="-5"
:max="5"
/></textarea>
<textarea v-pre><n-input-number
v-model="value2"
disabled
/>
<script>
export default {
data () {
return {
value2: 1
}
}
}
</script>
</textarea>
</div>
</div>
<div class="n-doc-section">
@ -46,20 +67,104 @@
Step
</div>
<div class="n-doc-section__view">
{{ value }}
{{ value3 }}
<n-input-number
v-model="value"
:min="-10"
:max="10"
step="3"
v-model="value3"
:step="2"
/>
</div>
<div class="n-doc-section__source">
<textarea><n-input-number
v-model="value"
:min="-10"
:max="10"
step="3"
v-model="value3"
:step="2"
/>
<script>
export default {
data () {
return {
value3: 2
}
}
}
</script>
</textarea>
</div>
</div>
<div class="n-doc-section">
<div class="n-doc-section__header">
Event
</div>
<div class="n-doc-section__view">
{{ value4 }}
<n-input-number
v-model="value4"
@change="handleChange"
/>
</div>
<div class="n-doc-section__source">
<textarea v-pre><n-input-number
v-model="value4"
@change="handleChange"
/>
<script>
handleChange (newValue, oldValue) {
alert(`newValue: ${newValue}; oldValue: ${oldValue}`)
}
</script>
</textarea>
</div>
</div>
<div class="n-doc-section">
<div class="n-doc-section__header">
Size
</div>
<div class="n-doc-section__view">
{{ value5 }}
<n-input-number
v-model="value5"
size="small"
/>
<n-input-number
v-model="value5"
size="medium"
/>
<n-input-number
v-model="value5"
size="large"
/>
</div>
<div class="n-doc-section__source">
<textarea v-pre><n-input-number
v-model="value5"
size="small"
/>
<n-input-number
v-model="value5"
size="medium"
/>
<n-input-number
v-model="value5"
size="large"
/></textarea>
</div>
</div>
<div class="n-doc-section">
<div class="n-doc-section__header">
Min and Max
</div>
<div class="n-doc-section__view">
{{ value6 }}
<n-input-number
v-model="value6"
:min="-2"
:max="5"
/>
</div>
<div class="n-doc-section__source">
<textarea v-pre><n-input-number
v-model="value6"
:min="-2"
:max="5"
/></textarea>
</div>
</div>
@ -73,10 +178,18 @@ export default {
mixins: [docCodeEditorMixin],
data () {
return {
value: 'plx'
value1: null,
value2: 1,
value3: 2,
value4: 3,
value5: 4,
value6: 5
}
},
methods: {
handleChange (newValue, oldValue) {
alert(`newValue: ${newValue}; oldValue: ${oldValue}`)
}
}
}
</script>

View File

@ -1,17 +1,44 @@
<template>
<div>
<button @click="minus">
-
</button><input
<div
class="n-input-number"
:class="{
[`n-input-number--${size}-size`]: true,
'n-input-number--disabled': disabled
}"
>
<button
class="n-input-number__minus-button"
:class="{
[`n-input-bumber__button--disabled`]: value !== null && safeMin !== null && value <= safeMin
}"
@click="minus"
>
<n-icon type="md-remove" />
</button>
<input
class="n-input-number__input"
type="text"
:value="value"
><button @click="add">
+
:disabled="disabled ? 'disabled' : false"
@blur="handleBlurOrEnter"
@keyup.enter="handleBlurOrEnter"
>
<button
class="n-input-number__add-button"
:class="{
[`n-input-bumber__button--disabled`]: value !== null && safeMax !== null && value >= safeMax
}"
@click="add"
>
<n-icon type="md-add" />
</button>
<div class="n-input-number__border-layer" />
</div>
</template>
<script>
import NIcon from '../../Icon/index'
const DEFAULT_STEP = 1
function parseNumber (number) {
@ -29,13 +56,12 @@ function parseNumber (number) {
export default {
name: 'NInputNumber',
model: {
prop: 'value',
event: 'change'
components: {
NIcon
},
props: {
value: {
type: [Number, String],
type: Number,
default: null
},
step: {
@ -49,6 +75,14 @@ export default {
max: {
type: [Number, String],
default: null
},
size: {
type: String,
default: 'medium'
},
disabled: {
type: Boolean,
default: false
}
},
computed: {
@ -66,42 +100,73 @@ export default {
const parsedNumber = parseNumber(this.max)
if (parsedNumber !== null) return parsedNumber
else return null
},
aValidValue () {
if (this.safeMin !== null) {
return Math.max(0, this.safeMin)
} else if (this.safeMax !== null) {
return Math.min(0, this.safeMax)
} else {
return 0
}
}
},
watch: {
value (newValue, oldValue) {
if (newValue !== null) {
if (this.safeMax !== null && newValue > this.safeMax) {
newValue = this.safeMax
}
if (this.safeMin !== null && newValue < this.safeMin) {
newValue = this.safeMin
}
}
this.$emit('change', newValue, oldValue)
/**
* newValue === oldValue won't trigger watcher!
* so the call stack won't fall in loop
*/
this.$emit('input', newValue)
}
},
created () {
this.guardCurrentValue()
if (this.value !== null) {
if (this.safeMax !== null && this.value > this.safeMax) {
this.$emit('input', this.safeMax)
} else if (this.safeMin !== null && this.value < this.safeMin) {
this.$emit('input', this.safeMin)
}
}
},
methods: {
guardCurrentValue () {
if (typeof this.value !== 'number') {
const parsedNumber = Number(this.value)
if (Number.isNaN(parsedNumber)) {
this.$emit('change', 0)
} else {
this.$emit('change', parsedNumber)
}
}
},
add () {
this.guardCurrentValue()
const previousValue = this.value
let valueAfterChange = this.value + this.safeStep
if (this.safeMax !== null && valueAfterChange > this.safeMax) {
valueAfterChange = this.safeMax
}
if (valueAfterChange !== previousValue) {
this.$emit('change', valueAfterChange)
if (this.value === null) {
this.$emit('input', this.aValidValue)
} else {
const valueAfterChange = this.value + this.safeStep
this.$emit('input', valueAfterChange)
}
},
minus () {
this.guardCurrentValue()
const previousValue = this.value
let valueAfterChange = this.value - this.safeStep
if (this.safeMin !== null && valueAfterChange < this.safeMin) {
valueAfterChange = this.safeMin
if (this.value === null) {
this.$emit('input', this.aValidValue)
} else {
const valueAfterChange = this.value - this.safeStep
this.$emit('input', valueAfterChange)
}
if (valueAfterChange !== previousValue) {
this.$emit('change', valueAfterChange)
},
handleBlurOrEnter (e) {
const value = e.target.value
if (value === '') {
this.$emit('input', null)
return
}
const parsedNumber = Number(value)
if (Number.isNaN(parsedNumber)) {
e.target.value = String(this.value)
} else {
const valueAfterChange = parsedNumber
this.$emit('input', valueAfterChange)
}
}
}

119
styles/InputNumber.scss Normal file
View File

@ -0,0 +1,119 @@
@import './mixins/mixins.scss';
@import './theme/default.scss';
@include b(input-number) {
position: relative;
border-radius: $input-number-border-radius;
background-color: $input-number-background-color;
width: 152px;
display: inline-block;
vertical-align: bottom;
& * {
outline: none;
}
&.n-input-number--disabled {
background:rgba(238,238,238,0.05);
.n-input-number__minus-button, .n-input-number__add-button {
background:rgba(238,238,238,0.05);
color:rgba(233,233,236,0.2);
pointer-events: none;
}
.n-input-number__input {
color:rgba(233,233,236,0.2);
pointer-events: none;
}
cursor: not-allowed;
}
&.n-input-number--default-size, &.n-input-number--medium-size {
height: $medium-height;
line-height: $medium-height;
.n-input-number__minus-button, .n-input-number__add-button, .n-input-number__input {
height: $medium-height;
line-height: $medium-height;
}
}
&.n-input-number--small-size {
height: $small-height;
line-height: $small-height;
.n-input-number__minus-button, .n-input-number__add-button, .n-input-number__input {
height: $small-height;
line-height: $small-height;
}
}
&.n-input-number--large-size {
height: $large-height;
line-height: $large-height;
.n-input-number__minus-button, .n-input-number__add-button, .n-input-number__input {
height: $large-height;
line-height: $large-height;
}
}
.n-input-number__border-layer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: $input-number-border-radius;
transition: box-shadow .3s $default-cubic-bezier;
pointer-events: none;
}
.n-input-number__minus-button, .n-input-number__add-button {
position: absolute;
top: 0;
padding: 0;
width: 28px;
border: none;
background-color: $input-number-button-background-color;
display: flex;
align-items: center;
justify-content: center;
color: #eeeeee;
font-size: 14px;
transition: color .3s $default-cubic-bezier, background-color .3s $default-cubic-bezier;
&:hover~.n-input-number__border-layer {
box-shadow: inset 0 0 0px 1px $main-color;
}
&:hover {
color: $main-color;
}
&:active {
transition: color .3s $default-cubic-bezier, background-color .15s $default-cubic-bezier;
background-color: rgba(99,226,183,0.12)
}
&.n-input-bumber__button--disabled {
background-color: rgba(221,238,247,0.3);
color: rgba(255, 255, 255, .2);
cursor: not-allowed;
}
}
.n-input-number__minus-button {
left: 0;
border-top-left-radius: $input-number-border-radius;
border-bottom-left-radius: $input-number-border-radius;
}
.n-input-number__add-button {
right: 0;
border-top-right-radius: $input-number-border-radius;
border-bottom-right-radius: $input-number-border-radius;
}
.n-input-number__input {
&:hover~.n-input-number__border-layer {
box-shadow: inset 0 0 0px 1px $main-color;
}
&:focus {
background-color: rgba(99,226,183,0.12);
&~.n-input-number__border-layer {
box-shadow: inset 0 0 0px 1px $main-color, 0px 0px 10px 1px #366555;
}
}
transition: color .3s $default-cubic-bezier, background-color .3s $default-cubic-bezier;
background-color: transparent;
border: none;
color: $input-number-color;
width: 84px;
text-align: center;
padding: 0 34px;
caret-color: $input-caret-color;
}
}