first commit

This commit is contained in:
Stefan Hacker
2026-04-03 09:38:48 +02:00
commit 37ad745546
47450 changed files with 3120798 additions and 0 deletions
@@ -0,0 +1,42 @@
import * as React from 'react';
import { SuggestionActionType } from './Suggestions.types';
import type { IButton } from '../../../Button';
import type { ISuggestionsProps } from './Suggestions.types';
import type { JSXElement } from '@fluentui/utilities';
export interface ISuggestionsState {
selectedActionType: SuggestionActionType;
}
/**
* {@docCategory Pickers}
*/
export declare class Suggestions<T> extends React.Component<ISuggestionsProps<T>, ISuggestionsState> {
protected _forceResolveButton: React.RefObject<IButton | null>;
protected _searchForMoreButton: React.RefObject<IButton | null>;
protected _selectedElement: React.RefObject<HTMLDivElement | null>;
protected _scrollContainer: React.RefObject<HTMLDivElement | null>;
private activeSelectedElement;
private _classNames;
constructor(suggestionsProps: ISuggestionsProps<T>);
componentDidMount(): void;
componentDidUpdate(): void;
render(): JSXElement;
/**
* Returns true if the event was handled, false otherwise
*/
tryHandleKeyDown: (keyCode: number, currentSuggestionIndex: number) => boolean;
hasSuggestedAction(): boolean;
hasSuggestedActionSelected(): boolean;
executeSelectedAction(): void;
focusAboveSuggestions(): void;
focusBelowSuggestions(): void;
focusSearchForMoreButton(): void;
scrollSelected(): void;
private _getAlertText;
private _renderSuggestions;
private _getMoreResults;
private _forceResolve;
private _shouldShowForceResolve;
private _onClickTypedSuggestionsItem;
private _refocusOnSuggestions;
private _onRemoveTypedSuggestionsItem;
}
@@ -0,0 +1,326 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Suggestions = void 0;
var tslib_1 = require("tslib");
var React = require("react");
var Utilities_1 = require("../../../Utilities");
var Button_1 = require("../../../Button");
var Spinner_1 = require("../../../Spinner");
var Announced_1 = require("../../../Announced");
var Suggestions_types_1 = require("./Suggestions.types");
var SuggestionsItem_1 = require("./SuggestionsItem");
var SuggestionsItem_styles_1 = require("./SuggestionsItem.styles");
var stylesImport = require("./Suggestions.scss");
var legacyStyles = stylesImport;
var getClassNames = (0, Utilities_1.classNamesFunction)();
var StyledSuggestionsItem = (0, Utilities_1.styled)(SuggestionsItem_1.SuggestionsItem, SuggestionsItem_styles_1.getStyles, undefined, {
scope: 'SuggestionItem',
});
/**
* {@docCategory Pickers}
*/
var Suggestions = /** @class */ (function (_super) {
tslib_1.__extends(Suggestions, _super);
function Suggestions(suggestionsProps) {
var _this = _super.call(this, suggestionsProps) || this;
_this._forceResolveButton = React.createRef();
_this._searchForMoreButton = React.createRef();
_this._selectedElement = React.createRef();
_this._scrollContainer = React.createRef();
/**
* Returns true if the event was handled, false otherwise
*/
_this.tryHandleKeyDown = function (keyCode, currentSuggestionIndex) {
var isEventHandled = false;
var newSelectedActionType = null;
var currentSelectedAction = _this.state.selectedActionType;
var suggestionLength = _this.props.suggestions.length;
if (keyCode === Utilities_1.KeyCodes.down) {
switch (currentSelectedAction) {
case Suggestions_types_1.SuggestionActionType.forceResolve:
if (suggestionLength > 0) {
_this._refocusOnSuggestions(keyCode);
newSelectedActionType = Suggestions_types_1.SuggestionActionType.none;
}
else if (_this._searchForMoreButton.current) {
newSelectedActionType = Suggestions_types_1.SuggestionActionType.searchMore;
}
else {
newSelectedActionType = Suggestions_types_1.SuggestionActionType.forceResolve;
}
break;
case Suggestions_types_1.SuggestionActionType.searchMore:
if (_this._forceResolveButton.current) {
newSelectedActionType = Suggestions_types_1.SuggestionActionType.forceResolve;
}
else if (suggestionLength > 0) {
_this._refocusOnSuggestions(keyCode);
newSelectedActionType = Suggestions_types_1.SuggestionActionType.none;
}
else {
newSelectedActionType = Suggestions_types_1.SuggestionActionType.searchMore;
}
break;
case Suggestions_types_1.SuggestionActionType.none:
if (currentSuggestionIndex === -1 && _this._forceResolveButton.current) {
newSelectedActionType = Suggestions_types_1.SuggestionActionType.forceResolve;
}
break;
}
}
else if (keyCode === Utilities_1.KeyCodes.up) {
switch (currentSelectedAction) {
case Suggestions_types_1.SuggestionActionType.forceResolve:
if (_this._searchForMoreButton.current) {
newSelectedActionType = Suggestions_types_1.SuggestionActionType.searchMore;
}
else if (suggestionLength > 0) {
_this._refocusOnSuggestions(keyCode);
newSelectedActionType = Suggestions_types_1.SuggestionActionType.none;
}
break;
case Suggestions_types_1.SuggestionActionType.searchMore:
if (suggestionLength > 0) {
_this._refocusOnSuggestions(keyCode);
newSelectedActionType = Suggestions_types_1.SuggestionActionType.none;
}
else if (_this._forceResolveButton.current) {
newSelectedActionType = Suggestions_types_1.SuggestionActionType.forceResolve;
}
break;
case Suggestions_types_1.SuggestionActionType.none:
if (currentSuggestionIndex === -1 && _this._searchForMoreButton.current) {
newSelectedActionType = Suggestions_types_1.SuggestionActionType.searchMore;
}
break;
}
}
if (newSelectedActionType !== null) {
_this.setState({ selectedActionType: newSelectedActionType });
isEventHandled = true;
}
return isEventHandled;
};
_this._getAlertText = function () {
var _a = _this.props, isLoading = _a.isLoading, isSearching = _a.isSearching, suggestions = _a.suggestions, suggestionsAvailableAlertText = _a.suggestionsAvailableAlertText, noResultsFoundText = _a.noResultsFoundText, isExtendedLoading = _a.isExtendedLoading, loadingText = _a.loadingText;
if (!isLoading && !isSearching) {
if (suggestions.length > 0) {
return suggestionsAvailableAlertText || '';
}
if (noResultsFoundText) {
return noResultsFoundText;
}
}
else if (isLoading && isExtendedLoading) {
return loadingText || '';
}
return '';
};
_this._getMoreResults = function () {
if (_this.props.onGetMoreResults) {
_this.props.onGetMoreResults();
// Reset selected action type as it will be of type SuggestionActionType.none after more results are gotten
_this.setState({ selectedActionType: Suggestions_types_1.SuggestionActionType.none });
}
};
_this._forceResolve = function () {
if (_this.props.createGenericItem) {
_this.props.createGenericItem();
}
};
_this._shouldShowForceResolve = function () {
return _this.props.showForceResolve ? _this.props.showForceResolve() : false;
};
_this._onClickTypedSuggestionsItem = function (item, index) {
return function (ev) {
_this.props.onSuggestionClick(ev, item, index);
};
};
_this._refocusOnSuggestions = function (keyCode) {
if (typeof _this.props.refocusSuggestions === 'function') {
_this.props.refocusSuggestions(keyCode);
}
};
_this._onRemoveTypedSuggestionsItem = function (item, index) {
return function (ev) {
var onSuggestionRemove = _this.props.onSuggestionRemove;
onSuggestionRemove(ev, item, index);
ev.stopPropagation();
};
};
(0, Utilities_1.initializeComponentRef)(_this);
_this.state = {
selectedActionType: Suggestions_types_1.SuggestionActionType.none,
};
return _this;
}
Suggestions.prototype.componentDidMount = function () {
this.scrollSelected();
this.activeSelectedElement = this._selectedElement ? this._selectedElement.current : null;
};
Suggestions.prototype.componentDidUpdate = function () {
// Only scroll to selected element if the selected element has changed. Otherwise do nothing.
// This prevents some odd behavior where scrolling the active element out of view and clicking on a selected element
// will trigger a focus event and not give the clicked element the click.
if (this._selectedElement.current && this.activeSelectedElement !== this._selectedElement.current) {
this.scrollSelected();
this.activeSelectedElement = this._selectedElement.current;
}
};
Suggestions.prototype.render = function () {
var _a, _b;
var _this = this;
var _c = this.props, forceResolveText = _c.forceResolveText, mostRecentlyUsedHeaderText = _c.mostRecentlyUsedHeaderText, searchForMoreIcon = _c.searchForMoreIcon, searchForMoreText = _c.searchForMoreText, className = _c.className, moreSuggestionsAvailable = _c.moreSuggestionsAvailable, noResultsFoundText = _c.noResultsFoundText, suggestions = _c.suggestions, isLoading = _c.isLoading, isSearching = _c.isSearching, loadingText = _c.loadingText, onRenderNoResultFound = _c.onRenderNoResultFound, searchingText = _c.searchingText, isMostRecentlyUsedVisible = _c.isMostRecentlyUsedVisible, resultsMaximumNumber = _c.resultsMaximumNumber, resultsFooterFull = _c.resultsFooterFull, resultsFooter = _c.resultsFooter, _d = _c.isResultsFooterVisible, isResultsFooterVisible = _d === void 0 ? true : _d, suggestionsHeaderText = _c.suggestionsHeaderText, suggestionsClassName = _c.suggestionsClassName, theme = _c.theme, styles = _c.styles, suggestionsListId = _c.suggestionsListId, suggestionsContainerAriaLabel = _c.suggestionsContainerAriaLabel;
// TODO
// Clean this up by leaving only the first part after removing support for SASS.
// Currently we can not remove the SASS styles from Suggestions class because it
// might be used by consumers separately from pickers extending from BasePicker
// and have not used the new 'styles' prop. Because it's expecting a type parameter,
// we can not use the 'styled' function without adding some helpers which can break
// downstream consumers who did not use the new helpers.
// We check for 'styles' prop which is going to be injected by the 'styled' HOC
// in BasePicker when the typed Suggestions class is ready to be rendered. If the check
// passes we can use the CSS-in-JS styles. If the check fails (ex: custom picker),
// then we just use the old SASS styles instead.
this._classNames = styles
? getClassNames(styles, {
theme: theme,
className: className,
suggestionsClassName: suggestionsClassName,
forceResolveButtonSelected: this.state.selectedActionType === Suggestions_types_1.SuggestionActionType.forceResolve,
searchForMoreButtonSelected: this.state.selectedActionType === Suggestions_types_1.SuggestionActionType.searchMore,
})
: {
root: (0, Utilities_1.css)('ms-Suggestions', className, legacyStyles.root),
title: (0, Utilities_1.css)('ms-Suggestions-title', legacyStyles.suggestionsTitle),
searchForMoreButton: (0, Utilities_1.css)('ms-SearchMore-button', legacyStyles.actionButton, (_a = {},
_a['is-selected ' + legacyStyles.buttonSelected] = this.state.selectedActionType === Suggestions_types_1.SuggestionActionType.searchMore,
_a)),
forceResolveButton: (0, Utilities_1.css)('ms-forceResolve-button', legacyStyles.actionButton, (_b = {},
_b['is-selected ' + legacyStyles.buttonSelected] = this.state.selectedActionType === Suggestions_types_1.SuggestionActionType.forceResolve,
_b)),
suggestionsAvailable: (0, Utilities_1.css)('ms-Suggestions-suggestionsAvailable', legacyStyles.suggestionsAvailable),
suggestionsContainer: (0, Utilities_1.css)('ms-Suggestions-container', legacyStyles.suggestionsContainer, suggestionsClassName),
noSuggestions: (0, Utilities_1.css)('ms-Suggestions-none', legacyStyles.suggestionsNone),
};
var spinnerStyles = this._classNames.subComponentStyles
? this._classNames.subComponentStyles.spinner
: undefined;
// TODO: cleanup after refactor of pickers to composition pattern and remove SASS support.
var spinnerClassNameOrStyles = styles
? { styles: spinnerStyles }
: {
className: (0, Utilities_1.css)('ms-Suggestions-spinner', legacyStyles.suggestionsSpinner),
};
var noResults = function () {
var defaultRender = function () {
return React.createElement("div", { className: _this._classNames.noSuggestions }, noResultsFoundText);
};
return (
// This ID can be used by the parent to set aria-activedescendant to this
React.createElement("div", { id: "sug-noResultsFound", role: "option" }, onRenderNoResultFound ? onRenderNoResultFound(undefined, defaultRender) : defaultRender()));
};
// MostRecently Used text should supercede the header text if it's there and available.
var headerText = suggestionsHeaderText;
if (isMostRecentlyUsedVisible && mostRecentlyUsedHeaderText) {
headerText = mostRecentlyUsedHeaderText;
}
var footerTitle = undefined;
if (isResultsFooterVisible) {
footerTitle = suggestions.length >= resultsMaximumNumber ? resultsFooterFull : resultsFooter;
}
var hasNoSuggestions = (!suggestions || !suggestions.length) && !isLoading;
var forceResolveId = this.state.selectedActionType === Suggestions_types_1.SuggestionActionType.forceResolve ? 'sug-selectedAction' : undefined;
var searchForMoreId = this.state.selectedActionType === Suggestions_types_1.SuggestionActionType.searchMore ? 'sug-selectedAction' : undefined;
return (React.createElement("div", { className: this._classNames.root, "aria-label": suggestionsContainerAriaLabel || headerText, id: suggestionsListId, role: "listbox" },
React.createElement(Announced_1.Announced, { message: this._getAlertText(), "aria-live": "polite" }),
headerText ? React.createElement("div", { className: this._classNames.title }, headerText) : null,
forceResolveText && this._shouldShowForceResolve() && (React.createElement(Button_1.CommandButton, { componentRef: this._forceResolveButton, className: this._classNames.forceResolveButton, id: forceResolveId, onClick: this._forceResolve, "data-automationid": 'sug-forceResolve' }, forceResolveText)),
isLoading && React.createElement(Spinner_1.Spinner, tslib_1.__assign({}, spinnerClassNameOrStyles, { ariaLabel: loadingText, label: loadingText })),
hasNoSuggestions ? noResults() : this._renderSuggestions(),
searchForMoreText && moreSuggestionsAvailable && (React.createElement(Button_1.CommandButton, { componentRef: this._searchForMoreButton, className: this._classNames.searchForMoreButton, iconProps: searchForMoreIcon || { iconName: 'Search' }, id: searchForMoreId, onClick: this._getMoreResults, "data-automationid": 'sug-searchForMore', role: 'option' }, searchForMoreText)),
isSearching ? React.createElement(Spinner_1.Spinner, tslib_1.__assign({}, spinnerClassNameOrStyles, { ariaLabel: searchingText, label: searchingText })) : null,
footerTitle && !moreSuggestionsAvailable && !isMostRecentlyUsedVisible && !isSearching ? (React.createElement("div", { className: this._classNames.title }, footerTitle(this.props))) : null));
};
Suggestions.prototype.hasSuggestedAction = function () {
return !!this._searchForMoreButton.current || !!this._forceResolveButton.current;
};
Suggestions.prototype.hasSuggestedActionSelected = function () {
return this.state.selectedActionType !== Suggestions_types_1.SuggestionActionType.none;
};
Suggestions.prototype.executeSelectedAction = function () {
switch (this.state.selectedActionType) {
case Suggestions_types_1.SuggestionActionType.forceResolve:
this._forceResolve();
break;
case Suggestions_types_1.SuggestionActionType.searchMore:
this._getMoreResults();
break;
}
};
Suggestions.prototype.focusAboveSuggestions = function () {
if (this._forceResolveButton.current) {
this.setState({ selectedActionType: Suggestions_types_1.SuggestionActionType.forceResolve });
}
else if (this._searchForMoreButton.current) {
this.setState({ selectedActionType: Suggestions_types_1.SuggestionActionType.searchMore });
}
};
Suggestions.prototype.focusBelowSuggestions = function () {
if (this._searchForMoreButton.current) {
this.setState({ selectedActionType: Suggestions_types_1.SuggestionActionType.searchMore });
}
else if (this._forceResolveButton.current) {
this.setState({ selectedActionType: Suggestions_types_1.SuggestionActionType.forceResolve });
}
};
Suggestions.prototype.focusSearchForMoreButton = function () {
if (this._searchForMoreButton.current) {
this._searchForMoreButton.current.focus();
}
};
Suggestions.prototype.scrollSelected = function () {
if (this._selectedElement.current &&
this._scrollContainer.current &&
this._scrollContainer.current.scrollTo !== undefined) {
var _a = this._selectedElement.current, offsetHeight = _a.offsetHeight, offsetTop = _a.offsetTop;
var _b = this._scrollContainer.current, parentOffsetHeight = _b.offsetHeight, scrollTop = _b.scrollTop;
var isAbove = offsetTop < scrollTop;
var isBelow = offsetTop + offsetHeight > scrollTop + parentOffsetHeight;
if (isAbove) {
this._scrollContainer.current.scrollTo(0, offsetTop);
}
else if (isBelow) {
this._scrollContainer.current.scrollTo(0, offsetTop - parentOffsetHeight + offsetHeight);
}
}
};
Suggestions.prototype._renderSuggestions = function () {
var _this = this;
var _a = this.props, onRenderSuggestion = _a.onRenderSuggestion, removeSuggestionAriaLabel = _a.removeSuggestionAriaLabel, suggestionsItemClassName = _a.suggestionsItemClassName, resultsMaximumNumber = _a.resultsMaximumNumber, showRemoveButtons = _a.showRemoveButtons, removeButtonIconProps = _a.removeButtonIconProps;
var suggestions = this.props.suggestions;
var StyledTypedSuggestionsItem = StyledSuggestionsItem;
var selectedIndex = -1;
suggestions.some(function (element, index) {
if (element.selected) {
selectedIndex = index;
return true;
}
return false;
});
if (resultsMaximumNumber) {
suggestions =
selectedIndex >= resultsMaximumNumber
? suggestions.slice(selectedIndex - resultsMaximumNumber + 1, selectedIndex + 1)
: suggestions.slice(0, resultsMaximumNumber);
}
if (suggestions.length === 0) {
return null;
}
return (React.createElement("div", { className: this._classNames.suggestionsContainer, ref: this._scrollContainer, role: "presentation" }, suggestions.map(function (suggestion, index) { return (React.createElement("div", { ref: suggestion.selected ? _this._selectedElement : undefined, key: suggestion.item.key ? suggestion.item.key : index, role: "presentation" },
React.createElement(StyledTypedSuggestionsItem, { suggestionModel: suggestion, RenderSuggestion: onRenderSuggestion, onClick: _this._onClickTypedSuggestionsItem(suggestion.item, index), className: suggestionsItemClassName, showRemoveButton: showRemoveButtons, removeButtonAriaLabel: removeSuggestionAriaLabel, onRemoveItem: _this._onRemoveTypedSuggestionsItem(suggestion.item, index), id: 'sug-' + index, removeButtonIconProps: removeButtonIconProps }))); })));
};
return Suggestions;
}(React.Component));
exports.Suggestions = Suggestions;
//# sourceMappingURL=Suggestions.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,12 @@
export declare const root = "root_8c91000a";
export declare const suggestionsItem = "suggestionsItem_8c91000a";
export declare const closeButton = "closeButton_8c91000a";
export declare const suggestionsItemIsSuggested = "suggestionsItemIsSuggested_8c91000a";
export declare const itemButton = "itemButton_8c91000a";
export declare const actionButton = "actionButton_8c91000a";
export declare const buttonSelected = "buttonSelected_8c91000a";
export declare const suggestionsTitle = "suggestionsTitle_8c91000a";
export declare const suggestionsContainer = "suggestionsContainer_8c91000a";
export declare const suggestionsNone = "suggestionsNone_8c91000a";
export declare const suggestionsSpinner = "suggestionsSpinner_8c91000a";
export declare const suggestionsAvailable = "suggestionsAvailable_8c91000a";
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
import type { ISuggestionsStyleProps, ISuggestionsStyles } from './Suggestions.types';
export declare function getStyles(props: ISuggestionsStyleProps): ISuggestionsStyles;
@@ -0,0 +1,144 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStyles = getStyles;
var tslib_1 = require("tslib");
var Styling_1 = require("../../../Styling");
var GlobalClassNames = {
root: 'ms-Suggestions',
suggestionsContainer: 'ms-Suggestions-container',
title: 'ms-Suggestions-title',
forceResolveButton: 'ms-forceResolve-button',
searchForMoreButton: 'ms-SearchMore-button',
spinner: 'ms-Suggestions-spinner',
noSuggestions: 'ms-Suggestions-none',
suggestionsAvailable: 'ms-Suggestions-suggestionsAvailable',
isSelected: 'is-selected',
};
function getStyles(props) {
var _a;
var className = props.className, suggestionsClassName = props.suggestionsClassName, theme = props.theme, forceResolveButtonSelected = props.forceResolveButtonSelected, searchForMoreButtonSelected = props.searchForMoreButtonSelected;
var palette = theme.palette, semanticColors = theme.semanticColors, fonts = theme.fonts;
var classNames = (0, Styling_1.getGlobalClassNames)(GlobalClassNames, theme);
var actionButtonStyles = {
backgroundColor: 'transparent',
border: 0,
cursor: 'pointer',
margin: 0,
paddingLeft: 8,
position: 'relative',
borderTop: "1px solid ".concat(palette.neutralLight),
height: 40,
textAlign: 'left',
width: '100%',
fontSize: fonts.small.fontSize,
selectors: {
':hover': {
backgroundColor: semanticColors.menuItemBackgroundPressed,
cursor: 'pointer',
},
':focus, :active': {
backgroundColor: palette.themeLight,
},
'.ms-Button-icon': {
fontSize: fonts.mediumPlus.fontSize,
width: 25,
},
'.ms-Button-label': {
margin: '0 4px 0 9px',
},
},
};
var actionButtonSelectedStyles = {
backgroundColor: palette.themeLight,
selectors: (_a = {},
_a[Styling_1.HighContrastSelector] = tslib_1.__assign({ backgroundColor: 'Highlight', borderColor: 'Highlight', color: 'HighlightText' }, (0, Styling_1.getHighContrastNoAdjustStyle)()),
_a[':after'] = {
pointerEvents: 'none',
content: '""',
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
right: 0,
border: "1px solid ".concat(theme.semanticColors.focusBorder),
},
_a),
};
return {
root: [
classNames.root,
{
display: 'flex',
flexDirection: 'column',
minWidth: 260,
maxHeight: '100%',
},
className,
],
suggestionsContainer: [
classNames.suggestionsContainer,
{
overflowY: 'auto',
overflowX: 'hidden',
maxHeight: 300,
transform: 'translate3d(0,0,0)',
},
suggestionsClassName,
],
title: [
classNames.title,
{
padding: '0 12px',
fontSize: fonts.small.fontSize,
color: palette.themePrimary,
lineHeight: 40,
borderBottom: "1px solid ".concat(semanticColors.menuItemBackgroundPressed),
},
],
forceResolveButton: [
classNames.forceResolveButton,
actionButtonStyles,
forceResolveButtonSelected && [classNames.isSelected, actionButtonSelectedStyles],
],
searchForMoreButton: [
classNames.searchForMoreButton,
actionButtonStyles,
searchForMoreButtonSelected && [classNames.isSelected, actionButtonSelectedStyles],
],
noSuggestions: [
classNames.noSuggestions,
{
textAlign: 'center',
color: palette.neutralSecondary,
fontSize: fonts.small.fontSize,
lineHeight: 30,
},
],
suggestionsAvailable: [classNames.suggestionsAvailable, Styling_1.hiddenContentStyle],
subComponentStyles: {
spinner: {
root: [
classNames.spinner,
{
margin: '5px 0',
paddingLeft: 14,
textAlign: 'left',
whiteSpace: 'nowrap',
lineHeight: 20,
fontSize: fonts.small.fontSize,
},
],
circle: {
display: 'inline-block',
verticalAlign: 'middle',
},
label: {
display: 'inline-block',
verticalAlign: 'middle',
margin: '0 10px 0 16px',
},
},
},
};
}
//# sourceMappingURL=Suggestions.styles.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,264 @@
import * as React from 'react';
import { KeyCodes } from '../../../Utilities';
import type { IReactProps, IRefObject, IRenderFunction, IStyleFunctionOrObject } from '../../../Utilities';
import type { IPersonaProps } from '../../Persona/Persona.types';
import type { IStyle, ITheme } from '../../../Styling';
import type { ISpinnerStyleProps } from '../../Spinner/Spinner.types';
import type { ISuggestionItemProps } from './SuggestionsItem.types';
import { IIconProps } from '../../Icon/Icon.types';
import type { JSXElement } from '@fluentui/utilities';
/**
* Suggestions component.
* {@docCategory Pickers}
*/
export interface ISuggestions<T> {
/** Execute the action selected. Can be SearchMore or ForceResolve actions. */
executeSelectedAction: () => void;
/** Focus on the ForceResolve action above the suggestions. If not available then focus on SearchMore action. */
focusAboveSuggestions: () => void;
/** Focus on the SearchMore action below the suggestions. If not available then focus on ForceResolve action. */
focusBelowSuggestions: () => void;
/** Focus the SearchMore action button. */
focusSearchForMoreButton: () => void;
/** Whether it has any suggested actions like ForceResolve or SearchMore. */
hasSuggestedAction: () => boolean;
/** Whether any of the suggested actions (ForceResolve or SearchMore) is selected. */
hasSuggestedActionSelected: () => boolean;
/** Returns true if the event was handled, false otherwise. */
tryHandleKeyDown: (keyCode: number, currentSuggestionIndex: number) => boolean;
}
/**
* Suggestions props interface. Refers to the entire container holding all the suggestions.
* Type T is the type of the items that are displayed.
* {@docCategory Pickers}
*/
export interface ISuggestionsProps<T> extends IReactProps<any> {
/**
* Optional callback to access the ISuggestions interface. Use this instead of ref for accessing
* the public methods and properties of the component.
*/
componentRef?: IRefObject<ISuggestions<T>>;
/**
* How the suggestion should look in the suggestion list.
*/
onRenderSuggestion: (props: T, suggestionItemProps: ISuggestionItemProps<T>) => JSXElement;
/**
* What should occur when a suggestion is clicked
*/
onSuggestionClick: (ev?: React.MouseEvent<HTMLElement>, item?: any, index?: number) => void;
/**
* The list of Suggestions that will be displayed
*/
suggestions: ISuggestionModel<T>[];
/**
* How the "no result found" should look in the suggestion list.
*/
onRenderNoResultFound?: IRenderFunction<void>;
/**
* The text that appears at the top of the suggestions list.
*/
suggestionsHeaderText?: string;
/**
* The text that should appear at the top of the most recently used box.
*/
mostRecentlyUsedHeaderText?: string;
/**
* The icon that appears indicating to the user that they can search for more results.
*/
searchForMoreIcon?: IIconProps;
/**
* The text that appears indicating to the user that they can search for more results.
*/
searchForMoreText?: string;
/**
* The callback that should be called when the user attempts to get more results
*/
onGetMoreResults?: () => void;
/**
* The text that appears indicating to the use to force resolve the input
*/
forceResolveText?: string;
/**
* The callback that should be called to see if the force resolve command should be shown
*/
showForceResolve?: () => boolean;
/**
* The callback that should be called when the user attempts to use the input text as as item
*/
createGenericItem?: () => void;
/**
* The CSS className of the suggestions root.
*/
className?: string;
/**
* The CSS className of the suggestions list
*/
suggestionsClassName?: string;
/**
* The text that should appear if there is a search error.
*
* @deprecated Use noResultsFoundText instead.
*/
searchErrorText?: string;
/**
* The text that should appear if no results are found when searching.
*/
noResultsFoundText?: string;
/**
* The className of the suggestion item.
*/
suggestionsItemClassName?: string;
/**
* Used to indicate whether or not the user can request more suggestions.
* Dictates whether or not the searchForMore button is displayed.
*/
moreSuggestionsAvailable?: boolean;
/**
* Used to indicate whether or not the suggestions are loading.
*/
isLoading?: boolean;
/**
* Used to indicate whether or not the suggestions are taking an extended amount of time to load.
*/
isExtendedLoading?: boolean;
/**
* Used to indicate whether or not the component is searching for more results.
*/
isSearching?: boolean;
/**
* The text to display while the results are loading.
*/
loadingText?: string;
/**
* The text to display while searching for more results in a limited suggestions list.
*/
searchingText?: string;
/**
* Indicates if a short list of recent suggestions should be shown.
*/
isMostRecentlyUsedVisible?: boolean;
/**
* Function to fire when one of the optional remove buttons on a suggestion is clicked.
*
* TODO (adjective-object) remove IPersonaprops before the next major version bump
*/
onSuggestionRemove?: (ev?: React.MouseEvent<HTMLElement>, item?: T | IPersonaProps, index?: number) => void;
/**
* Indicates if the text in resultsFooter or resultsFooterFull should be shown at the end of the suggestion list.
* @defaultvalue true
*/
isResultsFooterVisible?: boolean;
/**
* Maximum number of suggestions to show in the full suggestion list.
*/
resultsMaximumNumber?: number;
/**
* A renderer that adds an element at the end of the suggestions list it has more items than resultsMaximumNumber.
* This should not include interactive elements as the footer is not focusable.
*/
resultsFooterFull?: (props: ISuggestionsProps<T>) => JSXElement;
/**
* A renderer that adds an element at the end of the suggestions list it has fewer items than resultsMaximumNumber.
* This should not include interactive elements as the footer is not focusable.
*/
resultsFooter?: (props: ISuggestionsProps<T>) => JSXElement;
/**
* Indicates whether to show a button with each suggestion to remove that suggestion.
*/
showRemoveButtons?: boolean;
/**
* Screen reader message to read when there are suggestions available.
*/
suggestionsAvailableAlertText?: string;
/**
* A function that resets focus to the expected item in the suggestion list
*/
refocusSuggestions?: (keyCode: KeyCodes) => void;
/**
* An ARIA label for the container that is the parent of the suggestions.
*/
suggestionsContainerAriaLabel?: string;
/**
* An ARIA label to use for the buttons to remove individual suggestions.
*/
removeSuggestionAriaLabel?: string;
/**
* The string that will be used as the suggestionsListId.
* Will be used by the BasePicker to keep track of the list for aria.
*/
suggestionsListId?: string;
/** Call to provide customized styling that will layer on top of the variant rules. */
styles?: IStyleFunctionOrObject<any, any>;
/** Theme provided by High-Order Component. */
theme?: ITheme;
/**
* Props for the icon used in the item's remove button.
* @defaultvalue `{ iconName:'Cancel' }`
*/
removeButtonIconProps?: IIconProps;
}
/**
* The props needed to construct Suggestions styles.
* {@docCategory Pickers}
*/
export type ISuggestionsStyleProps = Required<Pick<ISuggestionsProps<any>, 'theme'>> & Pick<ISuggestionsProps<any>, 'className' | 'suggestionsClassName'> & {
/** Whether the forceResolve actionButton is selected. */
forceResolveButtonSelected?: boolean;
/** Whether the searchForMore actionButton is selected. */
searchForMoreButtonSelected?: boolean;
};
/**
* Represents the stylable areas of the Suggestions.
* {@docCategory Pickers}
*/
export interface ISuggestionsStyles {
/** Root element of the suggestions outer wrapper. */
root: IStyle;
/** Refers to the suggestions container. */
suggestionsContainer: IStyle;
/** Refers to the title rendered for suggestions container header and/or footer (if provided). */
title: IStyle;
/** Refers to the 'Force resolve' actionButton. */
forceResolveButton: IStyle;
/** Refers to the 'Search for more' actionButton. */
searchForMoreButton: IStyle;
/** Refers to the text rendered when no suggestions are found. */
noSuggestions: IStyle;
/** Refers to the text displaying if more suggestions available. */
suggestionsAvailable: IStyle;
/** SubComponents (Spinner) styles. */
subComponentStyles: ISuggestionsSubComponentStyles;
}
/**
* Styles interface of the SubComponents rendered within PeoplePickerItemSelected.
* {@docCategory Pickers}
*/
export interface ISuggestionsSubComponentStyles {
/** Refers to the Spinner rendered within the Suggestions when searching or loading suggestions. */
spinner: IStyleFunctionOrObject<ISpinnerStyleProps, any>;
}
/**
* SuggestionModel interface.
* Type T is the type of the item that is suggested (Persona, Tag or any other custom picker).
* {@docCategory Pickers}
*/
export interface ISuggestionModel<T> {
/** The suggested item of the type T */
item: T;
/** Whether the suggested item is selected or not. */
selected: boolean;
/** Aria-label string for each suggested item. */
ariaLabel?: string;
}
/**
* Enum to help identify which suggestions action button is selected.
* {@docCategory Pickers}
*/
export declare enum SuggestionActionType {
/** None of the actions is selected. */
none = 0,
/** ForceResolve action is selected. */
forceResolve = 1,
/** SearchMore action is selected. */
searchMore = 2
}
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuggestionActionType = void 0;
/**
* Enum to help identify which suggestions action button is selected.
* {@docCategory Pickers}
*/
var SuggestionActionType;
(function (SuggestionActionType) {
/** None of the actions is selected. */
SuggestionActionType[SuggestionActionType["none"] = 0] = "none";
/** ForceResolve action is selected. */
SuggestionActionType[SuggestionActionType["forceResolve"] = 1] = "forceResolve";
/** SearchMore action is selected. */
SuggestionActionType[SuggestionActionType["searchMore"] = 2] = "searchMore";
})(SuggestionActionType || (exports.SuggestionActionType = SuggestionActionType = {}));
//# sourceMappingURL=Suggestions.types.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,30 @@
import type { ISuggestionModel } from './Suggestions.types';
/**
* {@docCategory Pickers}
*/
export declare class SuggestionsController<T> {
currentIndex: number;
currentSuggestion: ISuggestionModel<T> | undefined;
suggestions: ISuggestionModel<T>[];
constructor();
updateSuggestions(newSuggestions: T[], selectedIndex?: number, maxCount?: number): void;
/**
* Increments the suggestion index and gets the next suggestion in the list.
*/
nextSuggestion(): boolean;
/**
* Decrements the suggestion index and gets the previous suggestion in the list.
*/
previousSuggestion(): boolean;
getSuggestions(): ISuggestionModel<T>[];
getCurrentItem(): ISuggestionModel<T>;
getSuggestionAtIndex(index: number): ISuggestionModel<T>;
hasSelectedSuggestion(): boolean;
removeSuggestion(index: number): void;
createGenericSuggestion(itemToConvert: ISuggestionModel<T> | T): void;
convertSuggestionsToSuggestionItems(suggestions: Array<ISuggestionModel<T> | T>): ISuggestionModel<T>[];
deselectAllSuggestions(): void;
setSelectedSuggestion(index: number): void;
private _isSuggestionModel;
private _ensureSuggestionModel;
}
@@ -0,0 +1,129 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuggestionsController = void 0;
/**
* {@docCategory Pickers}
*/
var SuggestionsController = /** @class */ (function () {
function SuggestionsController() {
var _this = this;
this._isSuggestionModel = function (value) {
return value.item !== undefined;
};
this._ensureSuggestionModel = function (suggestion) {
if (_this._isSuggestionModel(suggestion)) {
return suggestion;
}
else {
return {
item: suggestion,
selected: false,
ariaLabel: suggestion.ariaLabel,
};
}
};
this.suggestions = [];
this.currentIndex = -1;
}
SuggestionsController.prototype.updateSuggestions = function (newSuggestions, selectedIndex, maxCount) {
if (newSuggestions && newSuggestions.length > 0) {
if (maxCount && newSuggestions.length > maxCount) {
var startIndex = selectedIndex && selectedIndex > maxCount ? selectedIndex + 1 - maxCount : 0;
newSuggestions = newSuggestions.slice(startIndex, startIndex + maxCount - 1);
}
this.suggestions = this.convertSuggestionsToSuggestionItems(newSuggestions);
this.currentIndex = selectedIndex ? selectedIndex : 0;
if (selectedIndex === -1) {
this.currentSuggestion = undefined;
}
else if (selectedIndex !== undefined) {
this.suggestions[selectedIndex].selected = true;
this.currentSuggestion = this.suggestions[selectedIndex];
}
}
else {
this.suggestions = [];
this.currentIndex = -1;
this.currentSuggestion = undefined;
}
};
/**
* Increments the suggestion index and gets the next suggestion in the list.
*/
SuggestionsController.prototype.nextSuggestion = function () {
if (this.suggestions && this.suggestions.length) {
if (this.currentIndex < this.suggestions.length - 1) {
this.setSelectedSuggestion(this.currentIndex + 1);
return true;
}
else if (this.currentIndex === this.suggestions.length - 1) {
this.setSelectedSuggestion(0);
return true;
}
}
return false;
};
/**
* Decrements the suggestion index and gets the previous suggestion in the list.
*/
SuggestionsController.prototype.previousSuggestion = function () {
if (this.suggestions && this.suggestions.length) {
if (this.currentIndex > 0) {
this.setSelectedSuggestion(this.currentIndex - 1);
return true;
}
else if (this.currentIndex === 0) {
this.setSelectedSuggestion(this.suggestions.length - 1);
return true;
}
}
return false;
};
SuggestionsController.prototype.getSuggestions = function () {
return this.suggestions;
};
SuggestionsController.prototype.getCurrentItem = function () {
return this.currentSuggestion;
};
SuggestionsController.prototype.getSuggestionAtIndex = function (index) {
return this.suggestions[index];
};
SuggestionsController.prototype.hasSelectedSuggestion = function () {
return this.currentSuggestion ? true : false;
};
SuggestionsController.prototype.removeSuggestion = function (index) {
this.suggestions.splice(index, 1);
};
SuggestionsController.prototype.createGenericSuggestion = function (itemToConvert) {
var itemToAdd = this.convertSuggestionsToSuggestionItems([itemToConvert])[0];
this.currentSuggestion = itemToAdd;
};
SuggestionsController.prototype.convertSuggestionsToSuggestionItems = function (suggestions) {
return Array.isArray(suggestions) ? suggestions.map(this._ensureSuggestionModel) : [];
};
SuggestionsController.prototype.deselectAllSuggestions = function () {
if (this.currentIndex > -1) {
this.suggestions[this.currentIndex].selected = false;
this.currentIndex = -1;
}
};
SuggestionsController.prototype.setSelectedSuggestion = function (index) {
if (index > this.suggestions.length - 1 || index < 0) {
this.currentIndex = 0;
this.currentSuggestion.selected = false;
this.currentSuggestion = this.suggestions[0];
this.currentSuggestion.selected = true;
}
else {
if (this.currentIndex > -1) {
this.suggestions[this.currentIndex].selected = false;
}
this.suggestions[index].selected = true;
this.currentIndex = index;
this.currentSuggestion = this.suggestions[index];
}
};
return SuggestionsController;
}());
exports.SuggestionsController = SuggestionsController;
//# sourceMappingURL=SuggestionsController.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
import * as React from 'react';
import type { ISuggestionItemProps } from './SuggestionsItem.types';
import type { JSXElement } from '@fluentui/utilities';
/**
* {@docCategory Pickers}
*/
export declare class SuggestionsItem<T> extends React.Component<ISuggestionItemProps<T>, {}> {
constructor(props: ISuggestionItemProps<T>);
render(): JSXElement;
}
@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuggestionsItem = void 0;
var tslib_1 = require("tslib");
var React = require("react");
var Utilities_1 = require("../../../Utilities");
var Button_1 = require("../../../Button");
var stylesImport = require("./Suggestions.scss");
var legacyStyles = stylesImport;
var getClassNames = (0, Utilities_1.classNamesFunction)();
/**
* {@docCategory Pickers}
*/
var SuggestionsItem = /** @class */ (function (_super) {
tslib_1.__extends(SuggestionsItem, _super);
function SuggestionsItem(props) {
var _this = _super.call(this, props) || this;
(0, Utilities_1.initializeComponentRef)(_this);
return _this;
}
SuggestionsItem.prototype.render = function () {
var _a;
var _b = this.props, suggestionModel = _b.suggestionModel, RenderSuggestion = _b.RenderSuggestion, onClick = _b.onClick, className = _b.className, id = _b.id, onRemoveItem = _b.onRemoveItem, isSelectedOverride = _b.isSelectedOverride, removeButtonAriaLabel = _b.removeButtonAriaLabel, styles = _b.styles, theme = _b.theme, removeButtonIconProps = _b.removeButtonIconProps;
// TODO
// Clean this up by leaving only the first part after removing support for SASS.
// Currently we can not remove the SASS styles from SuggestionsItem class because it
// might be used by consumers separately from pickers extending from BasePicker
// and have not used the new 'styles' prop. Because it's expecting a type parameter,
// we can not use the 'styled' function without adding some helpers which can break
// downstream consumers who did not use the new helpers.
// We check for 'styles' prop which is going to be injected by the 'styled' HOC
// in Suggestions when the typed SuggestionsItem class is ready to be rendered. If the
// check passes we can use the CSS-in-JS styles. If the check fails (ex: custom picker),
// then we just use the old SASS styles instead.
var classNames = styles
? getClassNames(styles, {
theme: theme,
className: className,
suggested: suggestionModel.selected || isSelectedOverride,
})
: {
root: (0, Utilities_1.css)('ms-Suggestions-item', legacyStyles.suggestionsItem, (_a = {},
_a['is-suggested ' + legacyStyles.suggestionsItemIsSuggested] = suggestionModel.selected || isSelectedOverride,
_a), className),
itemButton: (0, Utilities_1.css)('ms-Suggestions-itemButton', legacyStyles.itemButton),
closeButton: (0, Utilities_1.css)('ms-Suggestions-closeButton', legacyStyles.closeButton),
};
return (React.createElement("div", { className: classNames.root, role: "presentation" },
React.createElement(Button_1.CommandButton, { onClick: onClick, className: classNames.itemButton, id: id, "aria-selected": suggestionModel.selected, role: "option", "aria-label": suggestionModel.ariaLabel }, RenderSuggestion(suggestionModel.item, this.props)),
this.props.showRemoveButton ? (React.createElement(Button_1.IconButton, { iconProps: removeButtonIconProps !== null && removeButtonIconProps !== void 0 ? removeButtonIconProps : { iconName: 'Cancel' }, styles: { icon: { fontSize: '12px' } }, title: removeButtonAriaLabel, ariaLabel: removeButtonAriaLabel, onClick: onRemoveItem, className: classNames.closeButton })) : null));
};
return SuggestionsItem;
}(React.Component));
exports.SuggestionsItem = SuggestionsItem;
//# sourceMappingURL=SuggestionsItem.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
import type { ISuggestionsItemStyleProps, ISuggestionsItemStyles } from './SuggestionsItem.types';
export declare const SuggestionsItemGlobalClassNames: {
root: string;
itemButton: string;
closeButton: string;
isSuggested: string;
};
export declare function getStyles(props: ISuggestionsItemStyleProps): ISuggestionsItemStyles;
@@ -0,0 +1,140 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuggestionsItemGlobalClassNames = void 0;
exports.getStyles = getStyles;
var tslib_1 = require("tslib");
var Styling_1 = require("../../../Styling");
var Utilities_1 = require("../../../Utilities");
exports.SuggestionsItemGlobalClassNames = {
root: 'ms-Suggestions-item',
itemButton: 'ms-Suggestions-itemButton',
closeButton: 'ms-Suggestions-closeButton',
isSuggested: 'is-suggested',
};
function getStyles(props) {
var _a, _b, _c, _d, _e, _f;
var className = props.className, theme = props.theme, suggested = props.suggested;
var palette = theme.palette, semanticColors = theme.semanticColors;
var classNames = (0, Styling_1.getGlobalClassNames)(exports.SuggestionsItemGlobalClassNames, theme);
return {
root: [
classNames.root,
{
display: 'flex',
alignItems: 'stretch',
boxSizing: 'border-box',
width: '100%',
position: 'relative',
selectors: {
'&:hover': {
background: semanticColors.menuItemBackgroundHovered,
},
'&:hover .ms-Suggestions-closeButton': {
display: 'block',
},
},
},
suggested && {
selectors: (_a = {},
_a[".".concat(Utilities_1.IsFocusVisibleClassName, " &, :host(.").concat(Utilities_1.IsFocusVisibleClassName, ") &")] = {
selectors: (_b = {},
_b[".".concat(classNames.closeButton)] = {
display: 'block',
background: semanticColors.menuItemBackgroundPressed,
},
_b),
},
_a[':after'] = {
pointerEvents: 'none',
content: '""',
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
right: 0,
border: "1px solid ".concat(theme.semanticColors.focusBorder),
},
_a),
},
className,
],
itemButton: [
classNames.itemButton,
{
justifyContent: 'flex-start',
width: '100%',
padding: 0,
border: 'none',
height: '100%',
// Force the item button to be collapsible so it can always shrink
// to accommodate the close button as a peer in its flex container.
minWidth: 0,
// Required for IE11 to truncate the component.
overflow: 'hidden',
selectors: (_c = {},
_c[Styling_1.HighContrastSelector] = {
color: 'WindowText',
selectors: {
':hover': tslib_1.__assign({ background: 'Highlight', color: 'HighlightText' }, (0, Styling_1.getHighContrastNoAdjustStyle)()),
},
},
_c[':hover'] = {
color: semanticColors.menuItemTextHovered,
},
_c['.ms-Button-flexContainer'] = {
width: '100%',
},
_c),
},
suggested && [
classNames.isSuggested,
{
background: semanticColors.menuItemBackgroundPressed,
selectors: (_d = {
':hover': {
background: semanticColors.menuDivider,
}
},
_d[Styling_1.HighContrastSelector] = tslib_1.__assign({ background: 'Highlight', color: 'HighlightText' }, (0, Styling_1.getHighContrastNoAdjustStyle)()),
_d),
},
],
],
closeButton: [
classNames.closeButton,
{
display: 'none',
color: palette.neutralSecondary,
padding: '0 4px',
height: 'auto',
width: 32,
selectors: (_e = {
':hover, :active': {
background: palette.neutralTertiaryAlt,
color: palette.neutralDark,
}
},
_e[Styling_1.HighContrastSelector] = {
color: 'WindowText',
},
_e),
},
suggested && (_f = {},
_f[".".concat(Utilities_1.IsFocusVisibleClassName, " &, :host(.").concat(Utilities_1.IsFocusVisibleClassName, ") &")] = {
selectors: {
':hover, :active': {
background: palette.neutralTertiary,
},
},
},
_f.selectors = {
':hover, :active': {
background: palette.neutralTertiary,
color: palette.neutralPrimary,
},
},
_f),
],
};
}
//# sourceMappingURL=SuggestionsItem.styles.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,73 @@
import * as React from 'react';
import type { IStyle, ITheme } from '../../../Styling';
import type { IRefObject, IStyleFunctionOrObject } from '../../../Utilities';
import type { ISuggestionModel } from './Suggestions.types';
import { IIconProps } from '../../Icon/Icon.types';
import type { JSXElement } from '@fluentui/utilities';
/**
* SuggestionItem component.
* {@docCategory Pickers}
*/
export interface ISuggestionsItem {
}
/**
* Suggestion item props. Refers to the each individual suggested items rendered within Suggestions callout.
* Type T is the type of the item that is displayed.
* {@docCategory Pickers}
*/
export interface ISuggestionItemProps<T> {
/**
* Optional callback to access the ISuggestionItem interface. Use this instead of ref for accessing
* the public methods and properties of the component.
*/
componentRef?: IRefObject<ISuggestionsItem>;
/** Individual suggestion object containing its properties. */
suggestionModel: ISuggestionModel<T>;
/** Optional renderer to override the default one for each type of picker. */
RenderSuggestion: (item: T, suggestionItemProps: ISuggestionItemProps<T>) => JSXElement;
/** Callback for when the user clicks on the suggestion. */
onClick: (ev: React.MouseEvent<HTMLButtonElement>) => void;
/** Callback for when the item is removed from the array of suggested items. */
onRemoveItem: (ev: React.MouseEvent<HTMLButtonElement>) => void;
/** Optional className for the root element of the suggestion item. */
className?: string;
/** Unique id of the suggested item. */
id?: string;
/** Whether the remove button should be rendered or not. */
showRemoveButton?: boolean;
/** An override for the 'selected' property of the SuggestionModel. */
isSelectedOverride?: boolean;
/**
* The ARIA label for the button to remove the suggestion from the list.
*/
removeButtonAriaLabel?: string;
/** Call to provide customized styling that will layer on top of the variant rules. */
styles?: IStyleFunctionOrObject<ISuggestionsItemStyleProps, ISuggestionsItemStyles>;
/** Theme provided by High-Order Component. */
theme?: ITheme;
/**
* Props for the icon used in the item's remove button.
* @defaultvalue `{ iconName:'Cancel' }`
*/
removeButtonIconProps?: IIconProps;
}
/**
* The props needed to construct SuggestionItem styles.
* {@docCategory Pickers}
*/
export type ISuggestionsItemStyleProps = Required<Pick<ISuggestionItemProps<any>, 'theme'>> & Pick<ISuggestionItemProps<any>, 'className'> & {
/** Whether the suggestion item is selected or not. */
suggested?: boolean;
};
/**
* Represents the stylable areas of the SuggestionItem.
* {@docCategory Pickers}
*/
export interface ISuggestionsItemStyles {
/** Root element of the suggested item. */
root: IStyle;
/** Refers to the CommandButton holding the content of the suggested item. */
itemButton: IStyle;
/** Refers to the remove button in case it's rendered. */
closeButton: IStyle;
}
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=SuggestionsItem.types.js.map
@@ -0,0 +1 @@
{"version":3,"file":"SuggestionsItem.types.js","sourceRoot":"../src/","sources":["components/pickers/Suggestions/SuggestionsItem.types.ts"],"names":[],"mappings":"","sourcesContent":["import * as React from 'react';\nimport type { IStyle, ITheme } from '../../../Styling';\nimport type { IRefObject, IStyleFunctionOrObject } from '../../../Utilities';\nimport type { ISuggestionModel } from './Suggestions.types';\nimport { IIconProps } from '../../Icon/Icon.types';\nimport type { JSXElement } from '@fluentui/utilities';\n/**\n * SuggestionItem component.\n * {@docCategory Pickers}\n */\nexport interface ISuggestionsItem {}\n\n/**\n * Suggestion item props. Refers to the each individual suggested items rendered within Suggestions callout.\n * Type T is the type of the item that is displayed.\n * {@docCategory Pickers}\n */\nexport interface ISuggestionItemProps<T> {\n /**\n * Optional callback to access the ISuggestionItem interface. Use this instead of ref for accessing\n * the public methods and properties of the component.\n */\n componentRef?: IRefObject<ISuggestionsItem>;\n\n /** Individual suggestion object containing its properties. */\n suggestionModel: ISuggestionModel<T>;\n\n /** Optional renderer to override the default one for each type of picker. */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n RenderSuggestion: (item: T, suggestionItemProps: ISuggestionItemProps<T>) => JSXElement;\n\n /** Callback for when the user clicks on the suggestion. */\n onClick: (ev: React.MouseEvent<HTMLButtonElement>) => void;\n\n /** Callback for when the item is removed from the array of suggested items. */\n onRemoveItem: (ev: React.MouseEvent<HTMLButtonElement>) => void;\n\n /** Optional className for the root element of the suggestion item. */\n className?: string;\n\n /** Unique id of the suggested item. */\n id?: string;\n\n /** Whether the remove button should be rendered or not. */\n showRemoveButton?: boolean;\n\n /** An override for the 'selected' property of the SuggestionModel. */\n isSelectedOverride?: boolean;\n\n /**\n * The ARIA label for the button to remove the suggestion from the list.\n */\n removeButtonAriaLabel?: string;\n\n /** Call to provide customized styling that will layer on top of the variant rules. */\n styles?: IStyleFunctionOrObject<ISuggestionsItemStyleProps, ISuggestionsItemStyles>;\n\n /** Theme provided by High-Order Component. */\n theme?: ITheme;\n\n /**\n * Props for the icon used in the item's remove button.\n * @defaultvalue `{ iconName:'Cancel' }`\n */\n removeButtonIconProps?: IIconProps;\n}\n\n/**\n * The props needed to construct SuggestionItem styles.\n * {@docCategory Pickers}\n */\nexport type ISuggestionsItemStyleProps = Required<Pick<ISuggestionItemProps<any>, 'theme'>> &\n Pick<ISuggestionItemProps<any>, 'className'> & {\n /** Whether the suggestion item is selected or not. */\n suggested?: boolean;\n };\n\n/**\n * Represents the stylable areas of the SuggestionItem.\n * {@docCategory Pickers}\n */\nexport interface ISuggestionsItemStyles {\n /** Root element of the suggested item. */\n root: IStyle;\n\n /** Refers to the CommandButton holding the content of the suggested item. */\n itemButton: IStyle;\n\n /** Refers to the remove button in case it's rendered. */\n closeButton: IStyle;\n}\n"]}