Skip to content

Latest commit

 

History

History
651 lines (494 loc) · 23.4 KB

README.md

File metadata and controls

651 lines (494 loc) · 23.4 KB

Vue Spruce

Build codecov Maintainability

A collection of useful Vue 2 renderless components.

Check out the demo

Install

Package

yarn add -D @crishellco/vue-spruce
# or
npm i -D @crishellco/vue-spruce

Vue Plugin

Installs all components globally.

import Vue from 'vue';
import VueSpruce from '@crishellco/vue-spruce';

Vue.use(VueSpruce);
// or with options
Vue.use(VueSpruce, { componentPrefix: 's' });

Nuxt Module

Installs all components globally.

// nuxt.config.js
{
  modules: [['@crishellco/vue-spruce/nuxt', { componentPrefix: 's' }]];
}

Options

Name Description Default
componentPrefix The prefix used when installing components globally spruce

Named Imports

Alternatively, use only the components you need.

import {
  SpruceAtLeast,
  SpruceCling,
  SpruceEvent,
  SpruceFetch,
  SpruceFunction,
  SprucePaginate,
  SpruceSearch,
  SpruceSort,
  SpruceState,
  SpruceToggle,
  SpruceWatch,
} from '@crishellco/vue-spruce';

export default {
  components: {
    SpruceAtLeast,
    SpruceCling,
    SpruceEvent,
    SpruceFetch,
    SpruceFunction,
    SprucePaginate,
    SpruceSearch,
    SpruceSort,
    SpruceState,
    SpruceToggle,
    SpruceWatch,
  },
};

The Components

SpruceAtLeast

Ensures a component shows for at least a given amount of time, in milliseconds, before hiding.

<spruce-at-least :ms="5000" :show="shouldShowImage">
  <div slot-scope="{ disabled, show }">
    <img v-if="show" src="https://placebeard.it/g/200/300" alt="" />

    <button :disabled="disabled" @click="shouldShowImage = !shouldShowImage">
      {{ disabled ? 'waiting...' : show ? 'hide' : 'show' }}
    </button>
  </div>
</spruce-at-least>

Props

Name Description Type Required Default
ms Minimum amount of time to show, in milliseconds Number Yes
show Weather or not to show the contents (given enough time has passed) Boolean No true

Slots

Name Required
default Yes

Slot Scope

Slot Name Description Type
default disabled True if waiting to hide content after ms time has passed Boolean
default show If the contents should be shown Boolean

SpruceCling

Clings the clinger slot's contents to the anchor slot's contents using popper.js. Great for things like dropdown menus. See the demo for more context.

<spruce-cling placement="bottom">
  <template #anchor>
    <button>
      i'm a button
    </button>
  </template>
  <template #clinger>
    <div>
      <div>i'm a clinger!</div>
    </div>
  </template>
</spruce-cling>

Props

Name Description Type Required Default
modifiers The popper.js modifiers Array No []
placement The popper.js placement of the clinger in relation to the anchor String No auto

Slots

Name Required
anchor Yes
clinger Yes

Slot Scope

Slot Name Description Type
anchor update Updates the popper instance Function
clinger update Updates the popper instance Function

SpruceEvent

Track any window event occurance inside or outside of SpruceEvent's default slot.

<spruce-event event="mouseover" @mouseover="someMethod">
  <button>Hover over me!</button>
</spruce-event>

Props

Name Description Type Required Default
event The event to listen to String Yes
immediate First event immediately (in mounted) Boolean No false
outside Listen for the even only outside of the default slot elements Boolean No false

Events

Name Description Payload
Same as the event prop Fired when the event happens. --

Slots

Name Required
default No

SpruceFetch

Make asynchronous API fetch calls.

<spruce-fetch url="https://dog-api.kinduff.com/api/facts" >
  <div slot-scope="{ loading, data, error, fetch }">
    <loading-indicator v-if="loading" />
    <div v-else-if="errors">Errors! {{ error.status }}</div>
    <div v-else>Data: {{ data }}</div>
    <button @click="fetch">Refetch</button>
  <div>
</spruce-fetch>

Props

Name Description Type Required Default
url The API url (changing this will refetch and rest all data) String Yes
immediate Weather to immediate make the request on mount Boolean No true

Events

Name Description Payload
success Fires when the request is successful {data: Object, fetch: Function}
error Fires when the request fails {data: Object, fetch: Function}

Slots

Name Required
default Yes

Slot Scope

Slot Name Description Type
default calls Number of calls made Number
default data The response on a successful request Object
default error The response on a failed request { data: Object, status: Number }
default loading Whether a request is in progress Boolean
default fetch Makes another request Function

SpruceFunction

Create reusable functions on the fly (great for lists!).

<div v-for="num in 10">
  <spruce-function :fn="() => alert(num)">
    <button slot-scope="{ fn }" @click="fn">Click me!</button>
  </spruce-function>
</div>

Props

Name Description Type Required Default
fn The function Function Yes

Slots

Name Required
default Yes

Slot Scope

Slot Name Description Type
default fn The function Function

SprucePaginate

Paginate an array and navigate through it's chunks.

<spruce-paginate :list="states" :size="15">
  <div slot-scope="{ page, next, prev, pageNum, totalPages, isFirst, isLast, rangeStart, rangeEnd }">
    <button :disabled="isFirst" @click="prev">
      prev
    </button>
    <div class="px-4 flex flex-col items-center">
      <div>Page: {{ pageNum }}/{{ totalPages }}</div>
      <div>Showing: {{ rangeStart }} - {{ rangeEnd }} of {{ states.length }}</div>
    </div>
    <button :disabled="isLast" @click="next">
      next
    </button>
  </div>
</spruce-paginate>

Props

Name Description Type Required Default
size Page size Number Yes
list The items to paginate Array Yes

Slots

Name Required
default Yes

Slot Scope

Slot Name Description Type
default first Go to first page Function
default go Go to specific page Function
default isFirst If currently on first page Boolean
default isLast If currently on last page Boolean
default last Go to last page Function
default next Go to next page Function
default page The current page any
default pages The chunked pages Array
default links A calculated array of specific page numbers that can be used for links [1, 2, 3, '...', 40] Array
default pageNum The current page number Number
default prev Go to previous page Function
default rangeEnd The end of the current page Number
default rangeStart The start of the current page Number
default reset Reset the state of pagination Function
default totalPages Total number of pages Number

SpruceSearch

Search an array of strings or objects by keys using fuse.js.

<spruce-search :list="states" :term="term" :keys="['name', 'email']">
  <div slot-scope="{ results }">
    <div v-for="(item, index) in results" :key="index">
      {{ item }}
    </div>
  </div>
</spruce-search>

Props

Name Description Type Required Default
keys The keys to search in Array No If list is Array<Object> then all of the first object's keys. Otherwise [].
list The list to search Array Yes
term The terms to search for String No ''
threshold Fuse.js match threshold Float No 0.6

Slots

Name Required
default Yes

Slot Scope

Slot Name Description Type
default results The searched list Array

SpruceSort

Sort an array of strings or objects in either direction by specific keys.

Note: string sorting is case insensitive.

<spruce-sort :list="people" :by="by" :direction="direction">
  <div slot-scope="{ results }">
    <div v-for="(item, index) in results" :key="index">
      {{ item }}
    </div>
  </div>
</spruce-sort>

Props

Name Description Type Required Default
list The list to search Array Yes
direction The direction to sort in, 'asc' or 'desc' String No 'asc'
by The object property to sort by String No ''

Events

Name Description Payload
change Fired when results change (the list is sorted). results

Slots

Name Required
default Yes

Slot Scope

Slot Name Description Type
default results The searched list Array<String, Object>

SpruceState

Create and manage localized state.

<spruce-state :value="{ count: 0 }">
  <div slot-scope="{ count, set }">
    <button @click="set({count: count + 1})">
      Increment ({{ count }})
    </button>
  </div>
</spruce-state>

Props

Name Description Type Required Default
value The state object Object Yes

Events

Name Description Payload
input Fired when state updates state

Slots

Name Required
default Yes

Slot Scope

Slot Name Description Type
default [key] Each key in the state prop Any
default set(newValue) Merges newValue with the current state Function

SpruceTagInput

Renderless tag input.

<template>
  <spruce-tag-input v-model="colors" :validator="validator">
    <div
      slot-scope="{ events, focusedTagIndex, invalid, remove, state, tags }"
      :class="{ 'border-red-500': invalid }"
    >
      <button
        v-for="(tag, index) in tags"
        :key="index"
        type="button"
        :class="{ 'bg-red-500': focusedTagIndex === index }"
        title="Remove tag"
        @click="remove(tag)"
      >
        <span>{{ tag }}</span>
        <span>&times;</span>
      </button>
      <input v-bind="state" placeholder="Add tag with letters only..." v-on="events" />
    </div>
  </spruce-tag-input>
</template>

<script>
export default {
  data() {
    return { colors: ['red', 'blue'] };
  },

  methods: {
    validator(tag) {
      return /^[a-zA-Z]+$/.test(tag);
    },
  },
};
</script>

Props

Name Description Type Required Default
allowDuplicates Allows duplicate tags Boolean No False
allowPaste Allows pasting to automatically create tags Boolean No False
disabled Disables all interactions Boolean No False
keepOnBackspace Disables deleting last tab on keyup.backspace in the input Boolean No False
maxTags Number of allowed tags Number No Null
separator Separator used to process pasted tags String No \t
v-model The tags Array Yes
validator Function that receives the String argument tag and returns true or false to determine the validity of the input's value Function No () => true

Slot Scope

Slot Name Description Type
default events Events to listen for on the input. input for binding value, keydown.backspace for delete last tag, keydown.enter for adding new unique tag, and keydown.escape for clearing input. Object
default focusedTagIndex Currently focused tag index (to be removed on next keydown.backspace). Used for styling. String
default remove() Removes a tag Function
default state State to bind to the input. value of the input. Object
default tags Array of tags Array

SpruceToggle

Toggle between on (true) and off (false).

<spruce-toggle :value="true">
  <div slot-scope="{ isOn, on, off, toggle }">
    <div>
      <span>Accordion header</span>
      <span @click="toggle">{{ isOn ? '▲' : '▼' }}</span>
    </div>
    <div v-if="isOn">
      Accordion content
    </div>
    <div>
      <button @click="on">Open</button>
      <button @click="off">Close</button>
    </div>
  </div>
</spruce-toggle>

Props

Name Description Type Required Default
value The state of the toggle Boolean No False

Events

Name Description Payload
input Fired when isOn updates isOn

Slots

Name Required
default Yes

Slot Scope

Slot Name Description Type
default isOn The state of the toggle Boolean
default on() Sets isOn to true Function
default off() Sets isOn to false Function
default toggle() Toggles isOn Function

SpruceWatch

Watches variables for changes and emits events when changes occur.

<spruce-watch :watch="{count}" @changed="handleAnyChange" @changed:count="handleCountChange">
  <button @click="count++">
    count++ ({{ count }})
  </button>
</spruce-watch>

Props

Name Description Type Required Default
watch Values you wish to watch Object Yes

Events

Name Description Payload
changed Fired when any value in watch changes {key: count, oldValue: 0, newValue: 1}
changed:[key] Fired when a specific value in watch changes {oldValue: 0, newValue: 1}

Slots

Name Required
default No

Examples

See the demo source code for real-world examples. Check out the demo to see the components in action with code examples.

Development

Lint

yarn lint

Test

yarn test:unit

Build Dist

yarn build

Run Demo

yarn serve

Build Demo

yarn build:demo

How to Contribute

Pull Requests

  1. Fork the repository
  2. Create a new branch for each feature or improvement
  3. Send a pull request from each feature branch to the develop branch

License

MIT