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

@marsio/vue-grid-layout

v1.0.2

Published

A draggable and resizable grid layout with responsive breakpoints, for vue3.

Downloads

83

Readme

Vue Grid Layout

Vue-Grid-Layout is responsive and supports breakpoints. Breakpoint layouts can be provided by the user or autogenerated, it supports Vue3, with compatibility for both web and mobile platforms.

VGL is Vue3-only and does not require jQuery. lQHPJwoeFsrrm2PNAaXNAyCwnrwXf3Au-Y4GmhEurCB8AA_800_421

[Demo | Changelog | View Response Layout]

Table of Contents

Demos

  1. Basic
  2. Response
  3. No Dragging
  4. Messy
  5. Static Elements
  6. Minimum and Maximum Width/Height
  7. Dynamic Minimum and Maximum Width/Height
  8. No Vertical Compacting (Free Movement)
  9. Prevent Collision
  10. Toolbox
  11. Drag From Outside
  12. Bounded Layout
  13. Responsive Bootstrap-style Layout
  14. Allow Overlap

Features

  • 100% Vue - no jQuery
  • Draggable widgets
  • Resizable widgets
  • Static widgets
  • Configurable packing: horizontal, vertical, or off
  • Bounds checking for dragging and resizing
  • Widgets may be added or removed without rebuilding grid
  • Layout can be serialized and restored
  • Responsive breakpoints
  • Separate layouts per responsive breakpoint
  • Grid Items placed using CSS Transforms
    • Performance with CSS Transforms

Installation

Install the Vue-Grid-Layout package using npm:

npm install @marsio/vue-grid-layout

Usage

The following example below will produce a grid with three items where:

  • users will not be able to drag or resize item a
  • item b will be restricted to a minimum width of 2 grid blocks and a maximum width of 4 grid blocks
  • users will be able to freely drag and resize item c
<template>
  <VGL
    class="layout"
    v-model="state.layout"
    :cols="12"
    :rowHeight="30"
    :width="1200"
  >
    <div key="a">a</div>
    <div key="b">b</div>
    <div key="c">c</div>
  </VGL>
</template>

<script>
import VGL from "@marsio/vue-grid-layout";
import { defineComponent, ref } from 'vue';

export default defineComponent({
  components: {
    VGL
  },
  setup() {
    const layout = [
      { i: "a", x: 0, y: 0, w: 1, h: 2, static: true },
      { i: "b", x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
      { i: "c", x: 4, y: 0, w: 1, h: 2 }
    ];
    const state = reactive({
      layout
    })
    return {
      state
    };
  }
});
</script>

You may also choose to set layout properties directly on the children:

<template>
  <GridLayout class="layout" :cols="12" :rowHeight="30" :width="1200">
    <div key="a" :data-grid="{ x: 0, y: 0, w: 1, h: 2, static: true }">
      a
    </div>
    <div key="b" :data-grid="{ x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 }"">
      b
    </div>
    <div key="c" :data-grid="{ x: 4, y: 0, w: 1, h: 2 }">
      c
    </div>
  </GridLayout>
</template>

<script>
import VGL from "@marsio/vue-grid-layout";
import { defineComponent, ref } from 'vue';

export default defineComponent({
  components: {
    VGL
  }
});
</script>

Responsive Usage

To make VGL responsive, use the <ResponsiveVueGridLayout> element:

<template>
  <ResponsiveGridLayout
    class="layout"
    :layouts="state.layouts"
    :breakpoints="{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }"
    :cols="{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }"
  >
    <div v-for="(l, i) in state.layouts.lg" :key="i+1" :class="{ static: l.static }">
      <span v-if="l.static" class="text" title="This item is static and cannot be removed or resized.">
        Static - {{ i }}
      </span>
      <span v-else class="text">{{ i }}</span>
    </div>
  </ResponsiveGridLayout>
</template>

<script>
import { Responsive as ResponsiveGridLayout } from "@marsio/vue-grid-layout";
import { defineComponent, ref } from 'vue';

export default defineComponent({
  components: {
    VGL
  },
  setup() {
    const generateLayout = () => {
      return Array.from({ length: 10 }, (item, i) => {
        const y = Math.ceil(Math.random() * 4) + 1;
        return {
          x: Math.round(Math.random() * 5) * 2,
          y: Math.floor(i / 6) * y,
          w: 2,
          h: y,
          i: (i + 1).toString(),
          static: Math.random() < 0.05
        };
      });
    }
    const state = {
      layouts: { lg: generateLayout() }
    }
    return {
      state
    }
  }
});
</script>

When in responsive mode, you should supply at least one breakpoint via the layouts property.

When using layouts, it is best to supply as many breakpoints as possible, especially the largest one. If the largest is provided, VGL will attempt to interpolate the rest.

You will also need to provide a width, when using <ResponsiveVueGridLayout> it is suggested you use the HOC WidthProvider as per the instructions below.

It is possible to supply default mappings via the data-grid property on individual items, so that they would be taken into account within layout interpolation.

Providing Grid Width

Both <ResponsiveVueGridLayout> and <Vu eGridLayout> take width to calculate positions on drag events. In simple cases a HOC WidthProvider can be used to automatically determine width upon initialization and window resize events.

<template>
  <ResponsiveGridLayout
    class="layout"
    :layouts="state.layouts"
    :breakpoints="{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }"
    :cols="{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }"
  >
    <div v-for="(l, i) in state.layouts.lg" :key="i+1" :class="{ static: l.static }">
      <span v-if="l.static" class="text" title="This item is static and cannot be removed or resized.">
        Static - {{ i }}
      </span>
      <span v-else class="text">{{ i }}</span>
    </div>
  </ResponsiveGridLayout>
</template>

<script>
import { Responsive, WidthProvider } from "@marsio/vue-grid-layout";
import { defineComponent, ref } from 'vue';

const ResponsiveGridLayout = WidthProvider(Responsive);

export default defineComponent({
  components: {
    VGL
  },
  setup() {
    const generateLayout = () => {
      return Array.from({ length: 10 }, (item, i) => {
        const y = Math.ceil(Math.random() * 4) + 1;
        return {
          x: Math.round(Math.random() * 5) * 2,
          y: Math.floor(i / 6) * y,
          w: 2,
          h: y,
          i: (i + 1).toString(),
          static: Math.random() < 0.05
        };
      });
    }
    const state = {
      layouts: { lg: generateLayout() }
    }
    return {
      state
    }
  }
});
</script>

This allows you to easily replace WidthProvider with your own Provider HOC if you need more sophisticated logic.

WidthProvider accepts a single prop, measureBeforeMount. If true, WidthProvider will measure the container's width before mounting children. Use this if you'd like to completely eliminate any resizing animation on application/component mount.

Grid Layout Props

VGL supports the following properties (see the source for the final word on this):

//
// Basic props
//

// This is required unless using the HOC <WidthProvider> or similar
width: number,

// If true, the container height swells and contracts to fit contents
autoSize?: boolean = true,

// Number of columns in this layout.
cols?: number = 12,

// A CSS selector for tags that will not be draggable.
// For example: draggableCancel:'.MyNonDraggableAreaClassName'
// If you forget the leading . it will not work.
// .vue-resizable-handle" is always prepended to this value.
draggableCancel?: string = '',

// A CSS selector for tags that will act as the draggable handle.
// For example: draggableHandle:'.MyDragHandleClassName'
// If you forget the leading . it will not work.
draggableHandle?: string = '',

// Compaction type.
compactType?: ('vertical' | 'horizontal' | null) = 'vertical';

// Layout is an array of objects with the format:
// The index into the layout must match the key used on each item component.
// If you choose to use custom keys, you can specify that key in the layout
// array objects using the `i` prop.
layout?: Array<{i?: string, x: number, y: number, w: number, h: number}> = null, // If not provided, use data-grid props on children

// Margin between items [x, y] in px.
margin?: [number, number] = [10, 10],

// Padding inside the container [x, y] in px
containerPadding?: [number, number] = margin,

// Rows have a static height, but you can change this based on breakpoints
// if you like.
rowHeight?: number = 150,

// Configuration of a dropping element. Dropping element is a "virtual" element
// which appears when you drag over some element from outside.
// It can be changed by passing specific parameters:
//  i - id of an element
//  w - width of an element
//  h - height of an element
droppingItem?: { i: string, w: number, h: number }

//
// Flags
//
isDraggable?: boolean = true,
isResizable?: boolean = true,
isBounded?: boolean = false,
// Uses CSS3 translate() instead of position top/left.
// This makes about 6x faster paint performance
useCSSTransforms?: boolean = true,
// If parent DOM node of ResponsiveVueGridLayout or VueGridLayout has "transform: scale(n)" css property,
// we should set scale coefficient to avoid render artefacts while dragging.
transformScale?: number = 1,

// If true, grid can be placed one over the other.
// If set, implies `preventCollision`.
allowOverlap?: boolean = false,

// If true, grid items won't change position when being
// dragged over. If `allowOverlap` is still false,
// this simply won't allow one to drop on an existing object.
preventCollision?: boolean = false,

// If true, droppable elements (with `draggable={true}` attribute)
// can be dropped on the grid. It triggers "onDrop" callback
// with position and event object as parameters.
// It can be useful for dropping an element in a specific position

isDroppable?: boolean = false,
// Defines which resize handles should be rendered.
// Allows for any combination of:
// 's' - South handle (bottom-center)
// 'w' - West handle (left-center)
// 'e' - East handle (right-center)
// 'n' - North handle (top-center)
// 'sw' - Southwest handle (bottom-left)
// 'nw' - Northwest handle (top-left)
// 'se' - Southeast handle (bottom-right)
// 'ne' - Northeast handle (top-right)
//
// Note that changing this property dynamically does not work due to a restriction in @marsio/vue-resizable.
resizeHandles?: Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se'],

// class to the `draggableCancel` prop.
resizeHandle?: VNode
  | ((
      resizeHandleAxis: ResizeHandleAxis,
      ref: VueRef<HTMLElement>
    ) => VNode)

//
// Callbacks
//

// Callback so you can save the layout.
// Calls back with (currentLayout) after every drag or resize stop.
onLayoutChange: (layout: Layout) => void,

//
// All callbacks below have signature (layout, oldItem, newItem, placeholder, e, element).
// 'start' and 'stop' callbacks pass `undefined` for 'placeholder'.
//
type ItemCallback = (layout: Layout, oldItem: LayoutItem, newItem: LayoutItem,
                     placeholder: LayoutItem, e: MouseEvent, element: HTMLElement) => void,

// Calls when drag starts.
onDragStart: ItemCallback,
// Calls on each drag movement.
onDrag: ItemCallback,
// Calls when drag is complete.
onDragStop: ItemCallback,
// Calls when resize starts.
onResizeStart: ItemCallback,
// Calls when resize movement happens.
onResize: ItemCallback,
// Calls when resize is complete.
onResizeStop: ItemCallback,

// Calls when an element has been dropped into the grid from outside.
onDrop: (layout: Layout, e: Event, item?: LayoutItem) => void,
// Calls when an element is being dragged over the grid from outside as above.
// This callback should return an object to dynamically change the droppingItem size
// Return false to short-circuit the dragover
onDropDragOver: (e: DragEvent) => { w?: number; h?: number } | false;

// Ref for getting a reference for the grid's wrapping div.
innerRef?: Ref<"div">

Responsive Grid Layout Props

The responsive grid layout can be used instead. It supports all of the props above, excepting layout. The new properties and changes are:

// {name: pxVal}, e.g. {lg: 1200, md: 996, sm: 768, xs: 480}
// Breakpoint names are arbitrary but must match in the cols and layouts objects.
breakpoints?: Object = {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0},

// # of cols. This is a breakpoint -> cols map, e.g. {lg: 12, md: 10, ...}
cols?: Object = {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},


// margin (in pixels). Can be specified either as horizontal and vertical margin, e.g. `[10, 10]` or as a breakpoint -> margin map, e.g. `{lg: [10, 10], md: [10, 10], ...}.
margin: [number, number],


// containerPadding (in pixels). Can be specified either as horizontal and vertical padding, e.g. `[10, 10]` or as a breakpoint -> containerPadding map, e.g. `{lg: [10, 10], md: [10, 10], ...}.
containerPadding: [number, number],


// layouts is an object mapping breakpoints to layouts.
// e.g. {lg: Layout, md: Layout, ...}
layouts,

//
// Emit
//

// Calls back with breakpoint and new # cols
onBreakpointChange: (newBreakpoint: string, newCols: number) => void,

// Callback so you can save the layout.
// AllLayouts are keyed by breakpoint.
onLayoutChange: (currentLayout: Layout, allLayouts) => void,

// Callback when the width changes, so you can modify the layout as needed.
onWidthChange: (containerWidth: number, margin: [number, number], cols: number, containerPadding: [number, number]) => void;

Grid Item Props

VGL supports the following properties on grid items or layout items. When initializing a grid, build a layout array (as in the first example above), or attach this object as the data-grid property to each of your child elements (as in the second example).

If data-grid is provided on an item, it will take precedence over an item in the layout with the same key (i).

Note that if a grid item is provided but incomplete (missing one of x, y, w, or h), an error will be thrown so you can correct your layout.

If no properties are provided for a grid item, one will be generated with a width and height of 1.

You can set minimums and maximums for each dimension. This is for resizing; it of course has no effect if resizing is disabled. Errors will be thrown if your mins and maxes overlap incorrectly, or your initial dimensions are out of range.

Any <GridItem> properties defined directly will take precedence over globally-set options. For example, if the layout has the property isDraggable: false, but the grid item has the prop isDraggable: true, the item will be draggable, even if the item is marked static: true.

{

  // A string corresponding to the component key
  i: string,

  // These are all in grid units, not pixels
  x: number,
  y: number,
  w: number,
  h: number,
  minW?: number = 0,
  maxW?: number = Infinity,
  minH?: number = 0,
  maxH?: number = Infinity,

  // If true, equal to `isDraggable: false, isResizable: false`.
  static?: boolean = false,
  // If false, will not be draggable. Overrides `static`.
  isDraggable?: boolean = true,
  // If false, will not be resizable. Overrides `static`.
  isResizable?: boolean = true,
  // By default, a handle is only shown on the bottom-right (southeast) corner.
  resizeHandles?: Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se']
  // If true and draggable, item will be moved only within grid.
  isBounded?: boolean = false
}

Grid Item Heights and Widths

Grid item widths are based on container and number of columns. The size of a grid unit's height is based on rowHeight.

Note that an item that has h=2 is not exactly twice as tall as one with h=1 unless you have no margin!

In order for the grid to not be ragged, when an item spans grid units, it must also span margins. So you must add the height or width or the margin you are spanning for each unit. So actual pixel height is (rowHeight * h) + (marginH * (h - 1).

For example, with rowHeight=30, margin=[10,10] and a unit with height 4, the calculation is (30 * 4) + (10 * 3) image

If this is a problem for you, set margin=[0,0] and handle visual spacing between your elements inside the elements' content.