/ Gists / Plugin patterns

Gists - Plugin patterns

On gists

Provider->Consumer

Patterns.dev Plugin patterns Vue.js

Provider.vue #

<!-- https://learn-vuejs.github.io/vue-patterns/patterns/#dependency-injection -->

<template>
  <div>
    <slot v-bind="{ state, actions }" />
  </div>
</template>

<script>
export default {
  computed: {
    state() {
      return {
        label: 'button',
      };
    },
    actions() {
      return {
        click: this.click,
      };
    },
  },
  methods: {
    click() {
      console.log('Clicked');
    },
  },
}
</script>

On gists

Vue patterns comparison

Plugin patterns Vue.js

claudeai.txt #

:D Jo, přesně tak! To jste trefil naprosto přesně.

V praxi to často vypadá takhle:

"Super-Mega-Awesome-Provider-Container-Renderless-Composition-Pattern™️"
```vue
<template>
  <slot v-bind="data">
    <default-view v-bind="data" />
  </slot>
</template>
```

A ve finále je to prostě:
- Komponenta
- Se slotem
- A výchozím vzhledem
- Která předává data dál

Často se z jednoduchých věcí dělá zbytečná věda jen proto, aby se dalo napsat článek nebo přednášet na konferenci. Místo:
"Hey, dej si tam slot a budeš to moct přepsat" 
máme:
"Implementace Advanced Renderless Component Pattern s Dependency Injection a Composable Factory Integration" 😅

Proto je super váš přístup - dívat se na věci prakticky a nenechat se unést přehnaně komplikovanými názvy pro jednoduché koncepty.

On gists

Renderless with default layouts

Patterns.dev Plugin patterns Vue.js

DataProvider.vue #

<!-- DataProvider.vue -->
<template>
  <slot 
    :data="data"
    :loading="loading"
    :error="error"
  >
    <component 
      :is="layouts[variant]" 
      :data="data"
      :loading="loading"
      :error="error"
    />
  </slot>
</template>

<script setup>
import DefaultLayout from './layouts/DefaultLayout.vue'
import CompactLayout from './layouts/CompactLayout.vue'
import CardLayout from './layouts/CardLayout.vue'

const props = defineProps({
  variant: {
    type: String,
    default: 'default',
    validator: (value) => ['default', 'compact', 'card'].includes(value)
  }
})

const layouts = {
  default: DefaultLayout,
  compact: CompactLayout,
  card: CardLayout
}
</script>

On gists

Container Pattern

Patterns.dev Plugin patterns Vue.js

DogImagesContainer.vue #

<!-- DogImagesContainer.vue -->

<template>
  <DogImages :dogs="dogs" />
</template>

<script setup>
  import { ref, onMounted } from "vue";
  import DogImages from "./DogImages.vue";

  const dogs = ref([]);

  onMounted(async () => {
    const response = await fetch(
      "https://dog.ceo/api/breed/labrador/images/random/6"
    );
    const { message } = await response.json();
    dogs.value = message;
  });
</script>

On gists

Data Provider

Patterns.dev Plugin patterns Vue.js

dataprovider01.vue #

<template>
  <slot :checkbox="checkbox" :toggleCheckbox="toggleCheckbox"></slot>
</template>

<script setup>
  import { ref, reactive } from "vue";

  const API_ENDPOINT_URL = "https://official-joke-api.appspot.com/random_joke";

  const data = reactive({
    setup: null,
    punchline: null,
  });
  const loading = ref(false);

  const fetchJoke = async () => {
    loading.value = true;

    const response = await fetch(API_ENDPOINT_URL);
    const responseData = await response.json();

    data.setup = responseData.setup;
    data.punchline = responseData.punchline;
    loading.value = false;
  };

  fetchJoke();
</script>



// usage
<template>
  <DataProvider v-slot="{ data, loading }">
    <div class="joke-section">
      <p v-if="loading">Joke is loading...</p>
      <p v-if="!loading">{{ data.setup }}</p>
      <p v-if="!loading">{{ data.punchline }}</p>
    </div>
  </DataProvider>

  <DataProvider v-slot="{ data, loading }">
    <p v-if="loading">Hold on one sec...</p>
    <div v-else class="joke-section">
      <details>
        <summary>{{ data.setup }}</summary>
        <p>{{ data.punchline }}</p>
      </details>
    </div>
  </DataProvider>
</template>

<script setup>
  import DataProvider from "./components/DataProvider.vue";
</script>

On gists

Renderless

Patterns.dev Plugin patterns Vue.js

MouseTracker01.vue #

<!-- MouseTracker.vue -->
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
  
const x = ref(0)
const y = ref(0)
 
const update = e => {
  x.value = e.pageX
  y.value = e.pageY
}
 
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
</script>
 
<template>
  <slot :x="x" :y="y"/>
</template>


On gists

JS plugin aka jQuery

JavaScript Plugin patterns Helpers-Filters-Plugins

Test.plugin.js #

class Test {
    constructor(el, options) {
        this.el = el;
        const defaultOptions = {
            onHover: (element, e) => {
                console.log(':)))', element, e);
            },
        };

        this.options = { ...defaultOptions, ...options };
        this.mouseOverHandler = this.handleMouseOver.bind(this); // Vytvoření odkazu na metodu handleMouseOver
    }

    init() {
        this.el.addEventListener('mouseover', this.mouseOverHandler); // Přidání posluchače události
    }

    destroy() {
        this.el.removeEventListener('mouseover', this.mouseOverHandler); // Odstranění posluchače události
    }

    handleMouseOver(e) {
        if (this.options.onHover) {
            this.options.onHover(this.el, e);
        }
    }
}

On gists

Basic jQuery Plugin using the Module Pattern

Plugin patterns jQuery-plugins

plugin.js #

/**
 *  A basic jQuery plugin using the Module Pattern
 * *
 *  @author      Sam Deering
 *  @copyright   Copyright (c) 2012 jQuery4u
 *  @license     http://jquery4u.com/license/
 *  @since       Version 1.0
 *
 */

!function(exports, $, undefined)
{
    var Plugin = function()
    {

        /*-------- PLUGIN VARS ------------------------------------------------------------*/

        var priv = {}, // private API

            Plugin = {}, // public API

            // Plugin defaults
            defaults = {
                id : '',
                name : '',
                url : ''
            };

        /*-------- PRIVATE METHODS --------------------------------------------------------*/

        priv.options = {}; //private options

        priv.method1 = function()
        {
            console.log('Private method 1 called...');
            $('#videos').append(''+this.options.name+'');
            priv.method2(this.options);
        };

        priv.method2 = function()
        {
            console.log('Private method 2 called...');
            $('#'+priv.options.id).append(''+this.options.url+''); // append title
            $('#'+priv.options.id).append(''); //append video
        };

        /*-------- PUBLIC METHODS ----------------------------------------------------------*/

        Plugin.method1 = function()
        {
            console.log('Public method 1 called...');
            console.log(Plugin);

            //options called in public methods must access through the priv obj
            console.dir(priv.options);
        };

        Plugin.method2 = function()
        {
            console.log('Public method 2 called...');
            console.log(Plugin);
        };

        // Public initialization
        Plugin.init = function(options)
        {
            console.log('new plugin initialization...');
            $.extend(priv.options, defaults, options);
            priv.method1();
            return Plugin;
        }

        // Return the Public API (Plugin) we want
        // to expose
        console.log('new plugin object created...');
        return Plugin;
    }

    exports.Plugin = Plugin;

}(this, jQuery);


jQuery(document).ready( function()
{
    console.log('doc rdy');

    // EXAMPLE OF CREATING PLUGIN OBJECTS WITH CUSTOM SETTINGS

    console.log('--------------------------------------------');

    var myPlugin1 = new Plugin;
    myPlugin1.init(
    {
        /* custom options */
        id : 'vid01',
        name : 'Video 1',
        url : 'http://www.youtube.com/embed/dXo0LextZTU?rel=0'
    });

    //call to public methods
    myPlugin1.method1();
    myPlugin1.method2();

    console.log('--------------------------------------------');

    var myPlugin2 = new Plugin;
    myPlugin2.init(
    {
        /* custom options */
        id : 'vid02',
        name : 'Video 2',
        url : 'http://www.youtube.com/embed/nPMAUW-4lNY?rel=0'
    });

    //call to public methods
    myPlugin2.method1();
    myPlugin2.method2();

    // console.log('--------------------------------------------');

});

On gists

jQuery plugin skeleton

Plugin patterns jQuery-plugins

skeleton.js #

/*!
 * AW {{PROJECTNAME}}
 *
 *
 * @author  {{firstname lastname}} <{{email}}>
 * @version  1.0.0
 *
 */

 /*
    {{PLUGIN}} => realname of project, eg: awBestPlugin
 */

;(function() {
    'use strict';

    function {{PLUGIN}}(element, options) {
        this.self  = $(element);
        this.settings   = $.extend(true, {}, $.fn.{{PLUGIN}}.defaults, options);

    }


    {{PLUGIN}}.prototype = {
        
        /* PRIVATE METHODS */
        _create: function() { 
           
        }

  
        /* PUBLIC METHODS */
      
    };


    (function ($) {
        $.fn.{{PLUGIN}} = function(options) {
            var
            args   = Array.prototype.slice.call(arguments, 1),
            method = {{PLUGIN}}.prototype[options];

            
            return this.each(function() {
                var self     = $(this);
                var instance = self.data('{{PLUGIN}}');

                if (!instance) {
                    instance = new {{PLUGIN}}(this, options);
                    instance._create();
                    self.data('{{PLUGIN}}', instance);
                }

                if (method && options.substring(0, 1) == '_') 
                    $.error('Cannot use private method "' + options + '" outside of object');
                
                if (method) {
                    method.apply(instance, args);
                } else if (options && typeof options !== 'object') {
                    $.error('Method "' + options + '" not found.');
                }
            });
        };

        $.fn.{{PLUGIN}}.defaults = {
 
        };
    })(jQuery);
})();