@coreui/vue
Version:
UI Components Library for Vue.js
116 lines (112 loc) • 4.53 kB
JavaScript
;
var vue = require('vue');
var _const = require('../chip/const.js');
var isRTL = require('../../utils/isRTL.js');
const chipSetContextKey = Symbol('CChipSet');
/**
* Shared chip-set engine for CChipSet and CChipInput — the Vue equivalent of the
* vanilla `ChipInput extends ChipSet`. It owns selection coordination
* (single/multiple, controlled-or-uncontrolled), roving focus, and the
* focus-a-neighbor-after-removal behavior, and `provide`s a context that CChip
* injects. Existence of the chips stays with the caller.
*/
const useChipSet = (options) => {
const { config, selectionMode = () => 'multiple', selected = () => undefined, defaultSelected = [], restoreFocusOnRemove = true, onSelectionChange, onRemove, } = options;
const rootRef = vue.ref();
const internalSelected = vue.ref(defaultSelected);
const selectedValues = vue.computed(() => {
const controlled = selected();
return controlled === undefined ? internalSelected.value : controlled;
});
const getFocusableChips = () => [
...(rootRef.value?.querySelectorAll(_const.SELECTOR_CHIP_FOCUSABLE) ?? []),
];
const emitSelection = (next) => {
if (selected() === undefined) {
internalSelected.value = next;
}
onSelectionChange?.(next);
};
const toggleSelected = (value, next) => {
const isSelected = selectedValues.value.includes(value);
if (next === isSelected) {
return;
}
if (next) {
// Single selection mode keeps only the freshly selected chip.
emitSelection(selectionMode() === 'single' ? [value] : [...selectedValues.value, value]);
return;
}
emitSelection(selectedValues.value.filter((item) => item !== value));
};
const clearSelection = () => {
if (selectedValues.value.length > 0) {
emitSelection([]);
}
};
const removeChip = (value) => {
if (restoreFocusOnRemove) {
const chips = getFocusableChips();
const index = chips.findIndex((chip) => chip.dataset.coreuiChipValue === value);
// Prefer the next chip, fall back to the previous one at the end.
const neighbor = chips[index + 1] ?? chips[index - 1] ?? null;
const neighborValue = neighbor?.dataset.coreuiChipValue ?? null;
if (neighborValue !== null) {
vue.nextTick(() => {
rootRef.value
?.querySelector(`[data-coreui-chip-value="${neighborValue}"]`)
?.focus();
});
}
}
emitSelection(selectedValues.value.filter((item) => item !== value));
onRemove?.(value);
};
// Roving focus: arrow keys move between chips and Home/End jump to the edges,
// with no cycling. Returns true when the key was handled.
const handleKeydown = (event) => {
const chip = event.target.closest(_const.SELECTOR_CHIP_FOCUSABLE);
if (!chip) {
return false;
}
const chips = getFocusableChips();
const index = chips.indexOf(chip);
// In RTL the visual direction is mirrored, so left/right swap.
const rtl = isRTL.default(rootRef.value);
switch (event.key) {
case 'ArrowLeft': {
event.preventDefault();
chips[rtl ? index + 1 : index - 1]?.focus();
return true;
}
case 'ArrowRight': {
event.preventDefault();
chips[rtl ? index - 1 : index + 1]?.focus();
return true;
}
case 'Home': {
event.preventDefault();
chips[0]?.focus();
return true;
}
case 'End': {
event.preventDefault();
chips[chips.length - 1]?.focus();
return true;
}
default: {
return false;
}
}
};
vue.provide(chipSetContextKey, {
config,
isSelected: (value) => selectedValues.value.includes(value),
toggleSelected: (value, next) => toggleSelected(value, next),
removeChip: (value) => removeChip(value),
});
return { rootRef, selectedValues, clearSelection, getFocusableChips, handleKeydown };
};
exports.chipSetContextKey = chipSetContextKey;
exports.useChipSet = useChipSet;
//# sourceMappingURL=useChipSet.js.map