reka-ui
Version:
Vue port for Radix UI Primitives.
243 lines (240 loc) • 7.76 kB
JavaScript
import { createContext } from "../shared/createContext.js";
import { getActiveElement } from "../shared/getActiveElement.js";
import { useDirection } from "../shared/useDirection.js";
import { useSelectionBehavior } from "../shared/useSelectionBehavior.js";
import { useTypeahead } from "../shared/useTypeahead.js";
import { Primitive } from "../Primitive/Primitive.js";
import { MAP_KEY_TO_FOCUS_INTENT } from "../RovingFocus/utils.js";
import { RovingFocusGroup_default } from "../RovingFocus/RovingFocusGroup.js";
import { flatten } from "./utils.js";
import { computed, createBlock, createVNode, defineComponent, nextTick, openBlock, ref, renderSlot, toRefs, unref, withCtx, withKeys, withModifiers } from "vue";
import { createEventHook, useVModel } from "@vueuse/core";
//#region src/Tree/TreeRoot.vue?vue&type=script&setup=true&lang.ts
const [injectTreeRootContext, provideTreeRootContext] = createContext("TreeRoot");
var TreeRoot_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "TreeRoot",
props: {
modelValue: {
type: null,
required: false
},
defaultValue: {
type: null,
required: false
},
items: {
type: Array,
required: false
},
expanded: {
type: Array,
required: false
},
defaultExpanded: {
type: Array,
required: false
},
getKey: {
type: Function,
required: true
},
getChildren: {
type: Function,
required: false,
default: (val) => val.children
},
selectionBehavior: {
type: String,
required: false,
default: "toggle"
},
multiple: {
type: Boolean,
required: false,
skipCheck: true
},
dir: {
type: String,
required: false
},
disabled: {
type: Boolean,
required: false
},
propagateSelect: {
type: Boolean,
required: false
},
bubbleSelect: {
type: Boolean,
required: false
},
asChild: {
type: Boolean,
required: false
},
as: {
type: null,
required: false,
default: "ul"
}
},
emits: ["update:modelValue", "update:expanded"],
setup(__props, { emit: __emit }) {
const props = __props;
const emits = __emit;
const { items, multiple, disabled, propagateSelect, dir: propDir, bubbleSelect } = toRefs(props);
const { handleTypeaheadSearch } = useTypeahead();
const dir = useDirection(propDir);
const rovingFocusGroupRef = ref();
const isVirtual = ref(false);
const virtualKeydownHook = createEventHook();
const modelValue = useVModel(props, "modelValue", emits, {
defaultValue: props.defaultValue ?? (multiple.value ? [] : void 0),
passive: true,
deep: true
});
const expanded = useVModel(props, "expanded", emits, {
defaultValue: props.defaultExpanded ?? [],
passive: props.expanded === void 0,
deep: true
});
const { onSelectItem, handleMultipleReplace } = useSelectionBehavior(modelValue, props);
const selectedKeys = computed(() => {
if (multiple.value && Array.isArray(modelValue.value)) return modelValue.value.map((i) => props.getKey(i));
else return [props.getKey(modelValue.value ?? {})];
});
function flattenItems(items$1, level = 1, parentItem) {
return items$1.reduce((acc, item, index) => {
const key = props.getKey(item);
const children = props.getChildren(item);
const isExpanded = expanded.value.includes(key);
const flattenedItem = {
_id: key,
value: item,
index,
level,
parentItem,
hasChildren: !!children,
bind: {
"value": item,
level,
"aria-setsize": items$1.length,
"aria-posinset": index + 1
}
};
acc.push(flattenedItem);
if (children && isExpanded) acc.push(...flattenItems(children, level + 1, item));
return acc;
}, []);
}
const expandedItems = computed(() => {
const items$1 = props.items;
const expandedKeys = expanded.value.map((i) => i);
return flattenItems(items$1 ?? []);
});
function handleKeydown(event) {
if (isVirtual.value) virtualKeydownHook.trigger(event);
else {
const collections = rovingFocusGroupRef.value?.getItems() ?? [];
handleTypeaheadSearch(event.key, collections);
}
}
function handleKeydownNavigation(event) {
if (isVirtual.value) return;
const intent = MAP_KEY_TO_FOCUS_INTENT[event.key];
nextTick(() => {
handleMultipleReplace(intent, getActiveElement(), rovingFocusGroupRef.value?.getItems, expandedItems.value.map((i) => i.value));
});
}
function handleBubbleSelect(item) {
if (item.parentItem != null && Array.isArray(modelValue.value) && props.multiple) {
const parentItem = expandedItems.value.find((i) => {
return item.parentItem != null && props.getKey(i.value) === props.getKey(item.parentItem);
});
if (parentItem != null) {
const areAllChilredOfParentSelected = props.getChildren(parentItem.value)?.every((i) => modelValue.value.find((v) => props.getKey(v) === props.getKey(i)));
if (areAllChilredOfParentSelected) modelValue.value = [...modelValue.value, parentItem.value];
else modelValue.value = modelValue.value.filter((v) => props.getKey(v) !== props.getKey(parentItem.value));
handleBubbleSelect(parentItem);
}
}
}
provideTreeRootContext({
modelValue,
selectedKeys,
onSelect: (val) => {
const condition = (baseValue) => props.getKey(baseValue ?? {}) === props.getKey(val);
const exist = props.multiple && Array.isArray(modelValue.value) ? modelValue.value?.findIndex(condition) !== -1 : void 0;
onSelectItem(val, condition);
if (props.bubbleSelect && props.multiple && Array.isArray(modelValue.value)) {
const item = expandedItems.value.find((i) => {
return props.getKey(i.value) === props.getKey(val);
});
if (item != null) handleBubbleSelect(item);
}
if (props.propagateSelect && props.multiple && Array.isArray(modelValue.value)) {
const children = flatten(props.getChildren(val) ?? []);
if (exist) modelValue.value = [...modelValue.value].filter((i) => !children.some((child) => props.getKey(i ?? {}) === props.getKey(child)));
else modelValue.value = [...modelValue.value, ...children];
}
},
expanded,
onToggle(val) {
const children = val ? props.getChildren(val) : void 0;
if (!children) return;
const key = props.getKey(val) ?? val;
if (expanded.value.includes(key)) expanded.value = expanded.value.filter((val$1) => val$1 !== key);
else expanded.value.push(key);
},
getKey: props.getKey,
getChildren: props.getChildren,
items,
expandedItems,
disabled,
multiple,
dir,
propagateSelect,
bubbleSelect,
isVirtual,
virtualKeydownHook,
handleMultipleReplace
});
return (_ctx, _cache) => {
return openBlock(), createBlock(unref(RovingFocusGroup_default), {
ref_key: "rovingFocusGroupRef",
ref: rovingFocusGroupRef,
"as-child": "",
orientation: "vertical",
dir: unref(dir)
}, {
default: withCtx(() => [createVNode(unref(Primitive), {
role: "tree",
as: _ctx.as,
"as-child": _ctx.asChild,
"aria-multiselectable": unref(multiple) ? true : void 0,
onKeydown: [handleKeydown, withKeys(withModifiers(handleKeydownNavigation, ["shift"]), ["up", "down"])]
}, {
default: withCtx(() => [renderSlot(_ctx.$slots, "default", {
flattenItems: expandedItems.value,
modelValue: unref(modelValue),
expanded: unref(expanded)
})]),
_: 3
}, 8, [
"as",
"as-child",
"aria-multiselectable",
"onKeydown"
])]),
_: 3
}, 8, ["dir"]);
};
}
});
//#endregion
//#region src/Tree/TreeRoot.vue
var TreeRoot_default = TreeRoot_vue_vue_type_script_setup_true_lang_default;
//#endregion
export { TreeRoot_default, injectTreeRootContext };
//# sourceMappingURL=TreeRoot.js.map