0 directories, 7 files

util

Home / testing / ai / tinai / util
/**
 * Selection manager for handling multi-select state, item toggles, and button updates.
 */

export class SelectionManager {
	#isSelectionMode = false;
	#selectedItems = [];
	#onSelectionChange;

	/**
	 * @param {Function} [onSelectionChange] - Callback invoked whenever selection state changes.
	 */
	constructor(onSelectionChange = null) {
		this.#onSelectionChange = onSelectionChange;
	}

	/**
	 * Gets whether multi-selection mode is active.
	 * @returns {boolean}
	 */
	get isSelectionMode() {
		return this.#isSelectionMode;
	}

	/**
	 * Sets multi-selection mode and notifies listeners.
	 * @param {boolean} val
	 */
	set isSelectionMode(val) {
		this.#isSelectionMode = !!val;
		this._notify();
	}

	/**
	 * Returns a copy of the selected items array.
	 * @returns {Array<string|number>}
	 */
	get selectedItems() {
		return [...this.#selectedItems];
	}

	/**
	 * Sets the selected items and notifies listeners.
	 * @param {Array<string|number>} items
	 */
	set selectedItems(items) {
		this.#selectedItems = Array.isArray(items) ? [...items] : [];
		this._notify();
	}

	/**
	 * Returns the count of selected items.
	 * @returns {number}
	 */
	get count() {
		return this.#selectedItems.length;
	}

	/**
	 * Toggles selection mode on or off. Clears selected items when disabled.
	 * @param {boolean|null} [force=null] - Optional boolean to force state.
	 * @returns {boolean} New selection mode state.
	 */
	toggleMode(force = null) {
		this.#isSelectionMode = force !== null ? !!force : !this.#isSelectionMode;
		if (!this.#isSelectionMode) {
			this.#selectedItems = [];
		}
		this._notify();
		return this.#isSelectionMode;
	}

	/**
	 * Checks if an item is currently selected.
	 * @param {string|number} id - Item identifier or index.
	 * @returns {boolean}
	 */
	isSelected(id) {
		return this.#selectedItems.includes(id);
	}

	/**
	 * Selects an item.
	 * @param {string|number} id - Item identifier or index.
	 */
	select(id) {
		if (!this.#selectedItems.includes(id)) {
			this.#selectedItems.push(id);
			this._notify();
		}
	}

	/**
	 * Deselects an item.
	 * @param {string|number} id - Item identifier or index.
	 */
	deselect(id) {
		const idx = this.#selectedItems.indexOf(id);
		if (idx !== -1) {
			this.#selectedItems.splice(idx, 1);
			this._notify();
		}
	}

	/**
	 * Toggles the selection state of a specific item.
	 * @param {string|number} id - Item identifier or index.
	 * @param {boolean|null} [force=null] - Optional force state.
	 * @returns {boolean} Whether the item is now selected.
	 */
	toggleItem(id, force = null) {
		const shouldSelect = force !== null ? !!force : !this.isSelected(id);
		if (shouldSelect) {
			this.select(id);
		} else {
			this.deselect(id);
		}
		return shouldSelect;
	}

	/**
	 * Resets selection state and exits selection mode.
	 */
	clear() {
		this.#selectedItems = [];
		this.#isSelectionMode = false;
		this._notify();
	}

	/**
	 * Updates the select button label and enables/disables associated action buttons.
	 * @param {HTMLElement|null} selectBtn - Button indicating selection count.
	 * @param {HTMLElement[]} [actionBtns=[]] - Action buttons to enable/disable.
	 * @param {string} [defaultLabel='Select'] - Default text when no items selected.
	 */
	updateControls(selectBtn, actionBtns = [], defaultLabel = 'Select') {
		if (selectBtn) {
			selectBtn.textContent = this.count > 0 ? String(this.count) : defaultLabel;
		}
		const hasSelection = this.count > 0;
		(actionBtns || []).forEach(btn => {
			if (btn) {
				btn.disabled = !hasSelection;
			}
		});
	}

	/**
	 * Notifies the selection change listener of updated state.
	 * @private
	 */
	_notify() {
		if (typeof this.#onSelectionChange === 'function') {
			this.#onSelectionChange({
				isSelectionMode: this.#isSelectionMode,
				selectedItems: this.selectedItems,
				count: this.count
			});
		}
	}
}
🌐
selection-manager.js ×
Type: Web, text/plain
3.76 Kilobytes
Last Modified 2026-09-04 14:23:26
⬇ Download File