From 87e41214441c0fc59ad8b3c2e0f6e2384c3a119d Mon Sep 17 00:00:00 2001 From: Kacper Donat <kadet1090@gmail.com> Date: Wed, 18 Mar 2020 16:33:06 +0100 Subject: [PATCH] UI - Add UiNumericInput component --- resources/components/ui/numeric.html | 11 +++++ resources/styles/_form.scss | 8 ++-- resources/styles/main.scss | 2 + resources/ts/components/app.ts | 2 + resources/ts/components/ui/icon.ts | 4 +- resources/ts/components/ui/index.ts | 1 + resources/ts/components/ui/numeric-input.ts | 53 +++++++++++++++++++++ templates/app.html.twig | 41 +++++++++------- 8 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 resources/components/ui/numeric.html create mode 100644 resources/ts/components/ui/numeric-input.ts diff --git a/resources/components/ui/numeric.html b/resources/components/ui/numeric.html new file mode 100644 index 0000000..c2cb1ce --- /dev/null +++ b/resources/components/ui/numeric.html @@ -0,0 +1,11 @@ +<div class="input-group input-group-sm"> + <input type="text" class="form-control form-control-sm" :id="id" inputmode="numeric" v-bind="$attrs" :value="value" @blur="update"/> + <div class="input-group-append"> + <button class="btn btn-addon" type="button" @click="increment" :disabled="!canIncrement"> + <ui-icon icon="increment"/> + </button> + <button class="btn btn-addon" type="button" @click="decrement" :disabled="!canDecrement"> + <ui-icon icon="decrement"/> + </button> + </div> +</div> diff --git a/resources/styles/_form.scss b/resources/styles/_form.scss index 1101da9..7e14acc 100644 --- a/resources/styles/_form.scss +++ b/resources/styles/_form.scss @@ -3,17 +3,18 @@ label { margin-bottom: 0; margin-top: -0.2rem; display: block; + font-size: .8rem; } .label-sm { - font-size: .8rem; + font-size: .6rem; } .form-group:last-child { margin-bottom: 0; } -.form-control, .input-group-text { +.form-control, .input-group-text, .btn-addon { background: rgba($dark, .06); border: none; border-bottom: 2px solid $dark; @@ -32,7 +33,8 @@ label { } } -.input-group-append { +.input-group-append, +.input-group-append .btn + .btn { margin-left: 0; } diff --git a/resources/styles/main.scss b/resources/styles/main.scss index 58b6f1b..6be0b6c 100644 --- a/resources/styles/main.scss +++ b/resources/styles/main.scss @@ -2,9 +2,11 @@ $border-radius: 0; $border-radius-lg: $border-radius; $border-radius-sm: $border-radius; + @import "~bootstrap/scss/functions"; @import "~bootstrap/scss/variables"; +$form-group-margin-bottom: $form-group-margin-bottom / 2; $primary: #005ea8; $custom-control-indicator-checked-bg: $dark; diff --git a/resources/ts/components/app.ts b/resources/ts/components/app.ts index 2f74e9f..c8cfd93 100644 --- a/resources/ts/components/app.ts +++ b/resources/ts/components/app.ts @@ -30,6 +30,8 @@ export class Application extends Vue { } }; + private count = 8; + private intervals = { messages: null, departures: null }; get messages() { diff --git a/resources/ts/components/ui/icon.ts b/resources/ts/components/ui/icon.ts index 419db53..e2928d5 100644 --- a/resources/ts/components/ui/icon.ts +++ b/resources/ts/components/ui/icon.ts @@ -23,7 +23,7 @@ import { faTimes, faTrashAlt } from "@fortawesome/pro-light-svg-icons"; -import { faClock as faClockBold, faCodeCommit, faSpinnerThird } from "@fortawesome/pro-regular-svg-icons"; +import { faClock as faClockBold, faCodeCommit, faMinus, faPlus, faSpinnerThird } from "@fortawesome/pro-regular-svg-icons"; import { faExclamationTriangle as faSolidExclamationTriangle, faWalking } from "@fortawesome/pro-solid-svg-icons"; import { fac } from "../../icons"; import { FontAwesomeIcon, FontAwesomeLayers, FontAwesomeLayersText } from "@fortawesome/vue-fontawesome"; @@ -81,6 +81,8 @@ const definitions: Dictionary<Icon> = { 'map': simple(faMapMarkerAlt), 'stop': simple(faSign), 'spinner': simple(faSpinnerThird, { spin: true }), + 'increment': simple(faPlus, { "fixed-width": true }), + 'decrement': simple(faMinus, { "fixed-width": true }), 'departure-warning': stack([ {icon: faClockBold}, {icon: faSolidExclamationTriangle, transform: "shrink-5 down-4 right-6"} diff --git a/resources/ts/components/ui/index.ts b/resources/ts/components/ui/index.ts index 9647ef5..8814273 100644 --- a/resources/ts/components/ui/index.ts +++ b/resources/ts/components/ui/index.ts @@ -1,2 +1,3 @@ export * from './switch'; export * from './icon'; +export * from './numeric-input' diff --git a/resources/ts/components/ui/numeric-input.ts b/resources/ts/components/ui/numeric-input.ts new file mode 100644 index 0000000..f8ef7e9 --- /dev/null +++ b/resources/ts/components/ui/numeric-input.ts @@ -0,0 +1,53 @@ +import Vue from 'vue' +import { Component, Prop } from 'vue-property-decorator' +import * as uuid from "uuid"; + +@Component({ + template: require('../../../components/ui/numeric.html'), + inheritAttrs: false +}) +export class UiNumericInput extends Vue { + @Prop({ + type: String, + default: () => `uuid-${uuid.v4()}` + }) + id: string; + + @Prop(Number) + value: number; + + @Prop({ type: Number, default: 1 }) + step: number; + + @Prop({ type: Number, default: -Infinity }) + min: number; + + @Prop({ type: Number, default: Infinity }) + max: number; + + update(ev) { + this.$emit('input', this.clamp(Number.parseInt(ev.target.value))); + } + + increment() { + this.$emit('input', this.clamp(this.value + this.step)); + } + + decrement() { + this.$emit('input', this.clamp(this.value - this.step)); + } + + clamp(value: number) { + return Math.max(Math.min(value, this.max), this.min); + } + + get canIncrement(): boolean { + return this.max - this.value > Number.EPSILON * 2; + } + + get canDecrement(): boolean { + return this.value - this.min > Number.EPSILON * 2; + } +} + +Vue.component('UiNumericInput', UiNumericInput); diff --git a/templates/app.html.twig b/templates/app.html.twig index 0817d50..eb64bd6 100644 --- a/templates/app.html.twig +++ b/templates/app.html.twig @@ -73,25 +73,34 @@ </button> <portal to="popups"> <popper reference="settings-departures" v-if="visibility.departures" arrow placement="left-start" @leave="visibility.departures = false"> - <h3 class="popper__heading flex"> - <label class="text" for="departures-auto-refresh"> - <ui-icon icon="refresh" fixed-width></ui-icon> - autoodświeżanie - </label> - <ui-switch id="departures-auto-refresh" v-model="autorefresh.departures.active" class="flex-space-left"></ui-switch> - </h3> - <div class="flex" v-if="autorefresh.departures.active"> - <label for="departures-auto-refresh-interval" class="text"> - <span class="sr-only">częstotliwość odświeżania</span> - co - </label> - <div class="input-group input-group-sm"> - <input type="text" class="form-control form-control-sm form-control-simple" id="departures-auto-refresh-interval" v-model="autorefresh.departures.interval"/> - <div class="input-group-append"> - <span class="input-group-text" aria-label="sekund">s</span> + <div class="form-group"> + <h3 class="popper__heading flex"> + <label class="text" for="departures-auto-refresh-interval"> + <ui-icon icon="refresh" fixed-width></ui-icon> + autoodświeżanie + </label> + <ui-switch id="departures-auto-refresh" v-model="autorefresh.departures.active" class="flex-space-left"></ui-switch> + </h3> + <div class="flex " v-if="autorefresh.departures.active"> + <label for="departures-auto-refresh-interval" class="text"> + <span class="sr-only">częstotliwość odświeżania</span> + co + </label> + <div class="input-group input-group-sm"> + <input type="text" class="form-control form-control-sm form-control-simple" id="departures-auto-refresh-interval" v-model="autorefresh.departures.interval"/> + <div class="input-group-append"> + <span class="input-group-text" aria-label="sekund">s</span> + </div> </div> </div> </div> + <div class="form-group"> + <label class="text" for="departures-count"> + <ui-icon icon="line-bus" fixed-width></ui-icon> + Liczba wpisów + </label> + <ui-numeric-input id="departures-count" v-model="count" :min="1" :max="16"></ui-numeric-input> + </div> </popper> </portal> </header>