invisiburu / vue-picker

A native-like select field, but better
MIT License
18 stars 4 forks source link
picker vue vuejs2 vuejs3

vue-picker

A native-like select field, but better.

version min size minzip size deps count tree shaking

Mostly behaves like native <select> but accepts custom markup for the options and the opener button.

The options are navigatable from the keyboard. The opener text is easily customizable.

The package versions of 2 and higher work with Vue3. For Vue2, use prior versions of the package.

Demo

See the demo: https://invisiburu.github.io/vue-picker/

See the demo sources in demo/

The Problem

When you think about a custom selector, you usually come to provide the options as an array. For sure, it resolves some reactivity issues by default but also has some pitfalls.

One such pitfall that in most cases, you need to map your data list to an array of something like { label, value } thus spending extra resources. Additionally, you write dummy code to display an options selector.

The second pitfall is that default <select> provides a more natural way to render lists - just by giving the elements by themselves, not the arrays.

The third pitfall is the poor customization capabilities of the options. Which mostly looks similar to the problem of default <select> but a bit milder.

VuePicker resolves all of these issues.

TypeScript

Currently, VuePicker comes with no TS declarations because of the poor TS support within Vue infrastructure. Please contact me or craft an issue if you think the times have changed or you have any other arguments of introducing TypeScript to the package. Also, you're welcome to contribute.

Installation

Using unpkg:

<head>
  <script src="https://unpkg.com/vue@3"></script>
  <script src="https://unpkg.com/@invisiburu/vue-picker"></script>
  <!-- optional css -->
  <link rel="stylesheet" href="https://unpkg.com/@invisiburu/vue-picker/dist/vue-picker.min.css">
</head>

<body>
  <div id="app"></div>
  <script>
    const App = {
      template:
        '<div>' +
          '<h3>Hello world</h3>' +
          '<VuePicker v-model="mv">' +
            '<VuePickerOption value="opt0">Option 0</VuePickerOption>' +
            '<VuePickerOption value="opt1">Option 1</VuePickerOption>' +
          '</VuePicker>' +
        '</div>',
      data: function () { return { mv: 'opt1' } },
    }
    const app = Vue.createApp(App)
    app.use(window.VuePicker)
    app.mount('#app')
  </script>
</body>

Using npm:

npm i --save @invisiburu/vue-picker

Import in your project:

import { VuePicker, VuePickerOption } from '@invisiburu/vue-picker'
// optional css
import '@invisiburu/vue-picker/dist/vue-picker.min.css'

const app = createApp(/* ... */)
app.component('VuePicker', VuePicker)
app.component('VuePickerOption', VuePickerOption)

Usage

Basic:

<VuePicker v-model="color" :isAutofocus="true">
  <VuePickerOption value="">Empty</VuePickerOption>
  <VuePickerOption value="red">Red</VuePickerOption>
  <VuePickerOption value="green">Green</VuePickerOption>
  <VuePickerOption value="blue">Blue</VuePickerOption>
  <VuePickerOption value="yellow" :isDisabled="true">Yellow</VuePickerOption>
  <VuePickerOption value="teal" text="Teal">
    How about teal (Teal will be shown instead)
  </VuePickerOption>
</VuePicker>

Custom options:

<template>
  <VuePicker v-model="variant">
    <VuePickerOption value="italic-bold">
      Some <i>italics</i> or <b>bold</b>?
    </VuePickerOption>

    <VuePickerOption value="special" text="Special! Yes!">
      <div class="grid">
        <span class="title">Or something more special?</span>
        <span class="subtitle">I am a subheading!</span>
      </div>
    </VuePickerOption>
  </VuePicker>
</template>

<style scoped>
.grid {
  display: grid;
  grid: auto-flow auto / auto;
  gap: 4px;
}

.title {
  font-size: 1.05em;
  font-weight: bold;
}

.subtitle {
  font-size: 0.9em;
  color: lightgray;
}
</style>

Custom opener:

<template>
  <VuePicker v-model="custom">
    <template #opener="{ opener }">
      <span>
        <i>{{ opener.value }}</i>
        <b>{{ opener.text }}</b>
      </span>
    </template>

    <VuePickerOption value="value-1">Value 1</VuePickerOption>
    <VuePickerOption value="value-2">Value 2</VuePickerOption>
  </VuePicker>
</template>

Api

VuePicker

Props:

VuePickerOption

Props:

onKeyDown prop

Accepts a function like the following:

function onKeyDown (event: KeyboardEvent, dropdown: any, options: any) => boolean

The listener is fired in two cases:

  1. When the dropdown in open
  2. When the dropdown is closed but the opener is focused

Return false from your onKeyDown() function to prevent the default key-down handler.

Issues

In case of a bug or a suggestion, please report on the Issues page or contact me by email.

Changelog

Check the changes in CHANGELOG.md

TODO

License

MIT