npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@crishellco/vue-spruce

v3.6.0

Published

A collection of useful Vue 2 renderless components

Downloads

40

Readme

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