123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // Composables
- import { useProxiedModel } from "./proxiedModel.mjs"; // Utilities
- import { computed, inject, onBeforeUnmount, onMounted, provide, reactive, toRef, watch } from 'vue';
- import { consoleWarn, deepEqual, findChildrenWithProvide, getCurrentInstance, getUid, propsFactory, wrapInArray } from "../util/index.mjs"; // Types
- export const makeGroupProps = propsFactory({
- modelValue: {
- type: null,
- default: undefined
- },
- multiple: Boolean,
- mandatory: [Boolean, String],
- max: Number,
- selectedClass: String,
- disabled: Boolean
- }, 'group');
- export const makeGroupItemProps = propsFactory({
- value: null,
- disabled: Boolean,
- selectedClass: String
- }, 'group-item');
- export function useGroupItem(props, injectKey) {
- let required = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
- const vm = getCurrentInstance('useGroupItem');
- if (!vm) {
- throw new Error('[Vuetify] useGroupItem composable must be used inside a component setup function');
- }
- const id = getUid();
- provide(Symbol.for(`${injectKey.description}:id`), id);
- const group = inject(injectKey, null);
- if (!group) {
- if (!required) return group;
- throw new Error(`[Vuetify] Could not find useGroup injection with symbol ${injectKey.description}`);
- }
- const value = toRef(props, 'value');
- const disabled = computed(() => !!(group.disabled.value || props.disabled));
- group.register({
- id,
- value,
- disabled
- }, vm);
- onBeforeUnmount(() => {
- group.unregister(id);
- });
- const isSelected = computed(() => {
- return group.isSelected(id);
- });
- const selectedClass = computed(() => isSelected.value && [group.selectedClass.value, props.selectedClass]);
- watch(isSelected, value => {
- vm.emit('group:selected', {
- value
- });
- });
- return {
- id,
- isSelected,
- toggle: () => group.select(id, !isSelected.value),
- select: value => group.select(id, value),
- selectedClass,
- value,
- disabled,
- group
- };
- }
- export function useGroup(props, injectKey) {
- let isUnmounted = false;
- const items = reactive([]);
- const selected = useProxiedModel(props, 'modelValue', [], v => {
- if (v == null) return [];
- return getIds(items, wrapInArray(v));
- }, v => {
- const arr = getValues(items, v);
- return props.multiple ? arr : arr[0];
- });
- const groupVm = getCurrentInstance('useGroup');
- function register(item, vm) {
- // Is there a better way to fix this typing?
- const unwrapped = item;
- const key = Symbol.for(`${injectKey.description}:id`);
- const children = findChildrenWithProvide(key, groupVm?.vnode);
- const index = children.indexOf(vm);
- if (index > -1) {
- items.splice(index, 0, unwrapped);
- } else {
- items.push(unwrapped);
- }
- }
- function unregister(id) {
- if (isUnmounted) return;
- // TODO: re-evaluate this line's importance in the future
- // should we only modify the model if mandatory is set.
- // selected.value = selected.value.filter(v => v !== id)
- forceMandatoryValue();
- const index = items.findIndex(item => item.id === id);
- items.splice(index, 1);
- }
- // If mandatory and nothing is selected, then select first non-disabled item
- function forceMandatoryValue() {
- const item = items.find(item => !item.disabled);
- if (item && props.mandatory === 'force' && !selected.value.length) {
- selected.value = [item.id];
- }
- }
- onMounted(() => {
- forceMandatoryValue();
- });
- onBeforeUnmount(() => {
- isUnmounted = true;
- });
- function select(id, value) {
- const item = items.find(item => item.id === id);
- if (value && item?.disabled) return;
- if (props.multiple) {
- const internalValue = selected.value.slice();
- const index = internalValue.findIndex(v => v === id);
- const isSelected = ~index;
- value = value ?? !isSelected;
- // We can't remove value if group is
- // mandatory, value already exists,
- // and it is the only value
- if (isSelected && props.mandatory && internalValue.length <= 1) return;
- // We can't add value if it would
- // cause max limit to be exceeded
- if (!isSelected && props.max != null && internalValue.length + 1 > props.max) return;
- if (index < 0 && value) internalValue.push(id);else if (index >= 0 && !value) internalValue.splice(index, 1);
- selected.value = internalValue;
- } else {
- const isSelected = selected.value.includes(id);
- if (props.mandatory && isSelected) return;
- selected.value = value ?? !isSelected ? [id] : [];
- }
- }
- function step(offset) {
- // getting an offset from selected value obviously won't work with multiple values
- if (props.multiple) consoleWarn('This method is not supported when using "multiple" prop');
- if (!selected.value.length) {
- const item = items.find(item => !item.disabled);
- item && (selected.value = [item.id]);
- } else {
- const currentId = selected.value[0];
- const currentIndex = items.findIndex(i => i.id === currentId);
- let newIndex = (currentIndex + offset) % items.length;
- let newItem = items[newIndex];
- while (newItem.disabled && newIndex !== currentIndex) {
- newIndex = (newIndex + offset) % items.length;
- newItem = items[newIndex];
- }
- if (newItem.disabled) return;
- selected.value = [items[newIndex].id];
- }
- }
- const state = {
- register,
- unregister,
- selected,
- select,
- disabled: toRef(props, 'disabled'),
- prev: () => step(items.length - 1),
- next: () => step(1),
- isSelected: id => selected.value.includes(id),
- selectedClass: computed(() => props.selectedClass),
- items: computed(() => items),
- getItemIndex: value => getItemIndex(items, value)
- };
- provide(injectKey, state);
- return state;
- }
- function getItemIndex(items, value) {
- const ids = getIds(items, [value]);
- if (!ids.length) return -1;
- return items.findIndex(item => item.id === ids[0]);
- }
- function getIds(items, modelValue) {
- const ids = [];
- modelValue.forEach(value => {
- const item = items.find(item => deepEqual(value, item.value));
- const itemByIndex = items[value];
- if (item?.value != null) {
- ids.push(item.id);
- } else if (itemByIndex != null) {
- ids.push(itemByIndex.id);
- }
- });
- return ids;
- }
- function getValues(items, ids) {
- const values = [];
- ids.forEach(id => {
- const itemIndex = items.findIndex(item => item.id === id);
- if (~itemIndex) {
- const item = items[itemIndex];
- values.push(item.value != null ? item.value : itemIndex);
- }
- });
- return values;
- }
- //# sourceMappingURL=group.mjs.map
|