init component
This commit is contained in:
317
node_modules/buetify/lib/components/form/autocomplete/BAutocomplete.js
generated
vendored
Executable file
317
node_modules/buetify/lib/components/form/autocomplete/BAutocomplete.js
generated
vendored
Executable file
@@ -0,0 +1,317 @@
|
||||
import "../../../../src/components/form/sass/form.sass";
|
||||
import "../../../../src/components/dropdown/dropdown.sass";
|
||||
import "../../../../src/components/form/autocomplete/autocomplete.sass";
|
||||
import { StaticUseInputProps } from '../../../composables/input/useInput';
|
||||
import { getUseModelPropsDefinition } from '../../../composables/model/useModel';
|
||||
import { useProxy } from '../../../composables/proxy';
|
||||
import { getEqPropsDefinition } from '../../../composables/shared';
|
||||
import { useThemePropsDefinition } from '../../../composables/theme';
|
||||
import { constEmptyArray, extractProp, isFunction, isHTMLElement, isObject, isString, toggleListItem } from '../../../utils/helpers';
|
||||
import { DropdownThemeMap } from '../../dropdown';
|
||||
import BDropdown from '../../dropdown/BDropdown';
|
||||
import { isArrowDownEvent, isArrowUpEvent, isEnterEvent, isEscEvent, isTabEvent } from '../../../utils/eventHelpers';
|
||||
import { constant, constVoid } from 'fp-ts/lib/function';
|
||||
import BDropdownDivider from '../../dropdown/BDropdownDivider';
|
||||
import BDropdownItem from '../../dropdown/BDropdownItem';
|
||||
import { head, isEmpty, lookup } from 'fp-ts/lib/Array';
|
||||
import { alt, chain, fold, fromNullable, isSome, map, none, some, toUndefined } from 'fp-ts/lib/Option';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { defineComponent, shallowRef, computed, onBeforeUpdate, nextTick, toRef, h } from 'vue';
|
||||
import { BInput } from '../input';
|
||||
|
||||
function getActiveDescendentId(selectedItems, itemId) {
|
||||
return pipe(selectedItems, head, chain(item => {
|
||||
if (isString(item) && isFunction(itemId)) {
|
||||
const id = extractProp(itemId, item);
|
||||
return isString(id) ? some(id) : none;
|
||||
}
|
||||
|
||||
if (isString(item)) {
|
||||
return some(item);
|
||||
}
|
||||
|
||||
if (isString(itemId) && isObject(item) && Object.hasOwnProperty.call(item, itemId)) {
|
||||
const id = item[itemId];
|
||||
return isString(id) ? some(id) : none;
|
||||
}
|
||||
|
||||
const id = extractProp(itemId, item);
|
||||
return isString(id) ? some(id) : none;
|
||||
}), toUndefined);
|
||||
}
|
||||
|
||||
function getAutocompleteItems(items, selectedItems, itemId, itemText, eq, hoveredItem) {
|
||||
return items.map((item, index) => {
|
||||
const id = extractProp(itemId, item);
|
||||
const nid = isString(id) ? id : String(id);
|
||||
const text = extractProp(itemText, item);
|
||||
return {
|
||||
id: nid,
|
||||
isSelected: selectedItems.some(i => eq.equals(i, item)),
|
||||
isHovered: isSome(hoveredItem) ? hoveredItem.value.id === nid : false,
|
||||
text: isString(text) ? text : String(text),
|
||||
value: item,
|
||||
index
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getSetSelected(props, closeDropdown, inputModel, selectedItemsModel) {
|
||||
const toggle = toggleListItem(props.eq);
|
||||
return item => {
|
||||
const text = extractProp(props.itemText, item.value);
|
||||
inputModel.value = props.clearOnSelect ? '' : isString(text) ? text : String(text);
|
||||
selectedItemsModel.value = toggle(item.value, selectedItemsModel.value || []);
|
||||
|
||||
if (props.closeOnSelect) {
|
||||
closeDropdown();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getSetHovered(hoveredItem, templateItems) {
|
||||
return item => {
|
||||
const newItem = fromNullable(item);
|
||||
|
||||
if (isSome(newItem)) {
|
||||
hoveredItem.value = newItem;
|
||||
pipe(newItem, map(item => item.index), chain(index => lookup(index, templateItems.value)), fold(constant(constVoid), li => () => li.focus && li.focus()))();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getOnKeydown(autocompleteItems, hoveredItem, closeDropdown, setSelected, setHovered) {
|
||||
function onArrowPress(isUp) {
|
||||
pipe(hoveredItem.value, map(item => item.index), alt(() => some(0)), chain(index => lookup(isUp ? Math.max(index - 1, 0) : Math.min(index + 1, autocompleteItems.value.length - 1), autocompleteItems.value)), fold(constant(constVoid), newItem => () => setHovered(newItem)))();
|
||||
}
|
||||
|
||||
return function onKeydown(event) {
|
||||
if (isEnterEvent(event)) {
|
||||
event.preventDefault();
|
||||
|
||||
if (isSome(hoveredItem.value)) {
|
||||
setSelected(hoveredItem.value.value);
|
||||
}
|
||||
} else if (isTabEvent(event)) {
|
||||
event.preventDefault();
|
||||
|
||||
if (isSome(hoveredItem.value)) {
|
||||
setSelected(hoveredItem.value.value);
|
||||
} else {
|
||||
nextTick(closeDropdown);
|
||||
}
|
||||
} else if (isArrowUpEvent(event)) {
|
||||
event.preventDefault();
|
||||
onArrowPress(true);
|
||||
} else if (isArrowDownEvent(event)) {
|
||||
event.preventDefault();
|
||||
onArrowPress(false);
|
||||
} else if (isEscEvent(event)) {
|
||||
event.preventDefault();
|
||||
nextTick(closeDropdown);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getGenerateItem(itemsRef, length, onKeydown, setSelected, setHovered, slots) {
|
||||
return function generateItem(item, index) {
|
||||
return h(BDropdownItem, {
|
||||
key: item.id,
|
||||
ref: el => {
|
||||
if (isHTMLElement(el)) {
|
||||
itemsRef.value[index] = el;
|
||||
}
|
||||
},
|
||||
id: item.id,
|
||||
isActive: item.isSelected,
|
||||
tabindex: item.isSelected ? -1 : 0,
|
||||
'aria-selected': item.isSelected,
|
||||
'aria-label': `Option ${index + 1} of ${length.value}`,
|
||||
class: {
|
||||
'is-hovered': item.isHovered
|
||||
},
|
||||
onClick: () => setSelected(item),
|
||||
onMouseenter: () => setHovered(item),
|
||||
onKeydown
|
||||
}, () => slots.default ? slots.default({
|
||||
option: item,
|
||||
index
|
||||
}) : item.text);
|
||||
};
|
||||
}
|
||||
|
||||
function generateHeaderItem(slots) {
|
||||
return h('li', {
|
||||
class: 'dropdown-item',
|
||||
tabindex: -1
|
||||
}, slots.header && slots.header());
|
||||
}
|
||||
|
||||
function generateFooterItem(slots) {
|
||||
return h('li', {
|
||||
class: 'dropdown-item',
|
||||
tabindex: -1
|
||||
}, slots.footer && slots.footer());
|
||||
}
|
||||
|
||||
function generateLoadingItem(slots) {
|
||||
return h('li', {
|
||||
tabindex: -1
|
||||
}, [h(BDropdownItem, {
|
||||
tag: 'div'
|
||||
}, () => slots.loading ? slots.loading() : 'Loading results...')]);
|
||||
}
|
||||
|
||||
function generateEmptyItem(modelValue, slots) {
|
||||
return h(BDropdownItem, {
|
||||
class: 'is-disabled'
|
||||
}, () => slots.empty ? slots.empty({
|
||||
searchValue: modelValue
|
||||
}) : modelValue ? `No results` : `No results for ${modelValue}`);
|
||||
}
|
||||
|
||||
function defineAutocomplete() {
|
||||
return defineComponent({
|
||||
name: 'b-autocomplete',
|
||||
props: { ...StaticUseInputProps,
|
||||
...getEqPropsDefinition(),
|
||||
...useThemePropsDefinition(DropdownThemeMap),
|
||||
...getUseModelPropsDefinition(),
|
||||
...getUseModelPropsDefinition('selectedItems', 'onUpdate:selectedItems'),
|
||||
selectedItems: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: constEmptyArray
|
||||
},
|
||||
itemFilter: {
|
||||
type: Function,
|
||||
required: false
|
||||
},
|
||||
itemId: {
|
||||
type: [String, Function],
|
||||
default: 'id'
|
||||
},
|
||||
itemText: {
|
||||
type: [String, Function],
|
||||
default: 'text'
|
||||
},
|
||||
closeOnSelect: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
clearOnSelect: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
openOnFocus: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
onSelected: {
|
||||
type: Function,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
setup(props, {
|
||||
slots
|
||||
}) {
|
||||
const {
|
||||
value: searchValue
|
||||
} = useProxy(computed(() => props.modelValue ?? ''), toRef(props, 'onUpdate:modelValue'));
|
||||
const {
|
||||
value: selectedItems
|
||||
} = useProxy(toRef(props, 'selectedItems'), toRef(props, 'onUpdate:selectedItems'));
|
||||
const itemsRef = shallowRef([]);
|
||||
const filteredItems = computed(() => {
|
||||
if (props.itemFilter) {
|
||||
return props.items.filter(props.itemFilter(searchValue.value));
|
||||
} else {
|
||||
const sv = searchValue.value.toLowerCase();
|
||||
const extract = props.itemText;
|
||||
return props.items.filter(i => extractProp(extract, i).toLowerCase().includes(sv));
|
||||
}
|
||||
});
|
||||
onBeforeUpdate(() => {
|
||||
itemsRef.value = [];
|
||||
});
|
||||
const dropdown = shallowRef(null);
|
||||
|
||||
function close() {
|
||||
dropdown.value && dropdown.value.toggle.setOff();
|
||||
}
|
||||
|
||||
const hoveredItem = shallowRef(none);
|
||||
const activeDescendentId = computed(() => getActiveDescendentId(props.selectedItems, props.itemId));
|
||||
const autocompleteItems = computed(() => getAutocompleteItems(filteredItems.value, selectedItems.value, props.itemId, props.itemText, props.eq, hoveredItem.value));
|
||||
const numberOfItems = computed(() => autocompleteItems.value.length);
|
||||
const setSelected = getSetSelected(props, close, searchValue, selectedItems);
|
||||
const setHovered = getSetHovered(hoveredItem, itemsRef);
|
||||
const onKeydown = getOnKeydown(autocompleteItems, hoveredItem, close, setSelected, setHovered);
|
||||
const generateItem = getGenerateItem(itemsRef, numberOfItems, onKeydown, setSelected, setHovered, slots);
|
||||
return () => {
|
||||
return h(BDropdown, {
|
||||
ref: dropdown,
|
||||
isMobileModal: false,
|
||||
class: ['b-autocomplete', {
|
||||
'is-expanded': props.isExpanded
|
||||
}]
|
||||
}, {
|
||||
trigger: () => {
|
||||
return h(BInput, {
|
||||
modelValue: searchValue.value,
|
||||
type: 'text',
|
||||
size: props.size,
|
||||
isLoading: props.isLoading,
|
||||
isRounded: props.isRounded,
|
||||
icon: props.icon,
|
||||
maxlength: props.maxlength,
|
||||
autocomplete: 'off',
|
||||
placeholder: props.placeholder,
|
||||
role: 'searchbox',
|
||||
'aria-activedescendant': activeDescendentId.value,
|
||||
'onUpdate:modelValue': val => {
|
||||
searchValue.value = val;
|
||||
},
|
||||
// onFocus: () => {
|
||||
// nextTick().then(() => {
|
||||
// if (props.openOnFocus && toggle.isOff.value) {
|
||||
// toggle.setOn();
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
onBlur: props.onBlur,
|
||||
onKeydown
|
||||
});
|
||||
},
|
||||
default: () => {
|
||||
let nodes;
|
||||
|
||||
if (props.isLoading) {
|
||||
nodes = [generateLoadingItem(slots)];
|
||||
} else {
|
||||
nodes = isEmpty(autocompleteItems.value) ? [generateEmptyItem(searchValue.value, slots)] : autocompleteItems.value.map(generateItem);
|
||||
|
||||
if (slots.header) {
|
||||
nodes.unshift(generateHeaderItem(slots), h(BDropdownDivider));
|
||||
}
|
||||
|
||||
if (slots.footer) {
|
||||
nodes.push(h(BDropdownDivider), generateFooterItem(slots));
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
export const BAutocomplete = defineAutocomplete();
|
||||
//# sourceMappingURL=BAutocomplete.js.map
|
||||
Reference in New Issue
Block a user