'use client'; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "useTreeItem_unstable", { enumerable: true, get: function() { return useTreeItem_unstable; } }); const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard"); const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react")); const _reactdom = /*#__PURE__*/ _interop_require_wildcard._(require("react-dom")); const _reactutilities = require("@fluentui/react-utilities"); const _keyboardkeys = require("@fluentui/keyboard-keys"); const _tokens = require("../../utils/tokens"); const _contexts = require("../../contexts"); const _getTreeItemValueFromElement = require("../../utils/getTreeItemValueFromElement"); const _reactcontextselector = require("@fluentui/react-context-selector"); const _Tree = require("../../Tree"); function useTreeItem_unstable(props, ref) { 'use no memo'; const treeType = (0, _contexts.useTreeContext_unstable)((ctx)=>ctx.treeType); if (treeType === 'flat') { warnIfNoProperPropsFlatTreeItem(props); } const requestTreeResponse = (0, _contexts.useTreeContext_unstable)((ctx)=>ctx.requestTreeResponse); const navigationMode = (0, _contexts.useTreeContext_unstable)((ctx)=>{ var _ctx_navigationMode; return (_ctx_navigationMode = ctx.navigationMode) !== null && _ctx_navigationMode !== void 0 ? _ctx_navigationMode : 'tree'; }); const forceUpdateRovingTabIndex = (0, _contexts.useTreeContext_unstable)((ctx)=>ctx.forceUpdateRovingTabIndex); const { level: contextLevel } = (0, _contexts.useSubtreeContext_unstable)(); const parentValue = (0, _contexts.useTreeItemContext_unstable)((ctx)=>{ var _props_parentValue; return (_props_parentValue = props.parentValue) !== null && _props_parentValue !== void 0 ? _props_parentValue : ctx.value; }); // note, if the value is not externally provided, // then selection and expansion will not work properly const internalValue = (0, _reactutilities.useId)('fuiTreeItemValue-'); var _props_value; const value = (_props_value = props.value) !== null && _props_value !== void 0 ? _props_value : internalValue; const { onClick, onKeyDown, onChange, as = 'div', itemType = 'leaf', 'aria-level': level = contextLevel, 'aria-selected': ariaSelected, 'aria-expanded': ariaExpanded, ...rest } = props; const actionsRef = _react.useRef(null); const expandIconRef = _react.useRef(null); const layoutRef = _react.useRef(null); const subtreeRef = _react.useRef(null); const selectionRef = _react.useRef(null); const treeItemRef = _react.useRef(null); if (process.env.NODE_ENV !== 'production') { // This is acceptable since the NODE_ENV will not change during runtime // eslint-disable-next-line react-hooks/rules-of-hooks const hasTreeContext = (0, _reactcontextselector.useHasParentContext)(_contexts.TreeContext); // eslint-disable-next-line react-hooks/rules-of-hooks _react.useEffect(()=>{ var _treeItemRef_current; if (hasTreeContext) { return; } if ((_treeItemRef_current = treeItemRef.current) === null || _treeItemRef_current === void 0 ? void 0 : _treeItemRef_current.querySelector(`.${_Tree.treeClassNames.root}`)) { // eslint-disable-next-line no-console console.error(`@fluentui/react-tree [useTreeItem]: should be declared inside a component.`); } }, [ hasTreeContext ]); } _react.useEffect(()=>{ // When the tree item is mounted, we might need to update the roving tab index // in edge cases where the tree is empty and then populated forceUpdateRovingTabIndex === null || forceUpdateRovingTabIndex === void 0 ? void 0 : forceUpdateRovingTabIndex(); const treeItem = treeItemRef.current; return ()=>{ // When the tree item is unmounted, we need to update the roving tab index // if the tree item is the current tab indexed item if (treeItem && treeItem.tabIndex === 0) { forceUpdateRovingTabIndex === null || forceUpdateRovingTabIndex === void 0 ? void 0 : forceUpdateRovingTabIndex(); } }; }, [ forceUpdateRovingTabIndex ]); const open = (0, _contexts.useTreeContext_unstable)((ctx)=>{ var _props_open; return (_props_open = props.open) !== null && _props_open !== void 0 ? _props_open : ctx.openItems.has(value); }); const getNextOpen = ()=>itemType === 'branch' ? !open : open; const selectionMode = (0, _contexts.useTreeContext_unstable)((ctx)=>ctx.selectionMode); const checked = (0, _contexts.useTreeContext_unstable)((ctx)=>{ var _ctx_checkedItems_get; return (_ctx_checkedItems_get = ctx.checkedItems.get(value)) !== null && _ctx_checkedItems_get !== void 0 ? _ctx_checkedItems_get : false; }); const handleClick = (0, _reactutilities.useEventCallback)((event)=>{ var _expandIconRef_current; const isEventFromActions = ()=>actionsRef.current && (0, _reactutilities.elementContains)(actionsRef.current, event.target); const isEventFromSubtree = ()=>subtreeRef.current && (0, _reactutilities.elementContains)(subtreeRef.current, event.target); const isEventFromSelection = ()=>{ var _selectionRef_current; return (_selectionRef_current = selectionRef.current) === null || _selectionRef_current === void 0 ? void 0 : _selectionRef_current.contains(event.target); }; const isEventFromExpandIcon = (_expandIconRef_current = expandIconRef.current) === null || _expandIconRef_current === void 0 ? void 0 : _expandIconRef_current.contains(event.target); if (isEventFromActions() || isEventFromSubtree() || isEventFromSelection()) { return; } else if (!isEventFromExpandIcon) { onClick === null || onClick === void 0 ? void 0 : onClick(event); } if (event.isDefaultPrevented()) { return; } _reactdom.unstable_batchedUpdates(()=>{ const data = { event, value, open: getNextOpen(), target: event.currentTarget, type: isEventFromExpandIcon ? _tokens.treeDataTypes.ExpandIconClick : _tokens.treeDataTypes.Click }; if (itemType !== 'leaf') { var _props_onOpenChange; (_props_onOpenChange = props.onOpenChange) === null || _props_onOpenChange === void 0 ? void 0 : _props_onOpenChange.call(props, event, data); requestTreeResponse({ ...data, itemType, requestType: 'open' }); } requestTreeResponse({ ...data, itemType, parentValue, requestType: 'navigate', type: _tokens.treeDataTypes.Click }); }); }); const handleKeyDown = (0, _reactutilities.useEventCallback)((event)=>{ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event); if (event.isDefaultPrevented() || !treeItemRef.current) { return; } const isEventFromTreeItem = event.currentTarget === event.target; const isEventFromActions = actionsRef.current && actionsRef.current.contains(event.target); switch(event.key){ case _keyboardkeys.Space: { if (!isEventFromTreeItem) { return; } if (selectionMode !== 'none') { var _selectionRef_current; (_selectionRef_current = selectionRef.current) === null || _selectionRef_current === void 0 ? void 0 : _selectionRef_current.click(); // Prevents the page from scrolling down when the spacebar is pressed event.preventDefault(); } return; } case _tokens.treeDataTypes.Enter: { if (!isEventFromTreeItem) { return; } return event.currentTarget.click(); } case _tokens.treeDataTypes.End: case _tokens.treeDataTypes.Home: case _tokens.treeDataTypes.ArrowUp: { if (!isEventFromTreeItem && !isEventFromActions) { return; } return requestTreeResponse({ requestType: 'navigate', event, value, itemType, parentValue, type: event.key, target: event.currentTarget }); } case _tokens.treeDataTypes.ArrowDown: { if (!isEventFromTreeItem && !isEventFromActions) { return; } if (isEventFromActions && (!(0, _reactutilities.isHTMLElement)(event.target) || event.target.hasAttribute('aria-haspopup'))) { return; } return requestTreeResponse({ requestType: 'navigate', event, value, itemType, parentValue, type: event.key, target: event.currentTarget }); } case _tokens.treeDataTypes.ArrowLeft: { // arrow left with alt key is reserved for history navigation if (event.altKey) { return; } const data = { value, event, open: getNextOpen(), type: event.key, itemType, parentValue, target: event.currentTarget }; if (isEventFromActions && navigationMode === 'treegrid') { requestTreeResponse({ ...data, requestType: 'navigate' }); return; } if (!isEventFromTreeItem) { return; } // do not navigate to parent if the item is on the top level if (level === 1 && !open) { return; } if (open) { var _props_onOpenChange; (_props_onOpenChange = props.onOpenChange) === null || _props_onOpenChange === void 0 ? void 0 : _props_onOpenChange.call(props, event, data); } requestTreeResponse({ ...data, requestType: open ? 'open' : 'navigate' }); return; } case _tokens.treeDataTypes.ArrowRight: { // Ignore keyboard events that do not originate from the current tree item. if (!isEventFromTreeItem) { return; } // arrow right with alt key is reserved for history navigation if (event.altKey) { return; } const data = { value, event, open: getNextOpen(), type: event.key, target: event.currentTarget }; if (itemType === 'branch' && !open) { var _props_onOpenChange1; (_props_onOpenChange1 = props.onOpenChange) === null || _props_onOpenChange1 === void 0 ? void 0 : _props_onOpenChange1.call(props, event, data); requestTreeResponse({ ...data, itemType, requestType: 'open' }); } else { requestTreeResponse({ ...data, itemType, parentValue, requestType: 'navigate' }); } return; } } // Ignore keyboard events that do not originate from the current tree item. if (!isEventFromTreeItem) { return; } const isTypeAheadCharacter = event.key.length === 1 && event.key.match(/\w/) && !event.altKey && !event.ctrlKey && !event.metaKey; if (isTypeAheadCharacter) { requestTreeResponse({ requestType: 'navigate', event, target: event.currentTarget, value, itemType, type: _tokens.treeDataTypes.TypeAhead, parentValue }); } }); const handleChange = (0, _reactutilities.useEventCallback)((event)=>{ onChange === null || onChange === void 0 ? void 0 : onChange(event); if (event.isDefaultPrevented()) { return; } const isEventFromSubtree = subtreeRef.current && (0, _reactutilities.elementContains)(subtreeRef.current, event.target); if (isEventFromSubtree) { return; } requestTreeResponse({ requestType: 'selection', event, value, itemType, type: 'Change', target: event.currentTarget, checked: checked === 'mixed' ? true : !checked }); }); return { value, open, checked, subtreeRef, layoutRef, selectionRef, expandIconRef, treeItemRef, actionsRef, itemType, level, components: { root: 'div' }, // FIXME: this property is not necessary anymore, but as removing it would be a breaking change, we need to keep it as false isAsideVisible: false, // FIXME: this property is not necessary anymore, but as removing it would be a breaking change, we need to keep it as false isActionsVisible: false, root: _reactutilities.slot.always((0, _reactutilities.getIntrinsicElementProps)(as, { tabIndex: -1, [_getTreeItemValueFromElement.dataTreeItemValueAttrName]: value, role: 'treeitem', ...rest, ref: (0, _reactutilities.useMergedRefs)(ref, treeItemRef), 'aria-level': level, 'aria-checked': selectionMode === 'multiselect' ? checked : undefined, 'aria-selected': ariaSelected !== undefined ? ariaSelected : selectionMode === 'single' ? !!checked : undefined, 'aria-expanded': ariaExpanded !== undefined ? ariaExpanded : itemType === 'branch' ? open : undefined, onClick: handleClick, onKeyDown: handleKeyDown, onChange: handleChange }), { elementType: 'div' }) }; } function warnIfNoProperPropsFlatTreeItem(props) { if (process.env.NODE_ENV !== 'production') { if (props['aria-posinset'] === undefined || props['aria-setsize'] === undefined || props['aria-level'] === undefined || props.parentValue === undefined && props['aria-level'] !== 1) { // eslint-disable-next-line no-console console.error(`@fluentui/react-tree [${useTreeItem_unstable.name}]: A flat treeitem must have "aria-posinset", "aria-setsize", "aria-level" and "parentValue" (if "aria-level" > 1) to ensure a11y and navigation. - "aria-posinset": the position of this treeitem in the current level of the tree. - "aria-setsize": the number of siblings in this level of the tree. - "aria-level": the current level of the treeitem. - "parentValue": the "value" property of the parent item of this item.`); } } }