CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-opentiny--vue-common

Cross-framework compatibility utilities and adapters for building Vue components that work seamlessly across Vue 2, Vue 2.7, and Vue 3

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

composition-hooks.mddocs/

Core Composition Hooks

The Core Composition Hooks provide essential Vue composition utilities for component relationships, slot management, and performance optimization. These hooks are built on top of the adapter system and work seamlessly across Vue 2 and Vue 3.

Capabilities

Instance Slots Hook

Hook for managing component slots with cross-framework compatibility and reactive updates.

/**
 * Create instance slots manager with cross-framework compatibility
 * @param hooks - Adapter hooks system
 * @returns Instance slots manager
 */
function useInstanceSlots(hooks: AdapterHooks): any;

Usage Examples:

import { useInstanceSlots, hooks } from "@opentiny/vue-common";

export default {
  setup(props, context) {
    const instanceSlots = useInstanceSlots(hooks);
    
    // Access slots reactively
    const hasDefaultSlot = hooks.computed(() => {
      return !!instanceSlots.default;
    });
    
    const hasHeaderSlot = hooks.computed(() => {
      return !!instanceSlots.header;
    });
    
    return {
      instanceSlots,
      hasDefaultSlot,
      hasHeaderSlot
    };
  },
  template: `
    <div class="component">
      <header v-if="hasHeaderSlot">
        <slot name="header" />
      </header>
      <main v-if="hasDefaultSlot">
        <slot />
      </main>
    </div>
  `
};

Component Relation Hook

Hook for managing parent-child component relationships and communication patterns.

/**
 * Create component relation manager for parent-child communication
 * @param hooks - Adapter hooks system
 * @returns Component relation manager
 */
function useRelation(hooks: AdapterHooks): any;

Usage Examples:

import { useRelation, hooks } from "@opentiny/vue-common";

// Parent component
export default {
  name: 'ParentComponent',
  setup() {
    const relation = useRelation(hooks);
    
    const children = hooks.ref([]);
    
    const registerChild = (child) => {
      children.value.push(child);
    };
    
    const unregisterChild = (child) => {
      const index = children.value.indexOf(child);
      if (index > -1) {
        children.value.splice(index, 1);
      }
    };
    
    // Provide registration methods to children
    hooks.provide('parentRelation', {
      registerChild,
      unregisterChild
    });
    
    return {
      children,
      relation
    };
  }
};

// Child component
export default {
  name: 'ChildComponent',
  setup() {
    const relation = useRelation(hooks);
    const parentRelation = hooks.inject('parentRelation', null);
    
    const instance = hooks.getCurrentInstance();
    
    hooks.onMounted(() => {
      if (parentRelation) {
        parentRelation.registerChild(instance);
      }
    });
    
    hooks.onBeforeUnmount(() => {
      if (parentRelation) {
        parentRelation.unregisterChild(instance);
      }
    });
    
    return {
      relation
    };
  }
};

Deferred Rendering Hook

Performance optimization hook for progressive component rendering with frame-based control.

/**
 * Create deferred rendering controller for performance optimization
 * @param maxCount - Maximum frame count before stopping (default: 100)
 * @returns Deferred rendering controller
 */
function useDefer(maxCount?: number): {
  /** Check if rendering should be deferred for given frame number */
  defer(n: number): boolean;
  /** Reset frame counter to start over */
  reset(): void;
  /** Cancel animation frames and stop counting */
  cancel(): void;
};

Usage Examples:

import { useDefer, hooks } from "@opentiny/vue-common";

export default {
  setup() {
    const deferrer = useDefer(50); // Limit to 50 frames
    
    // Progressive rendering of list items
    const items = hooks.ref(Array.from({ length: 1000 }, (_, i) => `Item ${i}`));
    
    const visibleItems = hooks.computed(() => {
      return items.value.filter((_, index) => {
        // Show first 10 items immediately
        if (index < 10) return true;
        
        // Progressive reveal based on frame count
        const frameThreshold = Math.floor(index / 10) + 1;
        return deferrer.defer(frameThreshold);
      });
    });
    
    // Reset deferred rendering on data change
    hooks.watch(items, () => {
      deferrer.reset();
    });
    
    return {
      visibleItems,
      deferrer
    };
  },
  template: `
    <div class="large-list">
      <div v-for="item in visibleItems" :key="item" class="list-item">
        {{ item }}
      </div>
    </div>
  `
};

Advanced Usage Patterns

Slot-Based Component Composition

Using instance slots for flexible component composition.

import { useInstanceSlots, hooks } from "@opentiny/vue-common";

export default {
  setup() {
    const slots = useInstanceSlots(hooks);
    
    // Dynamic slot analysis
    const slotAnalysis = hooks.computed(() => {
      const analysis = {
        hasContent: false,
        hasActions: false,
        hasCustom: false,
        customSlots: []
      };
      
      if (slots.default) analysis.hasContent = true;
      if (slots.actions) analysis.hasActions = true;
      
      // Find custom slots
      Object.keys(slots).forEach(slotName => {
        if (!['default', 'actions'].includes(slotName)) {
          analysis.hasCustom = true;
          analysis.customSlots.push(slotName);
        }
      });
      
      return analysis;
    });
    
    return {
      slots,
      slotAnalysis
    };
  },
  template: `
    <div class="flexible-component">
      <div v-if="slotAnalysis.hasContent" class="content">
        <slot />
      </div>
      
      <div v-for="customSlot in slotAnalysis.customSlots" 
           :key="customSlot" 
           :class="'custom-' + customSlot">
        <slot :name="customSlot" />
      </div>
      
      <div v-if="slotAnalysis.hasActions" class="actions">
        <slot name="actions" />
      </div>
    </div>
  `
};

Parent-Child Communication System

Building a comprehensive parent-child communication system.

import { useRelation, hooks } from "@opentiny/vue-common";

// Communication mixin
const useParentChildComm = () => {
  const relation = useRelation(hooks);
  
  // For parent components
  const createParentComm = () => {
    const children = hooks.ref(new Map());
    
    const addChild = (id, child) => {
      children.value.set(id, child);
    };
    
    const removeChild = (id) => {
      children.value.delete(id);
    };
    
    const broadcastToChildren = (message, data) => {
      children.value.forEach(child => {
        if (child.receiveMessage) {
          child.receiveMessage(message, data);
        }
      });
    };
    
    hooks.provide('parentComm', {
      addChild,
      removeChild,
      broadcastToChildren
    });
    
    return {
      children,
      broadcastToChildren
    };
  };
  
  // For child components
  const createChildComm = (childId) => {
    const parentComm = hooks.inject('parentComm', null);
    const messages = hooks.ref([]);
    
    const receiveMessage = (message, data) => {
      messages.value.push({ message, data, timestamp: Date.now() });
    };
    
    const sendToParent = (message, data) => {
      if (parentComm) {
        parentComm.receiveFromChild?.(childId, message, data);
      }
    };
    
    hooks.onMounted(() => {
      if (parentComm) {
        parentComm.addChild(childId, { receiveMessage });
      }
    });
    
    hooks.onBeforeUnmount(() => {
      if (parentComm) {
        parentComm.removeChild(childId);
      }
    });
    
    return {
      messages,
      receiveMessage,
      sendToParent
    };
  };
  
  return {
    createParentComm,
    createChildComm
  };
};

Performance-Optimized List Rendering

Using deferred rendering for large datasets.

import { useDefer, hooks } from "@opentiny/vue-common";

export default {
  props: {
    items: Array,
    chunkSize: {
      type: Number,
      default: 20
    }
  },
  setup(props) {
    const deferrer = useDefer();
    
    // Chunk items for progressive rendering
    const itemChunks = hooks.computed(() => {
      const chunks = [];
      for (let i = 0; i < props.items.length; i += props.chunkSize) {
        chunks.push(props.items.slice(i, i + props.chunkSize));
      }
      return chunks;
    });
    
    // Progressive chunk rendering
    const visibleChunks = hooks.computed(() => {
      return itemChunks.value.filter((_, chunkIndex) => {
        return deferrer.defer(chunkIndex + 1);
      });
    });
    
    const visibleItems = hooks.computed(() => {
      return visibleChunks.value.flat();
    });
    
    // Loading state
    const isLoading = hooks.computed(() => {
      return visibleItems.value.length < props.items.length;
    });
    
    // Reset on items change
    hooks.watch(() => props.items, () => {
      deferrer.reset();
    });
    
    return {
      visibleItems,
      isLoading,
      totalItems: hooks.computed(() => props.items.length),
      loadedItems: hooks.computed(() => visibleItems.value.length)
    };
  }
};

Integration with Component Setup

These hooks integrate seamlessly with the component setup system:

import { setup, useInstanceSlots, useRelation, useDefer } from "@opentiny/vue-common";

export default {
  setup(props, context) {
    return setup({
      props,
      context,
      renderless: (props, hooks, utils) => {
        // Use composition hooks within renderless functions
        const slots = useInstanceSlots(hooks);
        const relation = useRelation(hooks);
        const deferrer = useDefer(30);
        
        return {
          slots,
          relation,
          deferrer
        };
      },
      api: ['slots', 'relation', 'deferrer']
    });
  }
};

Install with Tessl CLI

npx tessl i tessl/npm-opentiny--vue-common

docs

adapter.md

breakpoints.md

composition-hooks.md

css-utilities.md

design-system.md

index.md

setup.md

svg-icons.md

theme-mode.md

tile.json