{"_id":"@elrayes/dual-listbox","_rev":"6-d000bfbaf732cdf08d37e3cf7a2ba199","name":"@elrayes/dual-listbox","dist-tags":{"latest":"1.0.0"},"versions":{"0.1.0":{"name":"@elrayes/dual-listbox","version":"0.1.0","license":"MIT","_id":"@elrayes/dual-listbox@0.1.0","maintainers":[{"name":"elrayes","email":"ahmedwaill63@gmail.com"}],"homepage":"https://github.com/Ahmed-Elrayes/dual-listbox#readme","bugs":{"url":"https://github.com/Ahmed-Elrayes/dual-listbox/issues"},"dist":{"shasum":"1ca1422c787cc412f19eeddf9fe1834ea4846c12","tarball":"https://registry.npmjs.org/@elrayes/dual-listbox/-/dual-listbox-0.1.0.tgz","fileCount":13,"integrity":"sha512-tgr1BkLtB9dyhC1NZj57yo6GzhCSWx4dc48Jc5ETmy0QtseKShkbVK/ZMhUDLtN7QgGh7MgQT2Cpsd/7rM3MjQ==","signatures":[{"sig":"MEUCIQD6ZTro8J5DmQgIGsirxmVSvAj+MpEHJZr1kd+ffasjcgIgWMk1Ev5FHnzVVe7p6b+pm92Dc7d4UNdcTXWOG/9FsT4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":128565},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js"},"./styles/*":"./styles/*","./themes/*":"./themes/*"},"gitHead":"629c603be8722044bdc21caf722c6490c07e5b86","scripts":{"dev":"tsup --watch","build":"tsup","clean":"rimraf dist"},"_npmUser":{"name":"elrayes","email":"ahmedwaill63@gmail.com"},"repository":{"url":"git+https://github.com/Ahmed-Elrayes/dual-listbox.git","type":"git"},"_npmVersion":"11.5.2","description":"A zero-dependency (vanilla JS) dual list box with grouping, search, select-all, theming (Bootstrap/Tailwind), and TypeScript types.","directories":{},"sideEffects":["styles/*","themes/*"],"_nodeVersion":"22.12.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","rimraf":"^5.0.5","typescript":"^5.5.4"},"_npmOperationalInternal":{"tmp":"tmp/dual-listbox_0.1.0_1756643593955_0.1125958652625525","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@elrayes/dual-listbox","version":"0.2.0","license":"MIT","_id":"@elrayes/dual-listbox@0.2.0","maintainers":[{"name":"elrayes","email":"ahmedwaill63@gmail.com"}],"homepage":"https://github.com/Ahmed-Elrayes/dual-listbox#readme","bugs":{"url":"https://github.com/Ahmed-Elrayes/dual-listbox/issues"},"dist":{"shasum":"3b8ae79cc1e9d7941993ff7a63e3ba37dd269fab","tarball":"https://registry.npmjs.org/@elrayes/dual-listbox/-/dual-listbox-0.2.0.tgz","fileCount":12,"integrity":"sha512-9CKc/9QZCnvk66npaH4sFW0m0Jsn7b8AEcHzNJg6IYfBF60v1rizlmRbXCF/Wgkp81W83Akbb3JNdaKR0mACBQ==","signatures":[{"sig":"MEUCIQDphLEbvL4AzbxyS7n8k8mDExU2vEB333FYb6YDyYkjswIgTmKrCpjzl4oC0z8vTzO4UNo3CssNakiADuDzErqHio8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@elrayes%2fdual-listbox@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":127713},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs"},"./styles/*":"./styles/*","./themes/*":"./themes/*"},"gitHead":"ec3ec8a82c14a59fd6e0ea77c1eb23a14a26437b","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts","clean":"rimraf dist","coverage":"vitest run --coverage","test:watch":"vitest"},"_npmUser":{"name":"elrayes","email":"ahmedwaill63@gmail.com"},"repository":{"url":"git+https://github.com/Ahmed-Elrayes/dual-listbox.git","type":"git"},"_npmVersion":"11.5.2","description":"A zero-dependency (vanilla JS) dual list box with grouping, search, select-all, theming (Bootstrap/Tailwind), and TypeScript types.","directories":{},"sideEffects":["styles/*","themes/*"],"_nodeVersion":"20.19.4","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","jsdom":"^24.1.0","rimraf":"^5.0.5","vitest":"^3.2.4","typescript":"^5.5.4","@vitest/coverage-v8":"^3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/dual-listbox_0.2.0_1756648241751_0.20471513597652202","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@elrayes/dual-listbox","version":"0.3.0","license":"MIT","_id":"@elrayes/dual-listbox@0.3.0","maintainers":[{"name":"elrayes","email":"ahmedwaill63@gmail.com"}],"homepage":"https://github.com/Ahmed-Elrayes/dual-listbox#readme","bugs":{"url":"https://github.com/Ahmed-Elrayes/dual-listbox/issues"},"dist":{"shasum":"149df1cd39361b555267bf6c5351c210f8d65ae1","tarball":"https://registry.npmjs.org/@elrayes/dual-listbox/-/dual-listbox-0.3.0.tgz","fileCount":12,"integrity":"sha512-1Md9SVkI9yYoWw2tuWE7tXJCfnAEpdvXoh2artgKJLxViq6Az8ViESfYVtGQ2MzScEeBjE2IGf2/F5ONH/AHzw==","signatures":[{"sig":"MEUCIEzeoVdAw1SmBc4ZRc+KMp0jwAMWmXYabUOq76lwzC/kAiEArPsBrZl7lwN/iGcCnEpygQtQPBC1LnB//ZgaGL909P0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@elrayes%2fdual-listbox@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":127858},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs"},"./styles/*":"./styles/*","./themes/*":"./themes/*"},"gitHead":"09b6389583f63187b4bfb06a5f0e7b7df499c16c","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts","clean":"rimraf dist","coverage":"vitest run --coverage","test:watch":"vitest"},"_npmUser":{"name":"elrayes","email":"ahmedwaill63@gmail.com"},"repository":{"url":"git+https://github.com/Ahmed-Elrayes/dual-listbox.git","type":"git"},"_npmVersion":"11.5.2","description":"A zero-dependency (vanilla JS) dual list box with grouping, search, select-all, theming (Bootstrap/Tailwind), and TypeScript types.","directories":{},"sideEffects":["styles/*","themes/*"],"_nodeVersion":"20.19.4","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","jsdom":"^24.1.0","rimraf":"^5.0.5","vitest":"^3.2.4","typescript":"^5.5.4","@vitest/coverage-v8":"^3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/dual-listbox_0.3.0_1756649663287_0.7127012599588911","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@elrayes/dual-listbox","version":"0.3.1","license":"MIT","_id":"@elrayes/dual-listbox@0.3.1","maintainers":[{"name":"elrayes","email":"ahmedwaill63@gmail.com"}],"homepage":"https://github.com/Ahmed-Elrayes/dual-listbox#readme","bugs":{"url":"https://github.com/Ahmed-Elrayes/dual-listbox/issues"},"dist":{"shasum":"1ef7ecf5c477543b080786882724bb04c7b18982","tarball":"https://registry.npmjs.org/@elrayes/dual-listbox/-/dual-listbox-0.3.1.tgz","fileCount":11,"integrity":"sha512-k5acqx6Hgg+MAPP/pFNTVQuMRer/tNS0L4nS31d1IyztuTk9JckwtaAQgE/drKlXGJWqO75V/u3MSKsKj2XmbA==","signatures":[{"sig":"MEUCIGNBGmtU9oEkhUFvDq46aFJFI6fO6yzwocdrkVrlDXn6AiEA35RXCUe5zZ2jB+yosCa2r5BbfoKUjTbG9gkylJO5P1I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@elrayes%2fdual-listbox@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":125359},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./styles/*":"./styles/*","./themes/*":"./themes/*"},"gitHead":"92dbed71c0769f0ca76170a8c7867c0e8f99175c","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rimraf dist","prepare":"npm run build","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"elrayes","email":"ahmedwaill63@gmail.com"},"repository":{"url":"git+https://github.com/Ahmed-Elrayes/dual-listbox.git","type":"git"},"_npmVersion":"11.6.0","description":"A zero-dependency (vanilla JS) dual list box with grouping, search, select-all, theming (Bootstrap/Tailwind), and TypeScript types.","directories":{},"sideEffects":["styles/*","themes/*"],"_nodeVersion":"20.19.5","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","jsdom":"^24.1.0","rimraf":"^5.0.5","vitest":"^3.2.4","typescript":"^5.5.4","@vitest/coverage-v8":"^3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/dual-listbox_0.3.1_1758537690369_0.2882614918218587","host":"s3://npm-registry-packages-npm-production"}},"0.3.3":{"name":"@elrayes/dual-listbox","version":"0.3.3","license":"MIT","_id":"@elrayes/dual-listbox@0.3.3","maintainers":[{"name":"elrayes","email":"ahmedwaill63@gmail.com"}],"homepage":"https://github.com/Ahmed-Elrayes/dual-listbox#readme","bugs":{"url":"https://github.com/Ahmed-Elrayes/dual-listbox/issues"},"dist":{"shasum":"1cbbb80104b7691dcc679c0001308d2da6c1e913","tarball":"https://registry.npmjs.org/@elrayes/dual-listbox/-/dual-listbox-0.3.3.tgz","fileCount":11,"integrity":"sha512-F18DoPAz8Ptnp9vN9iQNq2nJXM3hQAfQ6TeZhQdUDT+wBRExMNlnMMGdfg838XCC8gCJgS3zzS18xl2OfEsdLQ==","signatures":[{"sig":"MEMCIBuhwQodbnoMHQjKmPv0pcOXKqWMGKT5Um2eFpmfOyWKAh8OIvx4XhSdR4qSLqvRs3U4kxdYVo6GVWSUCQOJw++1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@elrayes%2fdual-listbox@0.3.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":126684},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./styles/*":"./styles/*","./themes/*":"./themes/*"},"gitHead":"75aa609bd395b3ab2a589484723e0da7c0c6c597","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rimraf dist","prepare":"npm run build","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"elrayes","email":"ahmedwaill63@gmail.com"},"repository":{"url":"git+https://github.com/Ahmed-Elrayes/dual-listbox.git","type":"git"},"_npmVersion":"10.8.2","description":"A zero-dependency (vanilla JS) dual list box with grouping, search, select-all, theming (Bootstrap/Tailwind), and TypeScript types.","directories":{},"sideEffects":["styles/*","themes/*"],"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","jsdom":"^24.1.0","rimraf":"^5.0.5","vitest":"^3.2.4","typescript":"^5.5.4","@vitest/coverage-v8":"^3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/dual-listbox_0.3.3_1770238432330_0.7399354212488369","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@elrayes/dual-listbox","version":"1.0.0","publishConfig":{"access":"public"},"type":"module","description":"A zero-dependency (vanilla JS) dual list box with grouping, search, select-all, theming (Bootstrap/Tailwind), and TypeScript types.","main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./styles/*":"./styles/*","./themes/*":"./themes/*"},"sideEffects":["styles/*","themes/*"],"scripts":{"build":"tsup","clean":"rimraf dist","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","prepare":"npm run build"},"devDependencies":{"@vitest/coverage-v8":"^5.0.1","jsdom":"^30.1.0","rimraf":"^6.1.3","tsup":"^8.5.1","typescript":"^5.9.3","vitest":"^5.0.1"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/Ahmed-Elrayes/dual-listbox.git"},"gitHead":"eea6e0b3673a2dcef3bc918443132a6404e5e17e","_id":"@elrayes/dual-listbox@1.0.0","bugs":{"url":"https://github.com/Ahmed-Elrayes/dual-listbox/issues"},"homepage":"https://github.com/Ahmed-Elrayes/dual-listbox#readme","_nodeVersion":"22.23.2","_npmVersion":"12.0.2","dist":{"integrity":"sha512-8g12mkUei+bcLwekX8VQhmdniyWo0cMjUCDQWrGgiHjB7y2TSVoMfq3+W0tS949XUbd2J9c111ShYt36j6q7ag==","shasum":"0ddafdc7e799c2f20c7d32f4a42715a1672ce2fc","tarball":"https://registry.npmjs.org/@elrayes/dual-listbox/-/dual-listbox-1.0.0.tgz","fileCount":11,"unpackedSize":160053,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@elrayes%2fdual-listbox@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICccXe+QtfQRaCjfq4eii9omKiKgND1v4hBJqLJZCaQ6AiB32bpgCSG2Q0nF28mzRjHhtFcwLcdf296+Y2BWvT4/pQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a32dd4e9-52d7-4797-9db8-fdfef5be4ab2"}},"directories":{},"maintainers":[{"name":"elrayes","email":"ahmedwaill63@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dual-listbox_1.0.0_1789629386149_0.464798451834898"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-31T12:33:13.889Z","modified":"2026-09-17T07:16:26.696Z","0.1.0":"2025-08-31T12:33:14.152Z","0.2.0":"2025-08-31T13:50:41.937Z","0.3.0":"2025-08-31T14:14:23.501Z","0.3.1":"2025-09-22T10:41:30.554Z","0.3.3":"2026-02-04T20:53:52.532Z","1.0.0":"2026-09-17T07:16:26.288Z"},"bugs":{"url":"https://github.com/Ahmed-Elrayes/dual-listbox/issues"},"license":"MIT","homepage":"https://github.com/Ahmed-Elrayes/dual-listbox#readme","repository":{"type":"git","url":"git+https://github.com/Ahmed-Elrayes/dual-listbox.git"},"description":"A zero-dependency (vanilla JS) dual list box with grouping, search, select-all, theming (Bootstrap/Tailwind), and TypeScript types.","maintainers":[{"name":"elrayes","email":"ahmedwaill63@gmail.com"}],"readme":"# @elrayes/dual-listbox\n\nA zero-dependency (vanilla JS) dual-list box with grouping, search, select-all (with indeterminate state), and theming (Bootstrap 5.2 default, Tailwind optional), with TypeScript types.\n\n## Installation\n\n```bash\nnpm install @elrayes/dual-listbox\n```\n\n## Usage (ES Modules)\n\n```ts\nimport { DualListBox, useTheme, tailwindTheme } from '@elrayes/dual-listbox';\nimport '@elrayes/dual-listbox/styles/core.css';\nimport '@elrayes/dual-listbox/themes/bootstrap.css'; // or themes/tailwind.css\n\nuseTheme(tailwindTheme); // sets the global default theme for all new instances\n// DualListBox.setTheme(tailwindTheme); // back-compat alias for useTheme()\n\nconst items = [\n  { item: 'Apple', value: 1, group: 'Fruits' },\n  { item: 'Tomato', value: 2, group: 'Vegetables' },\n];\n\nconst dlb = new DualListBox('#dual-listbox-container', {\n  dataArray: items,\n  selectedItems: [],\n  onSubmit: (selected, unselected, all, selectedArray) => {\n    console.log(selectedArray);\n  },\n});\n\nconst selectedItems = dlb.getSelectedItems();\nconst unselectedItems = dlb.getUnselectedItems();\nconst allItems = dlb.getAllItems();\n```\n\n## Usage (CommonJS)\n\n```js\nconst { DualListBox } = require('@elrayes/dual-listbox');\nrequire('@elrayes/dual-listbox/styles/core.css');\nrequire('@elrayes/dual-listbox/themes/bootstrap.css');\n\nconst dlb = new DualListBox('#dual-listbox-container', { /* options */ });\n```\n\n## Options (TypeScript)\n\n```ts\nexport interface DualListBoxOptions {\n  itemName?: string;              // property holding an item's label, default \"item\"\n  groupName?: string;             // property holding an item's group, default \"group\"\n  valueName?: string;             // property holding an item's unique value, default \"value\"\n  inputName?: string;             // hidden-input name for form submission, default \"selectedItems\"\n  tabNameText?: string;           // left pane header, default \"Available Items\"\n  rightTabNameText?: string;      // right pane header, default \"Selected Items\"\n  searchPlaceholderText?: string; // default \"Search...\"\n  includeButtonText?: string;     // default \"Include >>\"\n  excludeButtonText?: string;     // default \"<< Exclude\"\n  dataArray?: any[];              // all available items\n  selectedItems?: any[];          // items preselected into the right pane (subset of dataArray)\n  hideEmptyGroups?: boolean;      // default false\n  submitForm?: boolean;           // append hidden inputs on submit, default true (ignored if onSubmit is set)\n  onSubmit?: (selected, unselected, allItems, selectedArray) => void | null;\n  theme?: Partial<DualListBoxTheme>;\n}\n```\n\n`dataArray` items can carry any extra properties beyond `item`/`group`/`value` (e.g. an `id`, `description`, or `metadata` field) — they pass through untouched and are returned as-is from `getSelectedItems()`/`getUnselectedItems()`/`getAllItems()`.\n\n## Select-all and indeterminate state\n\nBoth the per-group checkbox and each pane's \"Select All\" checkbox reflect the\nreal state of their descendants, not just checked/unchecked:\n\n- **Unchecked** — none of the covered items are selected.\n- **Checked** — all of the covered items are selected.\n- **Indeterminate** (the native `input.indeterminate` DOM property, rendered\n  as a dash `▬` by the browser) — *some but not all* covered items are\n  selected.\n\nThis updates live as items are checked, moved between panes, or filtered by\nsearch — no configuration needed.\n\n## Theming\n\n- Built-in theme maps every part of the widget (container, card, buttons, checkboxes, …) to a CSS class string.\n- Presets: Bootstrap 5.2 (`defaultTheme`, aliased as `bootstrapTheme`) and Tailwind (`tailwindTheme`, with `dark:` variants).\n- Theme resolution order per instance: `options.theme` (partial overrides) > global theme set via `useTheme()` > `defaultTheme`.\n\n### Global (shared) theme state\n\n`useTheme()` sets **module-level shared state** — every `DualListBox` constructed *after* the call picks it up automatically, without passing `theme` to each instance:\n\n```ts\nimport { DualListBox, useTheme } from '@elrayes/dual-listbox';\nimport { tailwindTheme } from '@elrayes/dual-listbox';\n\nuseTheme(tailwindTheme); // shared across every instance created from here on\n\nnew DualListBox('#el-1'); // uses the Tailwind theme\nnew DualListBox('#el-2'); // also uses the Tailwind theme\n```\n\nThings worth knowing about this shared state:\n\n- **It only affects instances created after the call.** Instances already rendered keep the theme they were built with — `useTheme()` does not retroactively re-theme them.\n- **It's per module instance, not a page-wide global.** If your bundler ever ships two separate copies of this package (e.g. two independently-chunked entry points that don't share a module graph), each copy has its own `GLOBAL_THEME` — a `useTheme()` call in one is invisible to the other. This is rare in a typical single-bundle app/Vite build.\n- **Per-instance `theme` always wins**, regardless of what `useTheme()` set:\n\n```ts\nnew DualListBox('#el', {\n  theme: {\n    btn: 'my-btn my-btn--primary', // overrides just this class, for this instance\n  },\n});\n```\n\n- `DualListBox.setTheme(theme)` is kept as a back-compat alias for `useTheme(theme)`.\n\nAlternatively, skip the theme system entirely and target `.dual-listbox` with your own stylesheet.\n\n## Multiple instances on one page\n\nEvery instance gets its own random `instanceId`, and every element `id`/`for`\npair it renders (container, search inputs, group checkboxes, item checkboxes)\nis namespaced with that ID. You can safely mount several `DualListBox`\ninstances on the same page — including with identical `dataArray` — without\n`id` collisions breaking label-to-checkbox association.\n\n## Security\n\nItem labels, group names, and values are HTML-escaped before being inserted\ninto the DOM. `dataArray`/`selectedItems` can safely contain user-generated\ncontent (e.g. names loaded from a database) without risking markup/script\ninjection into the page.\n\n## Laravel + Vite Integration\n\n1. Place the container in your Blade:\n\n```html\n<div id=\"dual-listbox-container\"></div>\n```\n\n2. Import and initialize in your app JS that Vite builds.\n3. Include `core.css` and choose a theme stylesheet.\n\n## API\n\n- `new DualListBox(element, options)`\n- `getSelectedValues(): Promise<(string|number)[]>` (legacy values API)\n- Getters: `selected`, `unselected`, `allItems`, `selectedArray`\n- Flat, de-duplicated item arrays:\n  - `getSelectedItems(): DualListBoxItem[]`\n  - `getUnselectedItems(): DualListBoxItem[]`\n  - `getAllItems(): DualListBoxItem[]`\n- `getSettings()` — the fully-resolved settings (defaults + options + merged theme) for this instance.\n- `useTheme(theme)` / `DualListBox.setTheme(theme)` — set the shared global theme (see [Global (shared) theme state](#global-shared-theme-state)).\n\n## Testing\n\n```bash\nnpm test        # run once\nnpm run test:watch\nnpm run coverage\n```\n\nTests cover: button enable/disable, form submission (`onSubmit` and hidden-input fallback), `hideEmptyGroups`, search filtering, global theming, select-all/group-checkbox indeterminate state, multi-instance ID isolation, and HTML escaping of user-supplied data.\n\n## License\nMIT\n","readmeFilename":"README.md"}