forge-select 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +227 -0
- package/dist/index.cjs +753 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +132 -0
- package/dist/index.d.ts +132 -0
- package/dist/index.global.js +751 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +726 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
- package/styles/forge-select.css +253 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/emitter.ts","../src/i18n.ts","../src/ForgeSelect.ts"],"sourcesContent":["export type Handler = (...args: unknown[]) => void;\n\nexport class Emitter {\n private handlers = new Map<string, Set<Handler>>();\n\n on(event: string, handler: Handler): void {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n set.add(handler);\n }\n\n off(event: string, handler: Handler): void {\n this.handlers.get(event)?.delete(handler);\n }\n\n emit(event: string, ...args: unknown[]): void {\n const set = this.handlers.get(event);\n if (!set) return;\n for (const handler of [...set]) handler(...args);\n }\n\n clear(): void {\n this.handlers.clear();\n }\n}\n","export type Strings = Record<string, string>;\n\nconst locales: Record<string, Strings> = {\n en: {\n noResults: \"No results found\",\n loading: \"Loading…\",\n createOption: 'Create \"{query}\"',\n clearSelection: \"Clear selection\",\n removeItem: \"Remove {label}\",\n search: \"Search\",\n },\n vi: {\n noResults: \"Không tìm thấy kết quả\",\n loading: \"Đang tải…\",\n createOption: 'Tạo \"{query}\"',\n clearSelection: \"Xóa lựa chọn\",\n removeItem: \"Xóa {label}\",\n search: \"Tìm kiếm\",\n },\n};\n\nexport function getStrings(language: string | Strings): Strings {\n if (typeof language === \"string\") {\n return locales[language] ?? locales.en;\n }\n return { ...locales.en, ...language };\n}\n\nexport function format(template: string, vars: Record<string, string>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => vars[key] ?? match);\n}\n","import { Emitter, type Handler } from \"./emitter\";\nimport { format, getStrings, type Strings } from \"./i18n\";\nimport type {\n AjaxConfig,\n DataItem,\n ForgeSelectEvent,\n ForgeSelectOptions,\n ForgeSelectPlugin,\n ForgeSelectValue,\n Option,\n OptionGroup,\n TemplateFn,\n} from \"./types\";\n\ntype Row =\n | { kind: \"group\"; label: string }\n | { kind: \"option\"; option: Option; navIndex: number }\n | { kind: \"create\"; navIndex: number }\n | { kind: \"empty\" }\n | { kind: \"loading\" };\n\ntype NavItem = { kind: \"option\"; option: Option } | { kind: \"create\" };\n\ninterface ResolvedOptions {\n placeholder: string;\n searchable: boolean;\n multiple: boolean;\n clearable: boolean;\n allowCreate: boolean;\n theme: string;\n disabled: boolean;\n data?: DataItem[];\n ajax?: AjaxConfig;\n templateResult?: TemplateFn;\n templateSelection?: TemplateFn;\n virtualScroll: boolean | undefined;\n itemHeight: number;\n language: string | Record<string, string>;\n plugins: ForgeSelectPlugin[];\n}\n\nconst DEFAULT_ITEM_HEIGHT = 36;\nconst VIRTUAL_BUFFER = 5;\nconst VIRTUAL_THRESHOLD = 100;\nconst ROW_CACHE_LIMIT = 2000;\n\nlet uidCounter = 0;\n\nfunction isGroup(item: DataItem): item is OptionGroup {\n return (item as OptionGroup).options !== undefined;\n}\n\nexport default class ForgeSelect {\n /** The original element ForgeSelect was mounted on. */\n readonly el: HTMLElement;\n\n private opts: ResolvedOptions;\n private strings: Strings;\n private data: DataItem[];\n private selected: string[] = [];\n private selectedOptions = new Map<string, Option>();\n private emitter = new Emitter();\n private plugins: ForgeSelectPlugin[];\n\n private uid = `forge-select-${++uidCounter}`;\n private root!: HTMLDivElement;\n private control!: HTMLDivElement;\n private valueEl!: HTMLDivElement;\n private clearBtn!: HTMLButtonElement;\n private dropdown!: HTMLDivElement;\n private searchInput: HTMLInputElement | null = null;\n private list!: HTMLUListElement;\n\n private isOpen = false;\n private isDisabled = false;\n private destroyed = false;\n private query = \"\";\n private rows: Row[] = [];\n private navItems: NavItem[] = [];\n private highlightedIndex = -1;\n private rowContentCache = new Map<string, Node>();\n\n private loading = false;\n private ajaxTimer: ReturnType<typeof setTimeout> | null = null;\n private ajaxRequestId = 0;\n private remoteLoaded = false;\n\n private onDocumentMouseDown = (event: MouseEvent): void => {\n if (!this.root.contains(event.target as Node)) this.close();\n };\n\n constructor(target: string | HTMLElement, options: ForgeSelectOptions = {}) {\n const el = typeof target === \"string\" ? document.querySelector<HTMLElement>(target) : target;\n if (!el) {\n throw new Error(`ForgeSelect: target element not found: ${String(target)}`);\n }\n this.el = el;\n\n const nativeSelect = el instanceof HTMLSelectElement ? el : null;\n this.opts = {\n placeholder: options.placeholder ?? \"\",\n searchable: options.searchable ?? true,\n multiple: options.multiple ?? nativeSelect?.multiple ?? false,\n clearable: options.clearable ?? false,\n allowCreate: options.allowCreate ?? false,\n theme: options.theme ?? \"default\",\n disabled: options.disabled ?? false,\n data: options.data,\n ajax: options.ajax,\n templateResult: options.templateResult,\n templateSelection: options.templateSelection,\n virtualScroll: options.virtualScroll,\n itemHeight: options.itemHeight ?? DEFAULT_ITEM_HEIGHT,\n language: options.language ?? \"en\",\n plugins: options.plugins ?? [],\n };\n this.strings = getStrings(this.opts.language);\n this.plugins = this.opts.plugins;\n\n this.data = this.opts.data ?? (nativeSelect ? parseNativeOptions(nativeSelect) : []);\n if (nativeSelect && !this.opts.data) {\n for (const option of Array.from(nativeSelect.querySelectorAll(\"option\"))) {\n if (option.hasAttribute(\"selected\")) this.selectValue(option.value, false);\n }\n }\n\n this.buildDom();\n this.renderValue();\n if (this.opts.disabled) this.disable();\n\n for (const plugin of this.plugins) plugin.onInit?.(this);\n }\n\n // ---------------------------------------------------------------- public API\n\n open(): void {\n if (this.isOpen || this.isDisabled || this.destroyed) return;\n this.isOpen = true;\n this.dropdown.hidden = false;\n this.root.classList.add(\"forge-select--open\");\n this.control.setAttribute(\"aria-expanded\", \"true\");\n document.addEventListener(\"mousedown\", this.onDocumentMouseDown);\n\n if (this.opts.ajax && !this.remoteLoaded) {\n this.scheduleRemoteLoad(this.query, 0);\n }\n this.renderList();\n if (this.searchInput) this.searchInput.focus();\n\n this.emitter.emit(\"open\");\n for (const plugin of this.plugins) plugin.onOpen?.(this);\n }\n\n close(): void {\n if (!this.isOpen) return;\n this.isOpen = false;\n this.dropdown.hidden = true;\n this.root.classList.remove(\"forge-select--open\");\n this.control.setAttribute(\"aria-expanded\", \"false\");\n document.removeEventListener(\"mousedown\", this.onDocumentMouseDown);\n this.highlightedIndex = -1;\n if (this.searchInput) {\n this.searchInput.value = \"\";\n this.query = \"\";\n }\n\n this.emitter.emit(\"close\");\n for (const plugin of this.plugins) plugin.onClose?.(this);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.close();\n for (const plugin of this.plugins) plugin.onDestroy?.(this);\n this.destroyed = true;\n if (this.ajaxTimer) clearTimeout(this.ajaxTimer);\n this.rowContentCache.clear();\n this.root.remove();\n this.el.style.display = \"\";\n this.emitter.clear();\n }\n\n getValue(): ForgeSelectValue {\n if (this.opts.multiple) return [...this.selected];\n return this.selected[0] ?? null;\n }\n\n setValue(value: ForgeSelectValue): void {\n const values = value == null ? [] : Array.isArray(value) ? value : [value];\n const next = this.opts.multiple ? values : values.slice(0, 1);\n if (arraysEqual(next, this.selected)) return;\n this.selected = [];\n for (const v of next) this.selectValue(v, false);\n this.afterSelectionChange();\n }\n\n enable(): void {\n this.isDisabled = false;\n this.root.classList.remove(\"forge-select--disabled\");\n this.control.tabIndex = 0;\n this.control.setAttribute(\"aria-disabled\", \"false\");\n }\n\n disable(): void {\n this.close();\n this.isDisabled = true;\n this.root.classList.add(\"forge-select--disabled\");\n this.control.tabIndex = -1;\n this.control.setAttribute(\"aria-disabled\", \"true\");\n }\n\n on(event: ForgeSelectEvent, handler: Handler): void {\n this.emitter.on(event, handler);\n }\n\n off(event: ForgeSelectEvent, handler: Handler): void {\n this.emitter.off(event, handler);\n }\n\n // ---------------------------------------------------------------- DOM setup\n\n private buildDom(): void {\n this.root = document.createElement(\"div\");\n this.root.className = \"forge-select\";\n this.root.dataset.theme = this.opts.theme;\n this.root.style.setProperty(\"--fs-item-height\", `${this.opts.itemHeight}px`);\n\n this.control = document.createElement(\"div\");\n this.control.className = \"forge-select__control\";\n this.control.setAttribute(\"role\", \"combobox\");\n this.control.setAttribute(\"aria-haspopup\", \"listbox\");\n this.control.setAttribute(\"aria-expanded\", \"false\");\n this.control.setAttribute(\"aria-controls\", `${this.uid}-list`);\n this.control.tabIndex = 0;\n\n this.valueEl = document.createElement(\"div\");\n this.valueEl.className = \"forge-select__value\";\n\n this.clearBtn = document.createElement(\"button\");\n this.clearBtn.type = \"button\";\n this.clearBtn.className = \"forge-select__clear\";\n this.clearBtn.setAttribute(\"aria-label\", this.strings.clearSelection);\n this.clearBtn.textContent = \"×\";\n this.clearBtn.hidden = true;\n\n const arrow = document.createElement(\"span\");\n arrow.className = \"forge-select__arrow\";\n arrow.setAttribute(\"aria-hidden\", \"true\");\n\n this.control.append(this.valueEl, this.clearBtn, arrow);\n\n this.dropdown = document.createElement(\"div\");\n this.dropdown.className = \"forge-select__dropdown\";\n this.dropdown.hidden = true;\n\n if (this.opts.searchable) {\n this.searchInput = document.createElement(\"input\");\n this.searchInput.type = \"search\";\n this.searchInput.className = \"forge-select__search\";\n this.searchInput.setAttribute(\"aria-label\", this.strings.search);\n this.searchInput.setAttribute(\"aria-autocomplete\", \"list\");\n this.searchInput.setAttribute(\"aria-controls\", `${this.uid}-list`);\n this.dropdown.append(this.searchInput);\n }\n\n this.list = document.createElement(\"ul\");\n this.list.className = \"forge-select__list\";\n this.list.id = `${this.uid}-list`;\n this.list.setAttribute(\"role\", \"listbox\");\n if (this.opts.multiple) this.list.setAttribute(\"aria-multiselectable\", \"true\");\n this.dropdown.append(this.list);\n\n this.root.append(this.control, this.dropdown);\n this.el.style.display = \"none\";\n this.el.insertAdjacentElement(\"afterend\", this.root);\n\n this.bindEvents();\n }\n\n private bindEvents(): void {\n this.control.addEventListener(\"click\", (event) => {\n if (event.target === this.clearBtn) return;\n if (this.isDisabled) return;\n this.isOpen ? this.close() : this.open();\n });\n\n this.control.addEventListener(\"keydown\", (event) => this.handleKeydown(event));\n\n this.clearBtn.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n this.clearSelection();\n });\n\n if (this.searchInput) {\n this.searchInput.addEventListener(\"input\", () => {\n this.query = this.searchInput!.value;\n this.highlightedIndex = -1;\n this.list.scrollTop = 0;\n this.emitter.emit(\"search\", this.query);\n if (this.opts.ajax) {\n this.scheduleRemoteLoad(this.query, this.opts.ajax.debounce ?? 250);\n } else {\n this.renderList();\n }\n });\n this.searchInput.addEventListener(\"keydown\", (event) => this.handleKeydown(event));\n }\n\n this.list.addEventListener(\"click\", (event) => {\n const li = (event.target as HTMLElement).closest<HTMLLIElement>(\"li[data-nav-index]\");\n if (!li) return;\n const navIndex = Number(li.dataset.navIndex);\n this.activateNavItem(navIndex);\n });\n\n this.list.addEventListener(\"scroll\", () => {\n if (this.usesVirtualScroll()) this.renderRows();\n });\n }\n\n private handleKeydown(event: KeyboardEvent): void {\n if (this.isDisabled) return;\n switch (event.key) {\n case \"Enter\":\n event.preventDefault();\n if (!this.isOpen) this.open();\n else if (this.highlightedIndex >= 0) this.activateNavItem(this.highlightedIndex);\n break;\n case \" \":\n if (event.target === this.control) {\n event.preventDefault();\n if (!this.isOpen) this.open();\n }\n break;\n case \"ArrowDown\":\n event.preventDefault();\n if (!this.isOpen) this.open();\n else this.moveHighlight(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (this.isOpen) this.moveHighlight(-1);\n break;\n case \"Escape\":\n if (this.isOpen) {\n event.preventDefault();\n this.close();\n this.control.focus();\n }\n break;\n case \"Tab\":\n this.close();\n break;\n }\n }\n\n // ---------------------------------------------------------------- selection\n\n private selectValue(value: string, notify: boolean): void {\n if (this.selected.includes(value)) return;\n const option = this.findOption(value) ?? this.selectedOptions.get(value) ?? { value, label: value };\n this.selectedOptions.set(value, option);\n if (this.opts.multiple) this.selected.push(value);\n else this.selected = [value];\n if (notify) this.afterSelectionChange();\n }\n\n private deselectValue(value: string, notify: boolean): void {\n const index = this.selected.indexOf(value);\n if (index === -1) return;\n this.selected.splice(index, 1);\n if (notify) this.afterSelectionChange();\n }\n\n private clearSelection(): void {\n if (this.selected.length === 0) return;\n this.selected = [];\n this.emitter.emit(\"clear\");\n this.afterSelectionChange();\n }\n\n private afterSelectionChange(): void {\n this.renderValue();\n this.syncNativeSelect();\n if (this.isOpen) this.renderList();\n this.emitter.emit(\"change\", this.getValue());\n }\n\n private syncNativeSelect(): void {\n if (!(this.el instanceof HTMLSelectElement)) return;\n const existing = new Set<string>();\n for (const option of Array.from(this.el.options)) {\n existing.add(option.value);\n option.selected = this.selected.includes(option.value);\n }\n for (const value of this.selected) {\n if (existing.has(value)) continue;\n const option = document.createElement(\"option\");\n option.value = value;\n option.textContent = this.selectedOptions.get(value)?.label ?? value;\n option.selected = true;\n this.el.append(option);\n }\n this.el.dispatchEvent(new Event(\"change\", { bubbles: true }));\n }\n\n private findOption(value: string): Option | undefined {\n for (const item of this.data) {\n if (isGroup(item)) {\n const found = item.options.find((o) => o.value === value);\n if (found) return found;\n } else if (item.value === value) {\n return item;\n }\n }\n return undefined;\n }\n\n private createFromQuery(): void {\n const label = this.query.trim();\n if (!label) return;\n const option: Option = { value: label, label };\n this.data.push(option);\n if (this.searchInput) {\n this.searchInput.value = \"\";\n this.query = \"\";\n }\n this.selectValue(option.value, true);\n if (!this.opts.multiple) this.close();\n }\n\n private activateNavItem(navIndex: number): void {\n const item = this.navItems[navIndex];\n if (!item) return;\n if (item.kind === \"create\") {\n this.createFromQuery();\n return;\n }\n const { value } = item.option;\n if (this.opts.multiple) {\n this.selected.includes(value) ? this.deselectValue(value, true) : this.selectValue(value, true);\n } else {\n this.selectValue(value, true);\n this.close();\n this.control.focus();\n }\n }\n\n // ---------------------------------------------------------------- rendering\n\n private renderValue(): void {\n this.valueEl.textContent = \"\";\n const hasValue = this.selected.length > 0;\n this.clearBtn.hidden = !(this.opts.clearable && hasValue);\n\n if (!hasValue) {\n const placeholder = document.createElement(\"span\");\n placeholder.className = \"forge-select__placeholder\";\n placeholder.textContent = this.opts.placeholder;\n this.valueEl.append(placeholder);\n return;\n }\n\n if (this.opts.multiple) {\n for (const value of this.selected) {\n const option = this.selectedOptions.get(value) ?? { value, label: value };\n const tag = document.createElement(\"span\");\n tag.className = \"forge-select__tag\";\n const label = document.createElement(\"span\");\n label.className = \"forge-select__tag-label\";\n this.renderTemplate(label, option, this.opts.templateSelection, \"inline\");\n const remove = document.createElement(\"button\");\n remove.type = \"button\";\n remove.className = \"forge-select__tag-remove\";\n remove.setAttribute(\"aria-label\", format(this.strings.removeItem, { label: option.label }));\n remove.textContent = \"×\";\n remove.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n if (!this.isDisabled) this.deselectValue(value, true);\n });\n tag.append(label, remove);\n this.valueEl.append(tag);\n }\n } else {\n const option = this.selectedOptions.get(this.selected[0]) ?? {\n value: this.selected[0],\n label: this.selected[0],\n };\n const span = document.createElement(\"span\");\n span.className = \"forge-select__single-value\";\n this.renderTemplate(span, option, this.opts.templateSelection, \"inline\");\n this.valueEl.append(span);\n }\n }\n\n private renderTemplate(\n container: HTMLElement,\n option: Option,\n template?: TemplateFn,\n variant: \"row\" | \"inline\" = \"row\",\n ): void {\n if (template) {\n const result = template(option);\n if (typeof result === \"string\") container.innerHTML = result;\n else container.append(result);\n return;\n }\n if (!option.avatar && !option.description) {\n container.textContent = option.label;\n return;\n }\n // Built-in rich renderer: DOM built via textContent, so all fields are XSS-safe.\n if (option.avatar) {\n const avatar = document.createElement(\"img\");\n avatar.className =\n variant === \"row\" ? \"forge-select__option-avatar\" : \"forge-select__inline-avatar\";\n avatar.src = option.avatar;\n avatar.alt = \"\";\n avatar.setAttribute(\"loading\", \"lazy\");\n avatar.setAttribute(\"decoding\", \"async\");\n container.append(avatar);\n }\n if (variant === \"row\" && option.description) {\n const body = document.createElement(\"span\");\n body.className = \"forge-select__option-body\";\n const label = document.createElement(\"span\");\n label.className = \"forge-select__option-label\";\n label.textContent = option.label;\n const desc = document.createElement(\"span\");\n desc.className = \"forge-select__option-desc\";\n desc.textContent = option.description;\n body.append(label, desc);\n container.append(body);\n } else {\n const label = document.createElement(\"span\");\n label.className = \"forge-select__option-label\";\n label.textContent = option.label;\n container.append(label);\n }\n }\n\n private buildRows(): void {\n this.rows = [];\n this.navItems = [];\n const query = this.query.trim().toLowerCase();\n const matches = (option: Option): boolean =>\n query === \"\" ||\n option.label.toLowerCase().includes(query) ||\n (option.description?.toLowerCase().includes(query) ?? false);\n\n const pushOption = (option: Option): void => {\n let navIndex = -1;\n if (!option.disabled) {\n navIndex = this.navItems.length;\n this.navItems.push({ kind: \"option\", option });\n }\n this.rows.push({ kind: \"option\", option, navIndex });\n };\n\n if (this.loading) {\n this.rows.push({ kind: \"loading\" });\n return;\n }\n\n for (const item of this.data) {\n if (isGroup(item)) {\n const visible = item.options.filter(matches);\n if (visible.length === 0) continue;\n this.rows.push({ kind: \"group\", label: item.label });\n visible.forEach(pushOption);\n } else if (matches(item)) {\n pushOption(item);\n }\n }\n\n if (this.opts.allowCreate && query !== \"\" && !this.hasExactMatch(query)) {\n const navIndex = this.navItems.length;\n this.navItems.push({ kind: \"create\" });\n this.rows.push({ kind: \"create\", navIndex });\n }\n\n if (this.rows.length === 0) this.rows.push({ kind: \"empty\" });\n }\n\n private hasExactMatch(lowerQuery: string): boolean {\n for (const item of this.data) {\n const options = isGroup(item) ? item.options : [item];\n if (options.some((o) => o.label.toLowerCase() === lowerQuery)) return true;\n }\n return false;\n }\n\n private usesVirtualScroll(): boolean {\n return this.opts.virtualScroll !== false && this.rows.length > VIRTUAL_THRESHOLD;\n }\n\n private renderList(): void {\n this.buildRows();\n this.renderRows();\n }\n\n private renderRows(): void {\n // Capture the scroll offset and viewport height BEFORE clearing the list:\n // removing the children collapses both scrollHeight (clamping scrollTop to 0)\n // and clientHeight (the list has no explicit height, only max-height, so an\n // empty list reports just its padding instead of the real box height).\n const scrollTop = this.list.scrollTop;\n const clientHeight = this.list.clientHeight;\n const virtual = this.usesVirtualScroll();\n this.list.textContent = \"\";\n\n const rowHeight = this.opts.itemHeight;\n let start = 0;\n let end = this.rows.length;\n if (virtual) {\n const viewport = clientHeight || rowHeight * 8;\n start = Math.max(0, Math.floor(scrollTop / rowHeight) - VIRTUAL_BUFFER);\n end = Math.min(this.rows.length, start + Math.ceil(viewport / rowHeight) + VIRTUAL_BUFFER * 2);\n\n const topSpacer = document.createElement(\"li\");\n topSpacer.className = \"forge-select__spacer\";\n topSpacer.setAttribute(\"aria-hidden\", \"true\");\n topSpacer.style.height = `${start * rowHeight}px`;\n this.list.append(topSpacer);\n }\n\n for (let i = start; i < end; i++) {\n this.list.append(this.renderRow(this.rows[i]));\n }\n\n if (virtual) {\n const bottomSpacer = document.createElement(\"li\");\n bottomSpacer.className = \"forge-select__spacer\";\n bottomSpacer.setAttribute(\"aria-hidden\", \"true\");\n bottomSpacer.style.height = `${(this.rows.length - end) * rowHeight}px`;\n this.list.append(bottomSpacer);\n\n // Restore the offset that clearing clamped away. The net scroll change\n // within this handler is zero, so this does not re-fire the scroll\n // listener; even if it did, the re-render is idempotent and settles.\n if (this.list.scrollTop !== scrollTop) {\n this.list.scrollTop = scrollTop;\n }\n }\n\n this.updateActiveDescendant();\n }\n\n private renderRow(row: Row): HTMLLIElement {\n const li = document.createElement(\"li\");\n switch (row.kind) {\n case \"group\":\n li.className = \"forge-select__group-label\";\n li.setAttribute(\"role\", \"presentation\");\n li.textContent = row.label;\n break;\n case \"empty\":\n li.className = \"forge-select__empty\";\n li.textContent = this.strings.noResults;\n break;\n case \"loading\":\n li.className = \"forge-select__loading\";\n li.textContent = this.strings.loading;\n break;\n case \"create\":\n li.className = \"forge-select__option forge-select__option--create\";\n li.setAttribute(\"role\", \"option\");\n li.id = `${this.uid}-nav-${row.navIndex}`;\n li.dataset.navIndex = String(row.navIndex);\n li.textContent = format(this.strings.createOption, { query: this.query.trim() });\n if (row.navIndex === this.highlightedIndex) li.classList.add(\"forge-select__option--highlighted\");\n break;\n case \"option\": {\n li.className = \"forge-select__option\";\n li.setAttribute(\"role\", \"option\");\n const isSelected = this.selected.includes(row.option.value);\n li.setAttribute(\"aria-selected\", String(isSelected));\n if (isSelected) li.classList.add(\"forge-select__option--selected\");\n if (row.option.disabled) {\n li.classList.add(\"forge-select__option--disabled\");\n li.setAttribute(\"aria-disabled\", \"true\");\n } else {\n li.id = `${this.uid}-nav-${row.navIndex}`;\n li.dataset.navIndex = String(row.navIndex);\n if (row.navIndex === this.highlightedIndex) li.classList.add(\"forge-select__option--highlighted\");\n }\n li.append(this.optionContent(row.option));\n break;\n }\n }\n return li;\n }\n\n /**\n * Rendered row content is cached per option value and cloned on each render,\n * so templates run once per option instead of once per scroll frame.\n * State classes (selected/highlighted/disabled) live on the <li>, keeping the\n * cached content state-free.\n */\n private optionContent(option: Option): Node {\n let cached = this.rowContentCache.get(option.value);\n if (!cached) {\n const holder = document.createElement(\"span\");\n holder.className = \"forge-select__option-content\";\n this.renderTemplate(holder, option, this.opts.templateResult);\n if (this.rowContentCache.size >= ROW_CACHE_LIMIT) {\n // FIFO eviction keeps memory bounded on very large lists.\n const oldest = this.rowContentCache.keys().next().value as string;\n this.rowContentCache.delete(oldest);\n }\n this.rowContentCache.set(option.value, holder);\n cached = holder;\n }\n return cached.cloneNode(true);\n }\n\n private moveHighlight(delta: number): void {\n if (this.navItems.length === 0) return;\n const next =\n this.highlightedIndex === -1 && delta > 0\n ? 0\n : (this.highlightedIndex + delta + this.navItems.length) % this.navItems.length;\n this.highlightedIndex = next;\n\n if (this.usesVirtualScroll()) {\n const rowIndex = this.rows.findIndex(\n (row) => (row.kind === \"option\" || row.kind === \"create\") && row.navIndex === next,\n );\n if (rowIndex >= 0) {\n const rowHeight = this.opts.itemHeight;\n const top = rowIndex * rowHeight;\n const viewport = this.list.clientHeight || rowHeight * 8;\n let target = this.list.scrollTop;\n if (top < target) target = top;\n else if (top + rowHeight > target + viewport) target = top + rowHeight - viewport;\n if (target !== this.list.scrollTop) this.list.scrollTop = target;\n }\n this.renderRows();\n } else {\n this.renderRows();\n const highlighted = this.list.querySelector<HTMLElement>(\".forge-select__option--highlighted\");\n highlighted?.scrollIntoView?.({ block: \"nearest\" });\n }\n }\n\n private updateActiveDescendant(): void {\n const target = this.searchInput ?? this.control;\n if (this.highlightedIndex >= 0) {\n target.setAttribute(\"aria-activedescendant\", `${this.uid}-nav-${this.highlightedIndex}`);\n } else {\n target.removeAttribute(\"aria-activedescendant\");\n }\n }\n\n // ---------------------------------------------------------------- remote data\n\n private scheduleRemoteLoad(query: string, delay: number): void {\n if (this.ajaxTimer) clearTimeout(this.ajaxTimer);\n this.loading = true;\n this.renderList();\n this.ajaxTimer = setTimeout(() => {\n void this.loadRemote(query);\n }, delay);\n }\n\n private async loadRemote(query: string): Promise<void> {\n const ajax = this.opts.ajax!;\n const requestId = ++this.ajaxRequestId;\n try {\n const url = buildUrl(ajax, query);\n const response = await fetch(url);\n const json: unknown = await response.json();\n if (requestId !== this.ajaxRequestId || this.destroyed) return;\n this.data = ajax.transform ? ajax.transform(json) : (json as Option[]);\n this.rowContentCache.clear();\n this.remoteLoaded = true;\n } catch {\n if (requestId !== this.ajaxRequestId || this.destroyed) return;\n this.data = [];\n this.rowContentCache.clear();\n } finally {\n if (requestId === this.ajaxRequestId && !this.destroyed) {\n this.loading = false;\n if (this.isOpen) this.renderList();\n }\n }\n }\n}\n\nfunction parseNativeOptions(select: HTMLSelectElement): DataItem[] {\n const data: DataItem[] = [];\n for (const child of Array.from(select.children)) {\n if (child instanceof HTMLOptGroupElement) {\n data.push({\n label: child.label,\n options: Array.from(child.querySelectorAll(\"option\")).map(parseOption),\n });\n } else if (child instanceof HTMLOptionElement) {\n data.push(parseOption(child));\n }\n }\n return data;\n}\n\nfunction parseOption(option: HTMLOptionElement): Option {\n return {\n value: option.value,\n label: option.textContent?.trim() ?? option.value,\n disabled: option.disabled || undefined,\n };\n}\n\nfunction buildUrl(ajax: AjaxConfig, query: string): string {\n if (typeof ajax.url === \"function\") return ajax.url(query);\n if (!ajax.params) return ajax.url;\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(ajax.params(query))) {\n params.set(key, String(value));\n }\n const separator = ajax.url.includes(\"?\") ? \"&\" : \"?\";\n return `${ajax.url}${separator}${params.toString()}`;\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "forge-select",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A modern, lightweight, highly customizable replacement for Select2.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"select",
|
|
7
|
+
"dropdown",
|
|
8
|
+
"combobox",
|
|
9
|
+
"select2",
|
|
10
|
+
"autocomplete",
|
|
11
|
+
"multiselect",
|
|
12
|
+
"tags"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "KonexForge",
|
|
16
|
+
"homepage": "https://cmm-cmm.github.io/ForgeSelect/",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/cmm-cmm/ForgeSelect/issues"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/cmm-cmm/ForgeSelect.git"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.js",
|
|
38
|
+
"require": "./dist/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./styles.css": "./styles/forge-select.css"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"styles"
|
|
45
|
+
],
|
|
46
|
+
"sideEffects": false,
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"build:site": "npm run build && node scripts/build-site.mjs",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"highlight.js": "^11.11.1",
|
|
57
|
+
"jsdom": "^26.0.0",
|
|
58
|
+
"markdown-it": "^14.3.0",
|
|
59
|
+
"tsup": "^8.3.5",
|
|
60
|
+
"typescript": "^5.7.0",
|
|
61
|
+
"vitest": "^3.0.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/* ForgeSelect — default theme, driven entirely by CSS custom properties. */
|
|
2
|
+
|
|
3
|
+
.forge-select {
|
|
4
|
+
--fs-bg: #ffffff;
|
|
5
|
+
--fs-fg: #1f2937;
|
|
6
|
+
--fs-muted: #9ca3af;
|
|
7
|
+
--fs-border: #d1d5db;
|
|
8
|
+
--fs-border-focus: #6366f1;
|
|
9
|
+
--fs-radius: 8px;
|
|
10
|
+
--fs-dropdown-bg: #ffffff;
|
|
11
|
+
--fs-dropdown-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
|
12
|
+
--fs-option-hover-bg: #eef2ff;
|
|
13
|
+
--fs-option-selected-bg: #e0e7ff;
|
|
14
|
+
--fs-option-selected-fg: #3730a3;
|
|
15
|
+
--fs-tag-bg: #e0e7ff;
|
|
16
|
+
--fs-tag-fg: #3730a3;
|
|
17
|
+
--fs-font-size: 14px;
|
|
18
|
+
|
|
19
|
+
position: relative;
|
|
20
|
+
display: inline-block;
|
|
21
|
+
min-width: 220px;
|
|
22
|
+
font-size: var(--fs-font-size);
|
|
23
|
+
color: var(--fs-fg);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.forge-select[data-theme="dark"] {
|
|
27
|
+
--fs-bg: #1f2937;
|
|
28
|
+
--fs-fg: #f9fafb;
|
|
29
|
+
--fs-muted: #6b7280;
|
|
30
|
+
--fs-border: #374151;
|
|
31
|
+
--fs-border-focus: #818cf8;
|
|
32
|
+
--fs-dropdown-bg: #111827;
|
|
33
|
+
--fs-dropdown-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
|
|
34
|
+
--fs-option-hover-bg: #312e81;
|
|
35
|
+
--fs-option-selected-bg: #3730a3;
|
|
36
|
+
--fs-option-selected-fg: #e0e7ff;
|
|
37
|
+
--fs-tag-bg: #3730a3;
|
|
38
|
+
--fs-tag-fg: #e0e7ff;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.forge-select__control {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 6px;
|
|
45
|
+
min-height: 38px;
|
|
46
|
+
padding: 4px 10px;
|
|
47
|
+
background: var(--fs-bg);
|
|
48
|
+
border: 1px solid var(--fs-border);
|
|
49
|
+
border-radius: var(--fs-radius);
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
user-select: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.forge-select__control:focus {
|
|
55
|
+
outline: none;
|
|
56
|
+
border-color: var(--fs-border-focus);
|
|
57
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fs-border-focus) 25%, transparent);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.forge-select--disabled .forge-select__control {
|
|
61
|
+
opacity: 0.55;
|
|
62
|
+
cursor: not-allowed;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.forge-select__value {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex: 1;
|
|
68
|
+
flex-wrap: wrap;
|
|
69
|
+
gap: 4px;
|
|
70
|
+
align-items: center;
|
|
71
|
+
min-width: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.forge-select__placeholder {
|
|
75
|
+
color: var(--fs-muted);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.forge-select__single-value {
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
text-overflow: ellipsis;
|
|
81
|
+
white-space: nowrap;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.forge-select__tag {
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
gap: 4px;
|
|
88
|
+
padding: 2px 6px;
|
|
89
|
+
background: var(--fs-tag-bg);
|
|
90
|
+
color: var(--fs-tag-fg);
|
|
91
|
+
border-radius: calc(var(--fs-radius) - 4px);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.forge-select__tag-remove,
|
|
95
|
+
.forge-select__clear {
|
|
96
|
+
padding: 0 2px;
|
|
97
|
+
background: none;
|
|
98
|
+
border: none;
|
|
99
|
+
font-size: 1.1em;
|
|
100
|
+
line-height: 1;
|
|
101
|
+
color: inherit;
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
opacity: 0.7;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.forge-select__tag-remove:hover,
|
|
107
|
+
.forge-select__clear:hover {
|
|
108
|
+
opacity: 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.forge-select__arrow {
|
|
112
|
+
width: 0;
|
|
113
|
+
height: 0;
|
|
114
|
+
border-left: 5px solid transparent;
|
|
115
|
+
border-right: 5px solid transparent;
|
|
116
|
+
border-top: 5px solid var(--fs-muted);
|
|
117
|
+
transition: transform 0.15s ease;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.forge-select--open .forge-select__arrow {
|
|
121
|
+
transform: rotate(180deg);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.forge-select__dropdown {
|
|
125
|
+
position: absolute;
|
|
126
|
+
z-index: 1000;
|
|
127
|
+
top: calc(100% + 4px);
|
|
128
|
+
left: 0;
|
|
129
|
+
right: 0;
|
|
130
|
+
background: var(--fs-dropdown-bg);
|
|
131
|
+
border: 1px solid var(--fs-border);
|
|
132
|
+
border-radius: var(--fs-radius);
|
|
133
|
+
box-shadow: var(--fs-dropdown-shadow);
|
|
134
|
+
overflow: hidden;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.forge-select__search {
|
|
138
|
+
width: 100%;
|
|
139
|
+
padding: 8px 10px;
|
|
140
|
+
background: transparent;
|
|
141
|
+
border: none;
|
|
142
|
+
border-bottom: 1px solid var(--fs-border);
|
|
143
|
+
color: var(--fs-fg);
|
|
144
|
+
font-size: inherit;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.forge-select__search:focus {
|
|
148
|
+
outline: none;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.forge-select__list {
|
|
152
|
+
max-height: 260px;
|
|
153
|
+
margin: 0;
|
|
154
|
+
padding: 4px 0;
|
|
155
|
+
overflow-y: auto;
|
|
156
|
+
list-style: none;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.forge-select__option {
|
|
160
|
+
display: flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
gap: 10px;
|
|
163
|
+
min-height: var(--fs-item-height, 36px);
|
|
164
|
+
padding: 0 12px;
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.forge-select__option-content {
|
|
169
|
+
display: contents;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.forge-select__option-avatar {
|
|
173
|
+
width: 28px;
|
|
174
|
+
height: 28px;
|
|
175
|
+
border-radius: 50%;
|
|
176
|
+
object-fit: cover;
|
|
177
|
+
flex-shrink: 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.forge-select__inline-avatar {
|
|
181
|
+
width: 18px;
|
|
182
|
+
height: 18px;
|
|
183
|
+
border-radius: 50%;
|
|
184
|
+
object-fit: cover;
|
|
185
|
+
flex-shrink: 0;
|
|
186
|
+
vertical-align: -4px;
|
|
187
|
+
margin-right: 5px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.forge-select__option-body {
|
|
191
|
+
display: flex;
|
|
192
|
+
flex-direction: column;
|
|
193
|
+
min-width: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.forge-select__option-label {
|
|
197
|
+
overflow: hidden;
|
|
198
|
+
text-overflow: ellipsis;
|
|
199
|
+
white-space: nowrap;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.forge-select__option-desc {
|
|
203
|
+
font-size: 0.85em;
|
|
204
|
+
color: var(--fs-muted);
|
|
205
|
+
overflow: hidden;
|
|
206
|
+
text-overflow: ellipsis;
|
|
207
|
+
white-space: nowrap;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.forge-select__option--highlighted,
|
|
211
|
+
.forge-select__option:hover:not(.forge-select__option--disabled) {
|
|
212
|
+
background: var(--fs-option-hover-bg);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.forge-select__option--selected {
|
|
216
|
+
background: var(--fs-option-selected-bg);
|
|
217
|
+
color: var(--fs-option-selected-fg);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.forge-select__option--disabled {
|
|
221
|
+
color: var(--fs-muted);
|
|
222
|
+
cursor: not-allowed;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.forge-select__option--create {
|
|
226
|
+
font-style: italic;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* All row types share --fs-item-height so virtual-scroll spacer math stays exact. */
|
|
230
|
+
.forge-select__group-label {
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: flex-end;
|
|
233
|
+
box-sizing: border-box;
|
|
234
|
+
min-height: var(--fs-item-height, 36px);
|
|
235
|
+
padding: 0 12px 4px;
|
|
236
|
+
font-size: 0.85em;
|
|
237
|
+
font-weight: 600;
|
|
238
|
+
text-transform: uppercase;
|
|
239
|
+
letter-spacing: 0.04em;
|
|
240
|
+
color: var(--fs-muted);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.forge-select__empty,
|
|
244
|
+
.forge-select__loading {
|
|
245
|
+
padding: 10px 12px;
|
|
246
|
+
color: var(--fs-muted);
|
|
247
|
+
text-align: center;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.forge-select__spacer {
|
|
251
|
+
padding: 0;
|
|
252
|
+
margin: 0;
|
|
253
|
+
}
|