IconPicker
This commit is contained in:
parent
4c9a148dbf
commit
f6ec1e1639
|
@ -20,3 +20,4 @@ npm run serve
|
|||
npm install @pnp/spfx-property-controls --save --save-exact
|
||||
npm run serve
|
||||
gulp clean; gulp build; gulp bundle --ship; gulp package-solution --ship;
|
||||
npm run serve
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
.autocomplete {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
import * as React from 'react';
|
||||
// import styles from './Autocomplete.module.scss';
|
||||
import { TextField, ITextFieldProps, Callout, DirectionalHint, ITextField } from 'office-ui-fabric-react';
|
||||
import { isNullOrEmpty, cssClasses, getDeepOrDefault, isFunction } from '@spfxappdev/utility';
|
||||
|
||||
|
||||
export interface IAutocompleteProps extends Omit<ITextFieldProps, "componentRef"> {
|
||||
showSuggestionsOnFocus?: boolean;
|
||||
minValueLength?: number;
|
||||
onLoadSuggestions?(newValue: string): void;
|
||||
onRenderSuggestions?(): JSX.Element;
|
||||
}
|
||||
|
||||
interface IAutocompleteState {
|
||||
currentValue: string;
|
||||
isFlyoutVisible: boolean;
|
||||
}
|
||||
|
||||
export class Autocomplete extends React.Component<IAutocompleteProps, IAutocompleteState> {
|
||||
|
||||
public state: IAutocompleteState = {
|
||||
currentValue: isNullOrEmpty(this.props.defaultValue) ? "" : this.props.defaultValue,
|
||||
isFlyoutVisible: false,
|
||||
};
|
||||
|
||||
public static defaultProps: IAutocompleteProps = {
|
||||
showSuggestionsOnFocus: false,
|
||||
minValueLength: 3
|
||||
};
|
||||
|
||||
private textFieldReference: ITextField = null;
|
||||
|
||||
private textFieldDomElement: HTMLInputElement = null;
|
||||
|
||||
private userIsTyping: boolean = false;
|
||||
|
||||
private lastValue: string = "";
|
||||
|
||||
public render(): React.ReactElement<IAutocompleteProps> {
|
||||
return (<>
|
||||
<TextField {...this.props}
|
||||
className={cssClasses("styles.autocomplete", this.props.className)}
|
||||
componentRef={(input: ITextField) => {
|
||||
this.textFieldReference = input;
|
||||
this.textFieldDomElement = getDeepOrDefault<HTMLInputElement>(input, "_textElement.current", null);
|
||||
|
||||
|
||||
}}
|
||||
onFocus={(ev: any) => {
|
||||
if(this.props.showSuggestionsOnFocus) {
|
||||
this.handleSuggestionListVisibility();
|
||||
}
|
||||
|
||||
if(isFunction(this.props.onFocus)) {
|
||||
this.props.onFocus(ev);
|
||||
}
|
||||
}}
|
||||
onBlur={(ev: any) => {
|
||||
|
||||
this.onTextFieldBlur();
|
||||
|
||||
if(isFunction(this.props.onBlur)) {
|
||||
this.props.onBlur(ev);
|
||||
}
|
||||
}}
|
||||
onChange={(ev: any, newValue: string) => {
|
||||
|
||||
this.onValueChanged(newValue);
|
||||
|
||||
if(isFunction(this.props.onChange)) {
|
||||
this.props.onChange(ev, newValue);
|
||||
}
|
||||
}}
|
||||
defaultValue={this.state.currentValue}
|
||||
/>
|
||||
|
||||
{this.renderSuggesstionsFlyout()}
|
||||
</>);
|
||||
}
|
||||
|
||||
private renderSuggesstionsFlyout(): JSX.Element {
|
||||
return (<Callout
|
||||
hidden={!this.state.isFlyoutVisible}
|
||||
className={"styles.bagHeaderMegamenu"}
|
||||
directionalHintFixed={true}
|
||||
gapSpace={0}
|
||||
// calloutWidth={window.outerWidth}
|
||||
isBeakVisible={false}
|
||||
target={this.textFieldDomElement}
|
||||
onDismiss={() => {
|
||||
this.hideSuggesstionsFlyout();
|
||||
}}
|
||||
preventDismissOnScroll={true}
|
||||
directionalHint={DirectionalHint.bottomCenter}>
|
||||
{isFunction(this.props.onRenderSuggestions) && this.props.onRenderSuggestions()}
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
private onValueChanged(newValue: string): void {
|
||||
this.userIsTyping = true;
|
||||
|
||||
this.state.currentValue = newValue;
|
||||
this.setState({
|
||||
currentValue: newValue
|
||||
});
|
||||
|
||||
this.handleSuggestionListVisibility();
|
||||
}
|
||||
|
||||
private onTextFieldBlur(): void {
|
||||
this.userIsTyping = false;
|
||||
|
||||
}
|
||||
|
||||
private handleSuggestionListVisibility(): void {
|
||||
let val = this.state.currentValue;
|
||||
|
||||
if(isNullOrEmpty(val)) {
|
||||
this.hideSuggesstionsFlyout();
|
||||
return;
|
||||
}
|
||||
|
||||
if(val.length < this.props.minValueLength) {
|
||||
this.hideSuggesstionsFlyout();
|
||||
return;
|
||||
}
|
||||
|
||||
let valueWasChanged = false;
|
||||
|
||||
if(!val.Equals(this.lastValue)) {
|
||||
this.userIsTyping = false;
|
||||
valueWasChanged = true;
|
||||
}
|
||||
|
||||
if(!valueWasChanged) {
|
||||
this.showSuggesstionsFlyout();
|
||||
return;
|
||||
}
|
||||
|
||||
window.setTimeout(() => {
|
||||
if(this.userIsTyping) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.showSuggesstionsFlyout();
|
||||
|
||||
if(isFunction(this.props.onLoadSuggestions)) {
|
||||
this.props.onLoadSuggestions(this.state.currentValue);
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
private hideSuggesstionsFlyout(): void {
|
||||
this.setState({
|
||||
isFlyoutVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
private showSuggesstionsFlyout(): void {
|
||||
this.setState({
|
||||
isFlyoutVisible: true
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
.iconpicker {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import * as React from 'react';
|
||||
import styles from './IconPicker.module.scss';
|
||||
import { ITextFieldProps, Icon } from 'office-ui-fabric-react';
|
||||
import { allIcons } from './availableIcons';
|
||||
import { isNullOrEmpty, cssClasses } from '@spfxappdev/utility';
|
||||
import { Autocomplete, IAutocompleteProps } from '@src/components/autocomplete/Autocomplete';
|
||||
|
||||
|
||||
export interface IIconPickerProps extends Omit<ITextFieldProps, "componentRef">, IAutocompleteProps {
|
||||
enableDialogPicker?: boolean;
|
||||
dialogPickerIconName?: string;
|
||||
}
|
||||
|
||||
interface IIconPickerState {
|
||||
currentValue: string;
|
||||
isFlyoutVisible: boolean;
|
||||
}
|
||||
|
||||
export class IconPicker extends React.Component<IIconPickerProps, IIconPickerState> {
|
||||
|
||||
public state: IIconPickerState = {
|
||||
currentValue: isNullOrEmpty(this.props.defaultValue) ? "" : this.props.defaultValue,
|
||||
isFlyoutVisible: false,
|
||||
};
|
||||
|
||||
public static defaultProps: IIconPickerProps = {
|
||||
dialogPickerIconName: "GroupedList",
|
||||
enableDialogPicker: true,
|
||||
showSuggestionsOnFocus: false,
|
||||
minValueLength: 0
|
||||
};
|
||||
|
||||
public render(): React.ReactElement<IIconPickerProps> {
|
||||
return (<>
|
||||
<Autocomplete {...this.props}
|
||||
className={cssClasses(styles.iconpicker)}
|
||||
defaultValue={this.state.currentValue}
|
||||
onLoadSuggestions={(newValue: string) => {
|
||||
this.state.currentValue = newValue;
|
||||
|
||||
this.setState({
|
||||
currentValue: newValue,
|
||||
isFlyoutVisible: true
|
||||
});
|
||||
}}
|
||||
onRenderSuggestions={() => {
|
||||
return this.renderSuggesstionsFlyout();
|
||||
}}
|
||||
iconProps={{
|
||||
iconName: this.state.currentValue
|
||||
}} />
|
||||
|
||||
|
||||
</>);
|
||||
}
|
||||
|
||||
private renderSuggesstionsFlyout(): JSX.Element {
|
||||
|
||||
return (<>
|
||||
<ul>
|
||||
{allIcons.Where(icon => icon.StartsWith(this.state.currentValue)).map((iconName: string): JSX.Element => {
|
||||
return (<li key={`Icon_${iconName}`}><Icon iconName={iconName} /></li>);
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -11,6 +11,7 @@ import '@spfxappdev/utility/lib/extensions/ArrayExtensions';
|
|||
import { IconButton } from '@microsoft/office-ui-fabric-react-bundle';
|
||||
import { MarkerIcon } from './MarkerIcon';
|
||||
import * as strings from 'MapWebPartStrings';
|
||||
import { IconPicker } from '@src/components/iconPicker/IconPicker';
|
||||
|
||||
export interface IManageMarkerCategoriesDialogProps {
|
||||
markerCategories: IMarkerCategory[];
|
||||
|
@ -163,7 +164,7 @@ export default class ManageMarkerCategoriesDialog extends React.Component<IManag
|
|||
/>
|
||||
</div>
|
||||
<div className='spfxappdev-grid-col spfxappdev-sm3'>
|
||||
<TextField
|
||||
<IconPicker
|
||||
defaultValue={categoryItem.iconProperties.iconName}
|
||||
onChange={(ev: any, name: string) => {
|
||||
this.state.markerCategories[index].iconProperties.iconName = name;
|
||||
|
|
Loading…
Reference in New Issue