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 🙏

© 2025 – Pkg Stats / Ryan Hefner

seo-select

v4.3.7

Published

Customizable, high-performance select dropdown Web Component with search, virtual scroll, multiple selection, i18n, and framework compatibility (React, Vue, Svelte, Angular, Qwik). Accessible, SSR-safe, and zero dependencies.

Readme

SeoSelect

A lightweight and extensible select component built with pure Web Components (no dependencies), designed to work seamlessly across frameworks. Supports search, virtual scrolling, multiple selection, i18n, and flexible theming.

NPM GitHub stars

🌟 Support with a GitHub star — your encouragement means the world to me!

Features

  • 🎨 Multiple Themes: Basic, float themes with dark mode support
  • 🌍 Internationalization: Built-in support for multiple languages (EN, KO, JA, ZH)
  • 🔍 Search Functionality: Advanced multilingual search with fuzzy matching
  • Accessibility: Full keyboard navigation and screen reader support
  • 🚀 Virtual Scrolling: High performance with large datasets
  • 📏 Auto Width: Automatic width calculation based on content
  • 🎯 Multiple Selection: Tag-based multi-select with individual remove buttons
  • 💡 Zero Dependencies: Built with native Web Components (no Lit, no external libraries) for minimal bundle size and maximum compatibility.
  • Modern Event System: Standard addEventListener with type-safe helpers

Why SeoSelect?

SeoSelect is designed to solve the real-world pain points of select components across modern web projects. Here’s how it stands out compared to popular alternatives:

| Feature | SeoSelect | react-select | headlessui | antd / prime | | ------------------ | ----------------------------- | ---------------- | -------------- | --------------- | | Framework Agnostic | ✅ Web Component | ❌ React only | ❌ React/Vue | ❌ Framework-bound | | Bundle Size | ✅ Ultra-small (0 deps) | ❌ Large (~700KB+) | △ | ❌ Large | | Dependencies | ✅ Zero | ❌ Many | △ | ❌ Many | | Virtual Scroll | ✅ Built-in | ❌ Extra setup | ❌ None | △ Limited | | Multilingual Search| ✅ Korean/JP/Chinese support | ❌ None | ❌ None | ❌ None | | Search Accuracy | ✅ Fuzzy + Multilingual | △ Simple filter | ❌ | △ | | Keyboard Access | ✅ Full support | △ | △ | △ | | Type Safety | ✅ Global type extension | △ | △ | ❌ Complex | | Event Design | ✅ Standard + type helpers | ❌ React props | ❌ | ❌ | | Multi-Framework | ✅ React/Vue/Angular/Qwik/Solid| ❌ | ❌ | ❌ | | Customization | ✅ CSS Variables | △ | △ | ❌ Theme-locked | | SSR Safe | ✅ | ❌ | △ | ❌ |

Why choose SeoSelect?

  • Framework Independence: Use anywhere—React, Vue, Angular, Svelte, Qwik, or plain HTML. No lock-in.
  • Tiny & Dependency-Free: No runtime dependencies, minimal bundle size, and lightning-fast load times.
  • Advanced Search: Out-of-the-box support for Korean initial consonant, Japanese romaji, and Chinese pinyin search. Fuzzy matching for all languages.
  • Virtual Scrolling: Handles thousands of options smoothly without extra setup.
  • Accessibility: Full keyboard navigation and screen reader support.
  • TypeScript First: Global type extensions for safe, ergonomic event handling in any environment.
  • Customizable: Style every aspect with CSS variables—no theme lock-in.
  • SSR Safe: Works seamlessly in SSR (Next.js, Nuxt, SvelteKit, etc.) and browser environments.
  • Modern Event System: Standard addEventListener with type-safe helpers for best DX.

SeoSelect is the only select component you need for modern, scalable, and framework-agnostic web apps.

Installation

For Modern Bundlers (Recommended)

npm install seo-select
// Import basic select component
import 'seo-select';

// Import search-enabled select component
import 'seo-select/components/seo-select-search';

// Import Style
import 'seo-select/styles'

// Import Types
import 'seo-select/types';

For Direct Browser Usage

<!-- Import both JavaScript and CSS files -->
<link rel="stylesheet" href="./min/index.css">
<script type="module" src="./min/index.js"></script>

Alternative: Download pre-built files from GitHub Releases

Components Overview

seo-select - Basic Select Component

Standard dropdown functionality with virtual scrolling, multiple selection, themes, and form integration.

seo-select-search - Search-Enhanced Select Component

Extended component with real-time multilingual search including Korean initial consonant search (ㅎㄱㅇ → 한국어), Japanese romaji search (nihongo → 日本語), and Chinese pinyin search (beijing → 北京).

Basic Usage Examples

Simple Select

<!DOCTYPE html>
<html>
<head>
  <script type="module">
    import 'seo-select';
  </script>
</head>
<body>
  <seo-select name="country" required>
    <option value="us">United States</option>
    <option value="kr">South Korea</option>
    <option value="jp">Japan</option>
  </seo-select>
</body>
</html>

Search-enabled Select

<script type="module">
  import 'seo-select/components/seo-select-search';
</script>

<seo-select-search
  name="city"
  theme="float"
  language="en"
  show-reset>
  <option value="seoul">Seoul</option>
  <option value="tokyo">Tokyo</option>
  <option value="beijing">Beijing</option>
</seo-select-search>

Multiple Selection

<seo-select-search
  multiple
  name="skills"
  theme="float"
  show-reset>
  <option value="js">JavaScript</option>
  <option value="ts">TypeScript</option>
  <option value="python">Python</option>
</seo-select-search>

Event System

Standard addEventListener (Recommended)

// Works in any framework — listen to custom events emitted by the component.
// All event payloads are provided in `event.detail`.

searchSelect.addEventListener('onSelect', (event: CustomEvent<{ label: string; value: string }>) => {
  console.log('Selected:', event.detail.label, event.detail.value);
});

searchSelect.addEventListener('onDeselect', (event: CustomEvent<{ label: string; value: string }>) => {
  console.log('Deselected:', event.detail.label, event.detail.value);
});

searchSelect.addEventListener(
  'onReset',
  ( event: CustomEvent<{ value: string; label: string } | { values: string[]; labels: string[] } >) => {
    if ('values' in event.detail) {
      console.log('Reset multiple:', event.detail.values, event.detail.labels);
    } else {
      console.log('Reset single:', event.detail.value, event.detail.label);
    }
  }
);

searchSelect.addEventListener('onChange', () => {
  console.log('Value changed');
});

searchSelect.addEventListener('onOpen', (event: CustomEvent<{ selectInstance?: any }>) => {
  console.log('Dropdown opened', event.detail);
});

// Search-specific events (seo-select-search only)
searchSelect.addEventListener(
  'onSearchChange',
  ( event: CustomEvent<{ searchText: string } | { searchText: string; previousSearchText: string } > ) => {
    console.log('Search text changed:', event.detail.searchText);
  }
);

searchSelect.addEventListener(
  'onSearchFilter',
  (
    event: CustomEvent<{
      filteredOptions: VirtualSelectOption[];
      searchText: string;
      hasResults: boolean;
    }>
  ) => {
    console.log(
      `Search filtered: ${event.detail.filteredOptions.length} results for "${event.detail.searchText}"`
    );
  }
);

Type-Safe Helper Methods (Built-in)

// These helper methods wrap `addEventListener` for better DX (Developer Experience).
// They provide typed callbacks and cleaner syntax.

searchSelect.onSelect(({ label, value }) => {
  console.log('Selected:', label, value);
});

searchSelect.onDeselect(({ label, value }) => {
  console.log('Deselected:', label, value);
});

searchSelect.onReset((detail) => {
  console.log('Reset event:', detail);
});

searchSelect.onChange(() => {
  console.log('Value changed');
});

searchSelect.onOpen(() => {
  console.log('Dropdown opened');
});

// Search-specific helpers
searchSelect.onSearchChange((searchText) => {
  console.log('Search text changed:', searchText);
});

searchSelect.onSearchFilter((filteredOptions) => {
  console.log('Filtered results count:', filteredOptions.length);
});

JavaScript/TypeScript Usage

import 'seo-select/components/seo-select-search';

// Create the element programmatically
const select = document.createElement('seo-select-search');
select.optionItems = [
  { value: 'option1', label: 'Option 1' },
  { value: 'option2', label: 'Option 2' }
];
select.multiple = true;
select.theme = 'float';
select.language = 'ko';

// Append to the DOM
document.body.appendChild(select);

// Handle events using addEventListener
select.addEventListener('onSelect', (event: CustomEvent<{ label: string; value: string }>) => {
  console.log('Selected:', event.detail.label, event.detail.value);
});

// Or use the built-in helper methods
select.onSelect(({ label, value }) => {
  console.log('Selected:', label, value);
});

Events Reference

Common Events (Both Components)

| Event Name | Properties | Description | |------------|------------|-------------| | onSelect | { label, value } | User selects an option | | onDeselect | { label, value } | User removes selected option (multiple mode) | | onChange | - | Form value changes | | onReset | { value, label } or { values, labels } | Component resets to default | | onOpen | - | Dropdown opens |

Search Component Additional Events (SeoSelectSearch Only)

| Event Name | Properties | Description | |------------|------------|-------------| | onSearchChange | searchText: string | Search text changes in real-time | | onSearchFilter | filteredOptions: VirtualSelectOption[] | Search results are filtered |

Component Properties

Common Properties

| Property | Type | Default | Description | |----------|------|---------|-------------| | name | string | - | Form field name | | required | boolean | false | Whether the field is required | | multiple | boolean | false | Enable multiple selection | | theme | 'basic' \| 'float' | 'float' | Visual theme | | dark | boolean | false | Enable dark mode | | language | 'en' \| 'ko' \| 'ja' \| 'zh' | 'en' | Interface language | | showReset | boolean | true | Show reset button | | width | string | null | Custom width (auto-calculated if not set) |

SeoSelectSearch Additional Properties

| Property | Type | Default | Description | |----------|------|---------|-------------| | searchTexts | Partial<SearchLocalizedTexts> | {} | Custom search-related texts |

Methods

SeoSelect (Basic Component)

The basic seo-select component provides fundamental option management and value control methods:

Value Management

// Get/Set single value
const currentValue = select.value;
select.value = 'option1';

// Get/Set multiple values (when multiple=true)
const selectedValues = select.selectedValues; // ['opt1', 'opt2']
select.selectedValues = ['opt1', 'opt3'];

// Reset to default value
select.resetToDefaultValue();

Dynamic Option Management

// Add multiple options at once (replaces existing options)
select.addOptions([
  { value: 'opt1', label: 'Option 1' },
  { value: 'opt2', label: 'Option 2' }
], preserveSelection); // preserveSelection: boolean (default: false)

// Add a single option
select.addOption({ value: 'new', label: 'New Option' }, index); // index: optional position

// Remove a specific option by value
select.clearOption('option-value-to-remove');

// Remove all options
select.clearAllOptions();

// Batch update operations (highly optimized for performance)
select.batchUpdateOptions([
  { action: 'add', option: { value: 'new1', label: 'New 1' } },
  { action: 'remove', value: 'old-option' },
  { action: 'update', value: 'existing', option: { value: 'existing', label: 'Updated Label' } }
]);

Customization Methods

// Change language dynamically
select.setLanguage('ko'); // 'en' | 'ko' | 'ja' | 'zh'

// Set custom localized texts
select.setTexts({
  placeholder: 'Custom placeholder...',
  required: 'This field is required',
  resetToDefault: 'Reset selection'
});

// Enable/disable auto-width calculation
select.setAutoWidth(true);

// Clear internal caches (useful for memory optimization)
select.clearCaches();

Event Management (Type-Safe Helpers)

// Basic events (always available)
select.onSelect((event) => {
  console.log('Selected:', event.label, event.value);
});

select.onDeselect((event) => {
  console.log('Deselected:', event.label, event.value);
});

select.onReset((event) => {
  if (select.multiple) {
    console.log('Reset multiple:', event.values, event.labels);
  } else {
    console.log('Reset single:', event.value, event.label);
  }
});

select.onChange(() => {
  console.log('Form value changed');
});

select.onOpen(() => {
  console.log('Dropdown opened');
});

Utility Methods

// Get current option count
const optionCount = select.options.length;

// Get selected index (single mode only)
const selectedIndex = select.selectedIndex;

// Get default value
const defaultValue = select.defaultValue;

// Check if component has options
const hasOptions = !select.hasNoOptions();

SeoSelectSearch (Search-Enhanced Component)

The seo-select-search component extends the basic component with advanced search functionality:

Search-Specific Methods

// ► Practical example: Auto-focus search when dropdown opens
searchSelect.onOpen(() => {
  // Set initial search text to help users
  searchSelect.searchText = 'Seoul';

  // Or clear previous search and let user type fresh
  searchSelect.clearSearchText();
});

// ► Advanced example: Set search based on previous selection
let lastSelected = '';
searchSelect.onSelect((event) => {
  lastSelected = event.value;
});

searchSelect.onOpen(() => {
  if (lastSelected.startsWith('kr')) {
    searchSelect.searchText = 'Korean';
  }
});

Advanced Option Management

// Update options while preserving search state
searchSelect.updateOptionsWithSearch([
  { value: 'opt1', label: 'Searchable Option 1' },
  { value: 'opt2', label: 'Searchable Option 2' }
], preserveSearch); // preserveSearch: boolean (default: true)

// Load options dynamically based on search
await searchSelect.loadOptionsForSearch('search term', async (searchText) => {
  // Custom async function to load options
  const response = await fetch(`/api/search?q=${searchText}`);
  return await response.json();
});

Search Event Handling

// Listen to search text changes
searchSelect.onSearchChange((searchText) => {
  console.log('User typed:', searchText);
  // Trigger API calls, analytics, etc.
});

// Listen to search filter events
searchSelect.onSearchFilter((filteredOptions) => {
  console.log(`Found ${filteredOptions.length} results`);
  // Update UI indicators, show result counts, etc.
});

Custom Search Texts

// Set search-specific localized texts
searchSelect.setSearchTexts({
  searchPlaceholder: 'Type to search...',
  noMatchText: 'No results found'
});

Inherited Methods

The seo-select-search component inherits all methods from the basic seo-select component, so you can use all the value management, option management, and customization methods mentioned above.

Performance Considerations

// For large datasets, use batch operations
searchSelect.batchUpdateOptions(largeUpdateArray);

// Clear caches when dealing with frequent option updates
searchSelect.clearCaches();

// Use preserveSelection for better UX when updating options
searchSelect.addOptions(newOptions, true); // Preserves current selection

Framework Wrappers

seo-select provides official wrapper components for major frameworks. Each wrapper offers native-feeling APIs with proper event handling and TypeScript support.

Supported Frameworks

| Framework | Import Path | Version | |-----------|-------------|---------| | React | seo-select/react | >= 17.0.0 | | Vue | seo-select/vue | >= 3.0.0 | | Angular | seo-select/angular | >= 14.0.0 | | Solid.js | seo-select/solid | >= 1.0.0 | | Qwik | seo-select/qwik | >= 1.0.0 |

All framework dependencies are optional peer dependencies - only install what you need!


React

import { SeoSelect, SeoSelectSearch } from 'seo-select/react';
import 'seo-select/styles';

export default function MyComponent() {
  const options = [
    { value: 'react', label: 'React' },
    { value: 'nextjs', label: 'Next.js' },
    { value: 'remix', label: 'Remix' }
  ];

  return (
    <SeoSelect
      name="framework"
      theme="float"
      language="ko"
      optionItems={options}
      onSelect={(e) => console.log('Selected:', e.detail)}
      onReset={(e) => console.log('Reset:', e.detail)}
    />
  );
}

With Search

import { SeoSelectSearch, type SeoSelectRef } from 'seo-select/react';
import { useRef } from 'react';

export default function SearchExample() {
  const selectRef = useRef<SeoSelectRef>(null);

  const handleReset = () => {
    selectRef.current?.reset();
  };

  return (
    <>
      <SeoSelectSearch
        ref={selectRef}
        name="city"
        multiple
        showReset
        optionItems={[
          { value: 'seoul', label: 'Seoul' },
          { value: 'tokyo', label: 'Tokyo' },
          { value: 'beijing', label: 'Beijing' }
        ]}
        onSelect={(e) => console.log('Selected:', e.detail)}
        onSearchChange={(e) => console.log('Search:', e.detail.searchText)}
      />
      <button onClick={handleReset}>Reset</button>
    </>
  );
}

Vue 3

<script setup lang="ts">
import { SeoSelect, SeoSelectSearch } from 'seo-select/vue';
import 'seo-select/styles';

const options = [
  { value: 'vue', label: 'Vue 3' },
  { value: 'nuxt', label: 'Nuxt 3' },
  { value: 'vite', label: 'Vite' }
];

const handleSelect = (detail: { label: string; value: string }) => {
  console.log('Selected:', detail);
};
</script>

<template>
  <SeoSelect
    name="framework"
    theme="float"
    :optionItems="options"
    @select="handleSelect"
  />
</template>

With Search and Multiple Selection

<script setup lang="ts">
import { ref } from 'vue';
import { SeoSelectSearch } from 'seo-select/vue';

const selectRef = ref();

const handleReset = () => {
  selectRef.value?.reset();
};
</script>

<template>
  <SeoSelectSearch
    ref="selectRef"
    name="city"
    multiple
    showReset
    :optionItems="[
      { value: 'seoul', label: 'Seoul' },
      { value: 'tokyo', label: 'Tokyo' }
    ]"
    @select="(detail) => console.log('Selected:', detail)"
    @searchChange="(detail) => console.log('Search:', detail.searchText)"
  />
  <button @click="handleReset">Reset</button>
</template>

Angular

import { Component } from '@angular/core';
import { SeoSelectComponent, SeoSelectSearchComponent } from 'seo-select/angular';
import 'seo-select/styles';

@Component({
  selector: 'app-select-demo',
  standalone: true,
  imports: [SeoSelectComponent, SeoSelectSearchComponent],
  template: `
    <app-seo-select
      name="framework"
      theme="float"
      [optionItems]="options"
      (selectEvent)="onSelect($event)"
      (resetEvent)="onReset($event)"
    />
  `
})
export class SelectDemoComponent {
  options = [
    { value: 'angular', label: 'Angular' },
    { value: 'ionic', label: 'Ionic' },
    { value: 'ngrx', label: 'NgRx' }
  ];

  onSelect(detail: { label: string; value: string }) {
    console.log('Selected:', detail);
  }

  onReset(detail: any) {
    console.log('Reset:', detail);
  }
}

With Search

@Component({
  selector: 'app-search-demo',
  standalone: true,
  imports: [SeoSelectSearchComponent],
  template: `
    <app-seo-select-search
      name="city"
      [multiple]="true"
      [showReset]="true"
      [optionItems]="options"
      (selectEvent)="onSelect($event)"
      (searchChangeEvent)="onSearchChange($event)"
    />
  `
})
export class SearchDemoComponent {
  options = [
    { value: 'seoul', label: 'Seoul' },
    { value: 'tokyo', label: 'Tokyo' }
  ];

  onSelect(detail: { label: string; value: string }) {
    console.log('Selected:', detail);
  }

  onSearchChange(detail: { searchText: string }) {
    console.log('Search:', detail.searchText);
  }
}

Solid.js

import { SeoSelect, SeoSelectSearch } from 'seo-select/solid';
import 'seo-select/styles';

export default function MyComponent() {
  const options = [
    { value: 'solid', label: 'Solid.js' },
    { value: 'start', label: 'SolidStart' }
  ];

  return (
    <SeoSelect
      name="framework"
      theme="float"
      optionItems={options}
      onSelect={(e) => console.log('Selected:', e.detail)}
    />
  );
}

With Ref

import { SeoSelectSearch } from 'seo-select/solid';

export default function SearchExample() {
  let selectEl: HTMLElement | undefined;

  const handleReset = () => {
    (selectEl as any)?.reset?.();
  };

  return (
    <>
      <SeoSelectSearch
        ref={(el) => { selectEl = el; }}
        name="city"
        multiple
        optionItems={[
          { value: 'seoul', label: 'Seoul' },
          { value: 'tokyo', label: 'Tokyo' }
        ]}
        onSelect={(e) => console.log('Selected:', e.detail)}
        onSearchChange={(e) => console.log('Search:', e.detail.searchText)}
      />
      <button onClick={handleReset}>Reset</button>
    </>
  );
}

Qwik

import { component$ } from '@builder.io/qwik';
import { SeoSelect, SeoSelectSearch } from 'seo-select/qwik';
import 'seo-select/styles';

export const SelectDemo = component$(() => {
  const options = [
    { value: 'qwik', label: 'Qwik' },
    { value: 'qwikcity', label: 'QwikCity' }
  ];

  return (
    <SeoSelect
      name="framework"
      theme="float"
      optionItems={options}
      onSelect$={(e) => console.log('Selected:', e.detail)}
    />
  );
});

With Search

import { component$ } from '@builder.io/qwik';
import { SeoSelectSearch } from 'seo-select/qwik';

export const SearchDemo = component$(() => {
  return (
    <SeoSelectSearch
      name="city"
      multiple
      showReset
      optionItems={[
        { value: 'seoul', label: 'Seoul' },
        { value: 'tokyo', label: 'Tokyo' }
      ]}
      onSelect$={(e) => console.log('Selected:', e.detail)}
      onSearchChange$={(e) => console.log('Search:', e.detail.searchText)}
    />
  );
});

Vanilla JavaScript / Web Component

For vanilla JavaScript or other frameworks, use the web component directly:

import 'seo-select/types';
import 'seo-select/styles';
import 'seo-select/components/seo-select-search';

const select = document.createElement('seo-select-search');

select.optionItems = [
  { value: 'vanilla', label: 'Vanilla JS' },
  { value: 'typescript', label: 'TypeScript' }
];
select.theme = 'float';
select.multiple = true;

select.addEventListener('onSelect', (event) => {
  console.log('Selected:', event.detail);
});

document.body.appendChild(select);

TypeScript Support

Type Definitions

For TypeScript projects, import the type definitions to enable full type safety and IntelliSense support for custom events and component APIs:

// Import type definitions (add this once in your project)
import 'seo-select/types';

// Import components
import 'seo-select';
import 'seo-select/components/seo-select-search';

That's it! With just import 'seo-select/types';, you get:

  • Full type safety for all event listeners (addEventListener)
  • IntelliSense support for event properties (event.label, event.value)
  • Global type extensions for HTMLElementEventMap
  • Zero configuration - works immediately

Basic Usage with Type Safety

import 'seo-select/types';
import 'seo-select';

const select = document.createElement('seo-select');

// All event listeners are now fully type-safe
select.addEventListener('onSelect', (event) => {
  // TypeScript knows event.label and event.value exist
  console.log('Selected:', event.label, event.value);
});

select.addEventListener('onReset', (event) => {
  // TypeScript automatically infers the correct event type
  if (event.values) {
    console.log('Multiple reset:', event.values, event.labels);
  } else {
    console.log('Single reset:', event.value, event.label);
  }
});

Advanced Usage with Specific Types

When you need specific types for your application logic, import them explicitly:

import 'seo-select/types';
import type {
  VirtualSelectOption,
  SeoSelectElement,
  SeoSelectSearchElement,
  SupportedLanguage,
  BatchUpdateOption
} from 'seo-select/types';

// Type-safe option creation
const options: VirtualSelectOption[] = [
  { value: 'us', label: 'United States' },
  { value: 'kr', label: 'South Korea' }
];

// Type-safe element creation
const selectElement = document.createElement('seo-select') as SeoSelectElement;
selectElement.optionItems = options;
selectElement.language = 'ko' as SupportedLanguage;

// Type-safe batch operations
const updates: BatchUpdateOption[] = [
  { action: 'add', option: { value: 'jp', label: 'Japan' } },
  { action: 'remove', value: 'us' }
];
selectElement.batchUpdateOptions(updates);

Available Types

| Category | Types | |----------|-------| | Component Elements | SeoSelectElement, SeoSelectSearchElement | | Options & Data | VirtualSelectOption, OptionItem, BatchUpdateOption | | Configuration | SupportedLanguage, SelectTheme, LocalizedTexts, SearchLocalizedTexts | | Component Props | SeoSelectProps, SeoSelectSearchProps | | Events | ResetEventData, SeoSelectEventType, SeoSelectEvents |

Event Constants

import { SeoSelectEvents } from 'seo-select/types';

// Use event constants for consistency
select.addEventListener(SeoSelectEvents.SELECT, (event) => {
  console.log('Selected:', event.label);
});

select.addEventListener(SeoSelectEvents.SEARCH_CHANGE, (event) => {
  console.log('Search text:', event.detail);
});

Styling and Customization

Quick Styling Example

seo-select {
  --select-border-color: #ccc;
  --select-focus-color: #007bff;
  --select-background: white;
  --select-text-color: #333;
}

/* Dark mode */
seo-select[dark] {
  --select-background: #374151;
  --select-text-color: #f3f4f6;
  --select-border-color: #6b7280;
}

Complete CSS Variables Reference

| Variable | Default Value | Description | |----------|---------------|-------------| | --select-padding | 0.5rem 0.8rem | Internal padding of select box | | --select-min-height | 35px | Minimum height of select component | | --select-min-width | 150px | Minimum width of select component | | --select-border-width | 1px | Border thickness | | --select-transition-duration | 0.3s | Animation transition duration | | --select-transition-easing | ease | Animation easing function | | --select-font-size | 12px | Font size of select text | | --select-font-color | #1f1b25 | Font color of select text |

Basic Theme: | Variable | Default Value | Description | |----------|---------------|-------------| | --select-basic-border-radius | 0 | Border radius for basic theme | | --select-basic-box-shadow | none | Box shadow for basic theme | | --select-basic-margin-bottom | 0 | Bottom margin for basic theme |

Float Theme: | Variable | Default Value | Description | |----------|---------------|-------------| | --select-float-border-radius | 5px | Border radius for float theme | | --select-float-box-shadow | 0 2px 4px rgba(0, 0, 0, 0.1) | Box shadow for float theme | | --select-float-margin-bottom | 0 | Bottom margin for float theme |

| Variable | Default Value | Description | |----------|---------------|-------------| | --select-multi-padding-right | 3rem | Right padding for multiple selection | | --select-tags-gap | 0.25rem | Gap between tags in multiple mode | | --select-tags-padding | 0.25rem 0 | Padding around tags container | | --select-tags-min-height | 1.5rem | Minimum height of tags area | | --tag-padding | 0.2rem 0.3rem | Internal padding of tags | | --tag-gap | 0.5rem | Gap between individual tags | | --tag-border-radius | 25rem | Border radius of tags (pill shape) | | --tag-border-width | 1px | Border thickness of tags | | --tag-remove-size | 1rem | Size of tag remove button | | --tag-remove-border-radius | 50% | Border radius of remove button | | --tag-remove-transition | all 0.2s ease | Transition for remove button |

Basic Dropdown: | Variable | Default Value | Description | |----------|---------------|-------------| | --dropdown-box-shadow | 0 5px 10px rgba(0, 0, 0, 0.1) | Box shadow of dropdown | | --dropdown-border-width | 1px | Border thickness of dropdown | | --dropdown-z-index | 1000 | Z-index stacking order | | --dropdown-basic-border-radius | 0 | Border radius for basic theme dropdown |

Float Theme Dropdown: | Variable | Default Value | Description | |----------|---------------|-------------| | --dropdown-float-border-radius | 5px | Border radius for float theme dropdown | | --dropdown-float-box-shadow | 0 8px 16px rgba(0, 0, 0, 0.15) | Enhanced shadow for float theme | | --dropdown-float-top | 130% | Dropdown position from select box | | --dropdown-float-animation-duration | 0.2s | Animation duration for float theme | | --dropdown-float-animation-easing | ease-out | Animation easing for float theme | | --dropdown-float-slide-distance | -20px | Slide distance for animation |

| Variable | Default Value | Description | |----------|---------------|-------------| | --search-input-padding | 0.3rem | Internal padding of search input | | --search-input-text-indent | 1.5rem | Text indentation for search icon space | | --search-icon-left | 0.7rem | Left position of search icon | | --search-icon-size | 1rem | Size of search icon |

| Variable | Default Value | Description | |----------|---------------|-------------| | --loading-container-padding | 1rem 2rem | Padding around loading container | | --loading-dots-gap | 0.5rem | Gap between loading dots | | --loading-dots-margin-bottom | 1rem | Bottom margin of dots container | | --loading-dot-size | 0.5rem | Size of individual loading dots | | --loading-animation-duration | 1.4s | Duration of loading animation | | --loading-text-font-size | 0.9rem | Font size of loading text | | --loading-dot-delay-1 | -0.32s | Animation delay for first dot | | --loading-dot-delay-2 | -0.16s | Animation delay for second dot | | --loading-dot-delay-3 | 0s | Animation delay for third dot |

| Variable | Default Value | Description | |----------|---------------|-------------| | --no-data-container-padding | 1rem 2rem | Padding of "no data" container | | --no-data-text-font-size | 0.9rem | Font size of "no data" text |

| Variable | Default Value | Description | |----------|---------------|-------------| | --option-padding | 0 0.8rem | Internal padding of option items | | --option-line-height | 300% | Line height of option items | | --option-check-mark-margin | 0.5rem | Margin of checkmark in options |

Single Select Reset: | Variable | Default Value | Description | |----------|---------------|-------------| | --reset-button-right | 3rem | Right position of reset button | | --reset-button-padding | 0 0.5rem | Internal padding of reset button | | --reset-button-height | 80% | Height of reset button | | --reset-button-font-size | 0.9rem | Font size of reset button | | --reset-button-transition | all 0.2s ease | Transition for reset button |

Multiple Select Reset: | Variable | Default Value | Description | |----------|---------------|-------------| | --multi-reset-button-size | 1.5rem | Size of multi-select reset button | | --multi-reset-button-position | -0.6rem | Position adjustment for multi reset | | --multi-reset-button-border-radius | 50% | Border radius of multi reset button | | --multi-reset-button-font-size | 0.9rem | Font size of multi reset button |

| Variable | Default Value | Description | |----------|---------------|-------------| | --arrow-right | 0.8rem | Right position of dropdown arrow | | --arrow-font-size | 0.9rem | Font size of dropdown arrow | | --arrow-margin-top | -0.1rem | Top margin adjustment for arrow | | --arrow-transition | transform 0.2s ease | Transition for arrow rotation |

Primary Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --primary-color | #3253de | Main brand color | | --primary-hover | #003766 | Primary color on hover state | | --primary-bg-color | #e5f1fbf2 | Primary background color | | --secondary-color | #5f77ca | Secondary brand color | | --secondary-hover | #303c65f2 | Secondary color on hover state | | --secondary-bg-color | #eff1faf2 | Secondary background color |

Semantic State Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --success-color | pick(green, 5) | Success state color | | --success-hover | pick(green, 6) | Success color on hover | | --success-bg-color | pick(green, 0) | Success background color | | --error-color | pick(red, 5) | Error state color | | --error-hover | pick(red, 6) | Error color on hover | | --error-bg-color | color.mix(#fff, pick(red, 1), 25%) | Error background color | | --warning-color | pick(orange, 4) | Warning state color | | --warning-hover | pick(orange, 5) | Warning color on hover | | --warning-bg-color | color.mix(#fff, pick(orange, 1), 25%) | Warning background color | | --disabled-color | color.adjust(pick(gray, 3), $lightness: 5%) | Disabled element color |

Typography Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --font-color | color.mix(#000, pick(indigo, 10), 70%) | Primary text color | | --font-secondary-color | pick(gray, 5) | Secondary text color |

UI Element Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --border-color | pick(gray, 3) | Default border color | | --box-head-border-color | color.mix(#fff, pick(gray, 2), 35%) | Box header border color |

Extended Color Palette (Open Color System): The component uses a sophisticated color system based on Open Color with primary color mixing:

| Color Name | Usage | Available Shades | |------------|-------|------------------| | gray | Neutral elements, borders, backgrounds | 0-10 (lightest to darkest) | | red | Error states, destructive actions | 0-10 | | pink | Accent colors, highlights | 0-10 | | grape | Decorative elements | 0-10 | | violet | Special highlights | 0-10 | | indigo | Primary variants | 0-10 | | blue | Information states, links | 0-10 | | cyan | Cool accents | 0-10 | | teal | Fresh accents | 0-10 | | green | Success states, positive actions | 0-10 | | lime | Fresh highlights | 0-10 | | yellow | Warning states, attention | 0-10 | | orange | Warning states, alerts | 0-10 |

Using the Color System:

// Access colors using the pick() function
.my-element {
  background-color: pick(blue, 2);    // Light blue
  border-color: pick(blue, 5);        // Medium blue
  color: pick(blue, 8);               // Dark blue
}

Background Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --dark-select-bg | #374151 | Background color in dark mode | | --dark-dropdown-bg | #374151 | Dropdown background in dark mode | | --dark-tag-bg | #4b5563 | Tag background in dark mode | | --dark-search-input-bg | #374151 | Search input background in dark mode | | --dark-accent-bg | #1f2937 | Darker background for special elements | | --dark-card-bg | #374151 | Card/container background | | --dark-surface-bg | #4b5563 | Surface element background |

Border Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --dark-border-color | #6b7280 | Border color in dark mode | | --dark-border-hover-color | #60a5fa | Border color on hover in dark mode | | --dark-border-focus-color | #60a5fa | Border color on focus in dark mode |

Text Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --dark-text-color | #f3f4f6 | Primary text color in dark mode | | --dark-text-secondary-color | #d1d5db | Secondary text color in dark mode | | --dark-placeholder-color | #9ca3af | Placeholder color in dark mode | | --dark-highlight-color | #fbbf24 | Highlight/accent color in dark mode |

Dark Mode State Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --dark-success-color | #10b981 | Success state color in dark mode | | --dark-warning-color | #f59e0b | Warning state color in dark mode | | --dark-error-color | #ef4444 | Error state color in dark mode | | --dark-info-color | #3b82f6 | Information state color in dark mode |

Option Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --dark-option-hover-bg | #4b5563 | Option background on hover | | --dark-option-selected-bg | #3b82f6 | Selected option background | | --dark-option-focused-bg | #4b5563 | Focused option background | | --dark-option-disabled-color | #9ca3af | Disabled option text color |

Tag Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --dark-tag-text-color | #f3f4f6 | Tag text color in dark mode | | --dark-tag-border-color | #60a5fa | Tag border color in dark mode | | --dark-tag-remove-hover-bg | #ef4444 | Tag remove button hover background |

Button Colors: | Variable | Default Value | Description | |----------|---------------|-------------| | --dark-reset-button-color | #d1d5db | Reset button color in dark mode | | --dark-reset-button-hover-color | #ef4444 | Reset button hover color | | --dark-multi-reset-button-bg | #6b7280 | Multi reset button background |

Shadow Effects: | Variable | Default Value | Description | |----------|---------------|-------------| | --dark-float-box-shadow | 0 4px 6px rgba(0, 0, 0, 0.4), 0 2px 4px rgba(0, 0, 0, 0.2) | Float theme shadow in dark mode | | --dark-dropdown-box-shadow | 0 10px 20px rgba(0, 0, 0, 0.5), 0 4px 8px rgba(0, 0, 0, 0.3) | Dropdown shadow in dark mode | | --dark-search-input-focus-shadow | 0 0 0 2px rgba(96, 165, 250, 0.3), 0 4px 12px rgba(0, 0, 0, 0.3) | Search input focus shadow |

License

MIT License - see LICENSE file for details.