@grail-ui/svelte
Version:
[](https://www.npmjs.com/package/@grail-ui/svelte) [](https://bundlephobia.com/package/@grail-ui/svelte) [ • 4.64 kB
JavaScript
import { writable } from 'svelte/store';
export function selectionModel(options) {
/** Currently-selected values. */
const _selection = new Set();
/** Event emitted when the value has changed. */
const changed = writable();
/** Keeps track of the deselected options that haven't been emitted by the change event. */
let _deselectedToEmit = [];
/** Keeps track of the selected options that haven't been emitted by the change event. */
let _selectedToEmit = [];
/** Cache for the array value of the selected items. */
let _selected = null;
const { initiallySelectedValues, multiple } = { multiple: false, ...options };
if (initiallySelectedValues && initiallySelectedValues.length) {
if (multiple) {
initiallySelectedValues.forEach((value) => _markSelected(value));
}
else {
_markSelected(initiallySelectedValues[0]);
}
}
_emitChangeEvent(true);
/** Selected values. */
function selected() {
if (!_selected) {
_selected = Array.from(_selection.values());
}
return _selected;
}
/** Selects a value or an array of values. */
function select(...values) {
_verifyValueAssignment(values);
values.forEach((value) => _markSelected(value));
_emitChangeEvent();
}
/** Deselects a value or an array of values. */
function deselect(...values) {
_verifyValueAssignment(values);
values.forEach((value) => _unmarkSelected(value));
_emitChangeEvent();
}
/** Toggles a value between selected and deselected. */
function toggle(value) {
isSelected(value) ? deselect(value) : select(value);
}
/** Clears all of the selected values. */
function clear() {
_unmarkAll();
_emitChangeEvent();
}
/** Determines whether a value is selected. */
function isSelected(value) {
return _selection.has(value);
}
/** Determines whether the model does not have a value. */
function isEmpty() {
return _selection.size === 0;
}
/** Determines whether the model has a value. */
function hasValue() {
return !isEmpty();
}
/** Sorts the selected values based on a predicate function. */
function sort(predicate) {
if (multiple && selected()) {
_selected?.sort(predicate);
}
}
/** Gets whether multiple values can be selected. */
function isMultipleSelection() {
return multiple;
}
/** Emits a change event and clears the records of selected and deselected values. */
function _emitChangeEvent(init = false) {
// Clear the selected values so they can be re-cached.
_selected = null;
if (init || _selectedToEmit.length || _deselectedToEmit.length) {
changed.set({
selection: _selection,
added: _selectedToEmit,
removed: _deselectedToEmit,
});
_deselectedToEmit = [];
_selectedToEmit = [];
}
}
/** Selects a value. */
function _markSelected(value) {
if (!isSelected(value)) {
if (!multiple) {
_unmarkAll();
}
_selection.add(value);
_selectedToEmit.push(value);
}
}
/** Deselects a value. */
function _unmarkSelected(value) {
if (isSelected(value)) {
_selection.delete(value);
_deselectedToEmit.push(value);
}
}
/** Clears out the selected values. */
function _unmarkAll() {
if (!isEmpty()) {
_selection.forEach((value) => _unmarkSelected(value));
}
}
/**
* Verifies the value assignment and throws an error if the specified value array is
* including multiple values while the selection model is not supporting multiple values.
*/
function _verifyValueAssignment(values) {
if (values.length > 1 && !multiple) {
throw getMultipleValuesInSingleSelectionError();
}
}
return {
changed,
toggle,
select,
deselect,
clear,
hasValue,
sort,
get selected() {
return selected();
},
isEmpty,
isSelected,
isMultipleSelection,
};
}
/**
* Returns an error that reports that multiple values are passed into a selection model
* with a single value.
*/
export function getMultipleValuesInSingleSelectionError() {
return Error('Cannot pass multiple values into SelectionModel with single-value mode.');
}