Skip to content

Latest commit

 

History

History
339 lines (304 loc) · 7.99 KB

README.md

File metadata and controls

339 lines (304 loc) · 7.99 KB

Proxi

<proxi> is a tiny proxy component. Whatever you add to it gets proxied to a target component!

🙋 Why?

  • ♻️ Uses Vue's Template API Doesn't re-invent component communication!
  • 💪 Provide/Inject on Steroids! Familiar concepts, but super powered!
  • 💥 Reactive All injected data is reactive (unlike provide/inject)!
  • 🐥 Tiny 755 B Gzipped!

🚀 Install

npm i vue-proxi

🚦 3-step Setup

1. 👩 Parent component

  • Import and register import Proxi from 'vue-proxi'
  • Insert anywhere in your template:
    • <proxi :proxi-key="key" [... attr / :prop / @listener]>
    • key is used to communicate with the Child. Use a unique string value or a Symbol

2. 👶 Child component

  • Import the Proxi Inject mixin: import { ProxiInject } from 'vue-proxi'
  • Register the mixin:
export default {
    // ...,

    mixins: [
        ProxiInject({
            from: key, // from Step 1
            props: [
                // Becomes available on VM eg. `this.propName`
                'propName'
                // ...
            ]
        })
    ]
}

3. ✅ Done!

  • The injected data is all available in this.$$
    • this.$$.class: Class
    • this.$$.props: Props (Automatically bound to VM)
    • this.$$.attrs: Attributes
      • eg. v-bind="$$.attrs" or v-bind="{ ...$attrs, ...$$.attrs }"
    • this.$$.listeners: Event listeners (Automatically bound to VM)
      • eg. v-on="$$.listeners" or v-on="{ ...$listeners, ...$$.listeners }"

👨‍🏫 Demos

Some quick demos to get you started!

Inheriting props?
When you declare a prop, it filters it out from the attributes list ($$.attrs) to be available directly on the view model (this.propName) and the props list ($$.props).
👩 Parent👶 Child
<proxi
    :proxi-key="key"
    :child-disabled="isDisabled"
    :child-label="label"
/>
<label>
    {{ label }}
    <input
        type="checkbox"
        :disabled="childDisabled"
    >
</label>

export default {
  mixins: [
    ProxiInject({
      from: key,
      props: [
        'childDisabled',
        'childLabel'
      ]
    })
  ],
  computed: {
    label() {
      return this.childLabel + ':';
    }
  }
};
Inheriting the class?
Both the static class and computed class are consolidated into $$.class for you to easily attach to any element.
👩 Parent👶 Child
<proxi
    :proxi-key="key"
    class="static-class"
    :class="['child-class', {
        disabled: isDisabled
    }]"
/>
<div :class="$$.class">
    Child
</div>

export default {
    mixins: [
        ProxiInject({ from: key })
    ],
};
Inheriting attrs?
  • Looking to inherit a specific attribute? Just pick it out from $$.attrs
  • Looking to inherit all attributes? Throw $$.attrs into v-bind
👩 Parent👶 Child
<proxi
    :proxi-key="key"
    :disabled="true"
/>
<div
    :disabled="$$.attrs.disabled"
    v-bind="$$.attrs"
>
    Child
</div>

export default {
    mixins: [
        ProxiInject({ from: key })
    ],
};
Inheriting listeners?
All event listeners are in $$.listeners to throw right into v-on!
👩 Parent👶 Child
<proxi
    :proxi-key="key"
    @click="handleClick"
    @custom-event="handleCustomEvent"
/>
<button v-on="$$.listeners">
    Child
</button>

export default {
    mixins: [
        ProxiInject({ from: key })
    ],
    mounted() {
        // Listeners are automatically bound to VM
        this.$emit('custom-event', 'Mounted!');
    }
};

Advanced

This demo shows how a parent-child pair, RadioGroup and Radio, communicate using Proxi. Note how the two components only come together at usage.

JSFiddle Demo

Usage
<template>
    <div>
        <radio-group v-model="selected">
            <radio
                label="Apples"
                value="apples"
            />
            <radio
                label="Oranges"
                value="oranges"
            />
            <radio
                label="Bananas"
                value="bananas"
            />
        </radio-group>
        <div>
            Selected: {{ selected }}
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            selected: []
        }
    }
}
</script>
Parent: RadioGroup.vue
<template>
    <div>
        <proxi
            :proxi-key="key"
            :checked-items="value"
            @update="$emit('input', $event)"
        >
            <slot />
        </proxi>
    </div>
</template>

<script>
import Proxi from 'vue-proxi'

export default {
    components: {
        Proxi
    },

    props: ['value'],

    data() {
        return {
            // Same idea as provide/inject
            // Use a Symbol for security
            key: 'radios'
        }
    }
}
</script>
Child: Radio.vue
<template>
    <label>
        <input
            type="checkbox"
            :checked="isChecked"
            @click="onClick"
        >
        {{ label }}
    </label>
</template>

<script>
import { ProxiInject } from 'vue-proxi'

export default {
    mixins: [
        ProxiInject({
            // Same key as parent
            from: 'radios',

            // Declare props that can be injected in
            // Only array supported for now
            props: ['checkedItems']
        })
    ],

    props: {
        label: String,
        value: null
    },

    computed: {
        isChecked() {
            return this.checkedItems.includes(this.value)
        }
    },

    methods: {
        onClick() {
            if (this.isChecked) {
                this.$emit('update', this.checkedItems.filter(i => i !== this.value))
            } else {
                this.$emit('update', [...this.checkedItems, this.value])
            }
        }
    }
}
</script>

👪 Related

  • vue-subslot - 💍 Pick 'n choose what you want from a slot passed into your Vue component
  • vue-pseudo-window - 🖼 Declaratively interface window/document in your Vue template
  • vue-vnode-syringe - 🧬Mutate your vNodes with vNode Syringe 💉