CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vue-multiselect

Vue 3 compatible multiselect component with advanced selection, search, tagging, and grouping capabilities

Pending
Overview
Eval results
Files

tagging-mode.mddocs/

Tagging Mode

Advanced tagging functionality allowing users to create new options dynamically through text input.

Capabilities

Basic Tagging

Enable tagging mode to allow users to create new options by typing and pressing Enter.

/**
 * Basic tagging configuration props
 */
interface BasicTaggingProps {
  /** Enable tagging functionality (default: false) */
  taggable?: boolean;
  
  /** Placeholder text shown when highlighting tag creation (default: 'Press enter to create a tag') */
  tagPlaceholder?: string;
  
  /** Position of tag creation option ('top' | 'bottom', default: 'top') */
  tagPosition?: 'top' | 'bottom';
}

Usage Example:

<template>
  <VueMultiselect
    v-model="selectedTags"
    :options="availableTags"
    :multiple="true"
    :taggable="true"
    :searchable="true"
    @tag="addTag"
    tag-placeholder="Add this as new tag"
    placeholder="Select or create tags">
  </VueMultiselect>
</template>

<script>
export default {
  data() {
    return {
      selectedTags: [],
      availableTags: ['Vue.js', 'React', 'Angular']
    }
  },
  methods: {
    addTag(newTag) {
      const tag = {
        name: newTag,
        code: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
      };
      this.availableTags.push(tag);
      this.selectedTags.push(tag);
    }
  }
}
</script>

Tagging Events

Events emitted during tag creation operations.

/**
 * Tagging-related events
 */
interface TaggingEvents {
  /**
   * Emitted when user creates a new tag
   * @param searchQuery - Text entered by user for new tag
   * @param id - Component identifier
   */
  '@tag': (searchQuery: string, id: string | number) => void;
}

Tag Creation Logic

Control how new tags are created and integrated with existing options.

<template>
  <VueMultiselect
    v-model="selectedSkills"
    :options="skills"
    :multiple="true"
    :taggable="true"
    :searchable="true"
    @tag="createSkill"
    label="name"
    track-by="id"
    tag-placeholder="Create new skill"
    placeholder="Select or add skills">
  </VueMultiselect>
</template>

<script>
export default {
  data() {
    return {
      selectedSkills: [],
      skills: [
        { id: 1, name: 'JavaScript', category: 'Programming' },
        { id: 2, name: 'Design', category: 'Creative' }
      ],
      nextId: 3
    }
  },
  methods: {
    createSkill(name) {
      // Validate tag name
      if (name.length < 2) {
        alert('Skill name must be at least 2 characters');
        return;
      }
      
      // Check for duplicates
      const exists = this.skills.some(skill => 
        skill.name.toLowerCase() === name.toLowerCase()
      );
      
      if (exists) {
        alert('This skill already exists');
        return;
      }
      
      // Create new skill
      const newSkill = {
        id: this.nextId++,
        name: name,
        category: 'Custom'
      };
      
      // Add to options and select it
      this.skills.push(newSkill);
      this.selectedSkills.push(newSkill);
    }
  }
}
</script>

Tag Positioning

Control where the tag creation option appears in the dropdown.

/**
 * Tag positioning configuration
 */
interface TagPositioningProps {
  /** 
   * Position of tag creation option in dropdown
   * 'top' - Show above search results (default)
   * 'bottom' - Show below search results
   */
  tagPosition?: 'top' | 'bottom';
}

Usage Example:

<template>
  <VueMultiselect
    v-model="selectedCategories"
    :options="categories"
    :multiple="true"
    :taggable="true"
    tag-position="bottom"
    tag-placeholder="Create new category"
    @tag="addCategory">
  </VueMultiselect>
</template>

<script>
export default {
  methods: {
    addCategory(categoryName) {
      // Add category to bottom of list
      this.categories.push(categoryName);
      this.selectedCategories.push(categoryName);
    }
  }
}
</script>

Advanced Tag Creation

Implement complex tag creation with validation, transformation, and async operations.

<template>
  <VueMultiselect
    v-model="selectedTags"
    :options="availableTags"
    :multiple="true"
    :taggable="true"
    :loading="isCreatingTag"
    @tag="createTagAsync"
    label="name"
    track-by="id"
    tag-placeholder="Create and save new tag"
    placeholder="Select existing or create new tags">
    
    <template #tag="{ option, remove }">
      <span class="custom-tag">
        <span>{{ option.name }}</span>
        <span v-if="option.isNew" class="tag-new-indicator">NEW</span>
        <button @click="remove(option)" class="tag-remove">×</button>
      </span>
    </template>
  </VueMultiselect>
</template>

<script>
export default {
  data() {
    return {
      selectedTags: [],
      availableTags: [
        { id: 1, name: 'JavaScript', isNew: false },
        { id: 2, name: 'Vue.js', isNew: false }
      ],
      isCreatingTag: false
    }
  },
  methods: {
    async createTagAsync(tagName) {
      // Validate tag name
      if (!this.validateTagName(tagName)) {
        return;
      }
      
      this.isCreatingTag = true;
      
      try {
        // Transform tag name
        const normalizedName = this.normalizeTagName(tagName);
        
        // Check if tag exists (case-insensitive)
        const existingTag = this.availableTags.find(tag =>
          tag.name.toLowerCase() === normalizedName.toLowerCase()
        );
        
        if (existingTag) {
          // Select existing tag instead
          if (!this.selectedTags.includes(existingTag)) {
            this.selectedTags.push(existingTag);
          }
          return;
        }
        
        // Create new tag via API
        const newTag = await this.saveTagToServer(normalizedName);
        
        // Add to local state
        this.availableTags.push(newTag);
        this.selectedTags.push(newTag);
        
      } catch (error) {
        console.error('Failed to create tag:', error);
        alert('Failed to create tag. Please try again.');
      } finally {
        this.isCreatingTag = false;
      }
    },
    
    validateTagName(name) {
      if (!name || name.trim().length < 2) {
        alert('Tag name must be at least 2 characters');
        return false;
      }
      
      if (name.length > 50) {
        alert('Tag name must be less than 50 characters');
        return false;
      }
      
      if (!/^[a-zA-Z0-9\s\-.]+$/.test(name)) {
        alert('Tag name contains invalid characters');
        return false;
      }
      
      return true;
    },
    
    normalizeTagName(name) {
      return name.trim()
        .replace(/\s+/g, ' ')  // Replace multiple spaces with single space
        .toLowerCase()
        .replace(/^\w/, c => c.toUpperCase()); // Capitalize first letter
    },
    
    async saveTagToServer(name) {
      const response = await fetch('/api/tags', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ name })
      });
      
      if (!response.ok) {
        throw new Error('Failed to save tag');
      }
      
      const tag = await response.json();
      return {
        ...tag,
        isNew: true
      };
    }
  }
}
</script>

<style>
.custom-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  background: #e3f2fd;
  border-radius: 4px;
}

.tag-new-indicator {
  font-size: 10px;
  background: #4caf50;
  color: white;
  padding: 1px 4px;
  border-radius: 2px;
}

.tag-remove {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 14px;
  color: #666;
}
</style>

Tag Management

Methods for managing tags programmatically.

/**
 * Tag management methods
 */
interface TagManagementMethods {
  /** Check if a tag with given text already exists */
  isExistingOption(query: string): boolean;
  
  /** Add a new tag programmatically */
  addTag(tagData: any): void;
  
  /** Remove a tag from available options */
  removeTag(tag: any): void;
  
  /** Get all currently selected tags */
  getSelectedTags(): any[];
}

Tagging with Objects

Use tagging with object-based options for more complex tag structures.

<template>
  <VueMultiselect
    v-model="selectedTopics"
    :options="topics"
    :multiple="true"
    :taggable="true"
    @tag="createTopic"
    label="title"
    track-by="slug"
    tag-placeholder="Create new topic">
  </VueMultiselect>
</template>

<script>
export default {
  data() {
    return {
      selectedTopics: [],
      topics: [
        { slug: 'javascript', title: 'JavaScript', color: '#f7df1e' },
        { slug: 'vue', title: 'Vue.js', color: '#4fc08d' }
      ]
    }
  },
  methods: {
    createTopic(title) {
      const slug = title.toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/(^-|-$)/g, '');
      
      const newTopic = {
        slug: slug,
        title: title,
        color: this.getRandomColor(),
        isCustom: true
      };
      
      this.topics.push(newTopic);
      this.selectedTopics.push(newTopic);
    },
    
    getRandomColor() {
      const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#feca57'];
      return colors[Math.floor(Math.random() * colors.length)];
    }
  }
}
</script>

Keyboard Shortcuts

Tagging supports standard keyboard shortcuts for tag creation.

/**
 * Keyboard shortcuts for tagging
 */
interface TaggingKeyboardShortcuts {
  /** Enter key - Create tag from current search query */
  'Enter': void;
  
  /** Tab key - Create tag and continue to next field */
  'Tab': void;
  
  /** Escape key - Cancel tag creation and close dropdown */
  'Escape': void;
}

Usage Notes:

  • Press Enter while typing to create a new tag
  • Press Tab to create a tag and move focus to next form element
  • Press Escape to cancel tag creation and close dropdown
  • Tags are created from the current search input value
  • Tag creation only works when taggable prop is true

Install with Tessl CLI

npx tessl i tessl/npm-vue-multiselect

docs

advanced-configuration.md

basic-selection.md

custom-rendering.md

grouped-options.md

index.md

search-filtering.md

tagging-mode.md

tile.json