# Bootstrap Multiselect

A jQuery plugin for Bootstrap 5 that transforms a `<select multiple>` into a searchable, nested dropdown with checkboxes, collapsible accordion groups, and unlimited nesting depth.

## Features

- Unlimited nesting depth (grandparent > parent > child > ...)
- Collapsible accordion groups with expand/collapse all
- Tri-state checkboxes (checked, unchecked, indeterminate)
- Selecting a parent selects all children; selecting all children auto-checks the parent
- Search/filter with debounced input
- "Select all" with smart visible-only mode during search
- Smart display text ("3 selected", "All selected")
- Disabled options (individual or entire groups)
- Parents can have their own values
- Pre-select items via `selected: true` in data
- Bootstrap 5.3 dark mode support (automatic via `data-bs-theme`)
- Hidden native `<select>` stays in sync — `$('#el').val()` just works
- No extra dependencies beyond jQuery and Bootstrap 5

## Quickstart

### 1. Include dependencies

```html
<!-- CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="src/bootstrap-multiselect.css" rel="stylesheet">

<!-- JS -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script src="src/bootstrap-multiselect.js"></script>
```

### 2. Add a select element

```html
<select id="mySelect" multiple></select>
```

### 3. Initialize with JS data

```js
$('#mySelect').multiselect({
    data: [
        {
            label: 'Group 1', value: 'grp1',
            children: [
                { label: 'Option A', value: 'a' },
                { label: 'Option B', value: 'b', selected: true },
                { label: 'Option C', value: 'c', disabled: true }
            ]
        },
        {
            label: 'Group 2', value: 'grp2', disabled: true,
            children: [
                { label: 'Option D', value: 'd' },
                { label: 'Option E', value: 'e' }
            ]
        },
        { label: 'Option F', value: 'f' }
    ]
});
```

### Or initialize from HTML

```html
<select id="mySelect" multiple>
    <optgroup label="Group 1">
        <option value="a">Option A</option>
        <option value="b" selected>Option B</option>
        <option value="c" disabled>Option C</option>
    </optgroup>
    <optgroup label="Group 2" disabled>
        <option value="d">Option D</option>
    </optgroup>
</select>

<script>
$('#mySelect').multiselect();
</script>
```

### 4. Read values

```js
// Standard jQuery — returns array of selected values
$('#mySelect').val();
```

## Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `data` | `Array\|null` | `null` | JS data provider. Each item: `{ label, value?, children?, selected?, disabled? }`. If `null`, parses from the `<select>` DOM. |
| `placeholder` | `string` | `'Select...'` | Text shown when nothing is selected. |
| `enableSearch` | `boolean` | `true` | Show/hide the search input. |
| `searchPlaceholder` | `string` | `'Search...'` | Placeholder for the search input. |
| `selectAll` | `boolean` | `true` | Show/hide the "Select all" checkbox. |
| `selectAllText` | `string` | `'Select all'` | Label for select-all. |
| `selectAllJustVisible` | `boolean` | `true` | When searching, "Select all" only affects visible items. |
| `collapseAll` | `boolean` | `true` | Start with all groups collapsed. |
| `maxHeight` | `number` | `250` | Max height (px) of the scrollable options area. |
| `numberDisplayed` | `number` | `3` | Max items to show as labels before switching to count text. |
| `nSelectedText` | `string` | `'# selected'` | Text when count exceeds `numberDisplayed`. `#` is replaced with count. |
| `allSelectedText` | `string` | `'All selected'` | Text when everything is selected. Set to `''` to disable. |
| `onChange` | `function\|null` | `null` | Callback on selection change: `function(selectedValues) {}`. |

## Methods

```js
// Selection
$('#el').multiselect('select', 'value')         // Select by value (string or array)
$('#el').multiselect('deselect', 'value')        // Deselect by value
$('#el').multiselect('selectAll')                // Select all
$('#el').multiselect('deselectAll')              // Deselect all
$('#el').multiselect('getSelected')              // Get selected values array

// Expand / Collapse
$('#el').multiselect('expand')                   // Expand all groups
$('#el').multiselect('expand', 'groupValue')     // Expand specific group(s)
$('#el').multiselect('collapse')                 // Collapse all groups
$('#el').multiselect('collapse', 'groupValue')   // Collapse specific group(s)

// Enable / Disable
$('#el').multiselect('enable')                   // Enable widget
$('#el').multiselect('disable')                  // Disable widget
$('#el').multiselect('enableOptions', 'value')   // Enable specific option(s)
$('#el').multiselect('disableOptions', 'value')  // Disable specific option(s)

// Lifecycle
$('#el').multiselect('refresh')                  // Rebuild from data
$('#el').multiselect('destroy')                  // Remove widget, restore <select>
```

All methods that accept values take either a single string or an array of strings.

## Dark Mode

The plugin automatically supports Bootstrap 5.3 dark mode. All colors use Bootstrap CSS variables that adapt when `data-bs-theme="dark"` is set on the `<html>` element.

```js
// Toggle dark mode
document.documentElement.setAttribute('data-bs-theme', 'dark');
```

No extra CSS is required.

## CSS Classes

All classes use the `bsms-` prefix. Override these to customize appearance.

| Class | Description |
|-------|-------------|
| `.bsms-container` | Root wrapper (`position: relative; width: 100%`) |
| `.bsms-display` | Top bar / button (styled as `form-select`) |
| `.bsms-display-text` | Selected items text (ellipsis overflow) |
| `.bsms-dropdown` | Dropdown panel |
| `.bsms-search-wrap` | Search input wrapper |
| `.bsms-search` | Search `<input>` |
| `.bsms-select-all` | Select-all row |
| `.bsms-expand-all` | Expand/collapse all arrow |
| `.bsms-options` | Scrollable options container |
| `.bsms-group` | Group wrapper |
| `.bsms-group-header` | Group header (arrow + checkbox + label) |
| `.bsms-arrow` | Accordion arrow (`.collapsed` when closed) |
| `.bsms-group-children` | Children container (`.show` when expanded) |
| `.bsms-leaf` | Leaf option row (`.selected` when checked) |
| `.bsms-check` | All checkboxes |
| `.bsms-disabled` | Disabled option/group header |
| `.bsms-hidden` | Hidden by search filter |
| `.bsms-no-results` | "No results found" message |

## Project Structure

```
bootstrap-multiselect/
  index.html                      # Demo page with live examples & full docs
  package.json                    # Project metadata
  src/
    bootstrap-multiselect.js      # jQuery plugin (source)
    bootstrap-multiselect.css     # Styles (source)
  dist/
    bootstrap-multiselect.js      # Unminified copy for distribution
    bootstrap-multiselect.min.js  # Minified JS (~19 KB)
    bootstrap-multiselect.css     # Unminified copy for distribution
    bootstrap-multiselect.min.css # Minified CSS (~3 KB)
```

For production, use the minified versions from `dist/`:

```html
<link href="dist/bootstrap-multiselect.min.css" rel="stylesheet">
<script src="dist/bootstrap-multiselect.min.js"></script>
```

## License

MIT
