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

grid-and-widgets

v0.0.14

Published

I created this package to introduce a new way to build web pages in Vue 3. I needed a way to create custom webpages quickly with drag and drop capability for our project lectful.com Since our platform is a SAAS solution where all of our customers share t

Downloads

33

Readme

Grid And Widgets

I created this package to introduce a new way to build web pages in Vue 3. I needed a way to create custom webpages quickly with drag and drop capability for our project lectful.com Since our platform is a SAAS solution where all of our customers share the same codebase but different databases, we needed a way to store the layout of pages of each customer in their database.

So, why didn't I just use some HTML WYSIWYG editor to build webpages? Well, manipulating HTML directly is difficult, not predictable, and would require us to always add HTML templates with bunch of slots...

The solution I came up with is to store the webpage layout as JSON instead of HTML. And then we build a bunch of independant widgets that can be dragged and dropped into the layout. I was able to do this thanks to the flexible Grid system that CSS offers.

How to install

npm install grid-and-widgets

or in the case of gitlab

npm install https://www.gitlab.com/vipayers/grid-and-widgets

How to use

This code lets you use the package globally in your app.js file

import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)

import 'grid-and-widgets/dist/style.css'


import GridAndWidgets from 'grid-and-widgets'; //Import as a plugin
app.use(GridAndWidgets) // And then use the plugin

app.mount('#app')

Or you can use it locally in your any component file

<template>
    <div>
        <Builder :widgets="widgets" v-model:content="content" @saveButtonClicked="save()" />
    </div>
</template>
<script>
import 'grid-and-widgets/dist/style.css'

import {Builder, Viewer} from 'grid-and-widgets'

import TextComponent from 'SOMEPATH/Widgets/Text/Component.vue';
import TextConfig from 'SOMEPATH/Widgets/Text/Config'
import TextIcon from 'SOMEPATH/Widgets/Text/Icon'

import ImageComponent from 'SOMEPATH/Widgets/Image/Component.vue';
import ImageConfig from 'SOMEPATH/Widgets/Image/Config'
import ImageIcon from 'SOMEPATH/Widgets/Image/Icon'

const widgets  = {
    'text' : {
        name : 'Text',
        component : TextComponent,
        config : TextConfig,
        icon : TextIcon
    },
    'image' :{
        name : 'Image',
        component : ImageComponent,
        config : ImageConfig,
        icon : ImageIcon
    }
}


export default {
    components:{
        Builder,
        Viewer
    },
    methods:{
        save(){
            //Save page layout, maybe by sending an Axios request to the backend
        }
    },
    data(){
        return {
            //This structure must be respected
            content : { layout: { lg: [], md: [], sm: [], xs: [], xxs: [] }, is_responsive: true }
        }
    }
}
</script>

Widgets

Widgets are the main building blocks that compose a webpage. We have seen in the previous example how to add them to the builder. Widgets have three composed of three things:

  1. Main Widget Component The main widget component is the actual UI of the widget that will be visible to the end user.
  2. A config component The config component of the widget is a component that allows you to customize your widget.
  3. An Icon component Each widget has an Icon, so you simply define the Icon of the widget, for example an SVG or PNG...

let's see an example widget that lets user display an image

SOMEPATH/Widgets/Image/Component.vue

<template>
    <div class="w-full h-full object-cover">
        <img class="w-full h-full object-cover" :src="url" />
    </div>
</template>

<script>
export default {
    props:{
        url : String
    }
}
</script>

SOMEPATH/Widgets/Image/Config

<template>
    <div>
        <h1>Image url</h1>
        <input :value="modelValue.url" @input="$emit('update:modelValue', {url : $event.target.value})" class="bg-transparent border p-1 rounded mt-1" />    
    </div>
</template>

<script>
export default {
    props:{
        modelValue:{
            type : Object,
            default : {
                url : ''
            }
        }
    }
}
</script>

SOMEPATH/Widgets/Text/Icon

<template>
    <div class="w-full h-full text-gray-300">
        <svg viewBox="0 0 24 24">
            <path fill="currentColor" d="M21,3H3C2,3 1,4 1,5V19A2,2 0 0,0 3,21H21C22,21 23,20 23,19V5C23,4 22,3 21,3M5,17L8.5,12.5L11,15.5L14.5,11L19,17H5Z"></path>
        </svg>
    </div>
</template>

Resize container height to fit widget height.

Sometimes, some widgets might have a non fixed height (for example elements that show a hidden content when clicked...), however The grid has a specific height defined for each container, so whenever a widget changes size it needs to totify the grid to update its height so that it accounts for the new size of our widget. To do that, inside our widget we can dispatch an event named fitHeight with the widget element so that the grid recalculate the container height. Here is an example widget.

<template>
    <div ref="myComponent">
        <button @click="toggle">Show content</button>
        <div v-if="isShown">
            Some hidden content that only appears when the isShown variable is true. When it appears it changes the 
            height of the component.
        </div>
    </div>
</template>
<script>
export default {
    methods:{
        toggle(){
            this.isShown = !this.isShown;

            //We use $nextTick so that we send the height after the component is rendered with the new height.
            this.$nextTick(() => {
                this.$emit('fitHeight', { element : this.$refs.myComponent })
            })

            
            //Or, We can specify the new container height explicitly
            let height = this.$refs.myComponent.clientHeight; //Or any value
            this.$nextTick(() => {
                this.$emit('fitHeight', { height })
            })
        }
    }
}
</script>