зеркало из
1
0
Форкнуть 0

1. Added support for Picker and Dropdown controls

This commit is contained in:
Kushal Mehrotra 2021-09-01 03:47:43 +05:30
Родитель 195bb66766
Коммит 04eff223fa
15 изменённых файлов: 574 добавлений и 18 удалений

Просмотреть файл

@ -22,6 +22,8 @@ Some of the features of the Editable Grid are:-
>- Flexibility to implement onChange callback on any cell value change (For cases like calculating summation of a column etc)
>- Length Validations during edit
>- Type Validations during edit
>- Rule-Based Cell Styling
>- In-built support for controls like TextField, Multiline-Textfield, Picker, Dropdown, Calendar
>- The component is completely Accessible
## Clone & Run
@ -141,6 +143,58 @@ This starts the project on port 3000 and you are ready to play around with the E
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.Date
},
{
key: 'payrolltype',
name: 'Payroll Type',
text: 'Payroll Type',
editable: true,
dataType: 'string',
minWidth: 150,
maxWidth: 150,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.DropDown,
dropdownValues: [
{ key: 'weekly', text: 'Weekly' },
{ key: 'biweekly', text: 'Bi-Weekly' },
{ key: 'monthly', text: 'Monthly' }
]
},
{
key: 'employmenttype',
name: 'Employment Type',
text: 'Employment Type',
editable: true,
dataType: 'string',
minWidth: 200,
maxWidth: 200,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.Picker,
pickerOptions: {
pickerTags: ['Employment Type1', 'Employment Type2', 'Employment Type3', 'Employment Type4', 'Employment Type5', 'Employment Type6', 'Employment Type7', 'Employment Type8', 'Employment Type9', 'Employment Type10', 'Employment Type11', 'Employment Type12'],
minCharLimitForSuggestions: 2,
tagsLimit: 1,
pickerDescriptionOptions: {
enabled: true,
values: [
{ key: 'Employment Type1', description: 'Employment Type1 Description'},
{ key: 'Employment Type2', description: 'Employment Type2 Description'},
{ key: 'Employment Type3', description: 'Employment Type3 Description'},
{ key: 'Employment Type4', description: 'Employment Type4 Description'},
{ key: 'Employment Type5', description: 'Employment Type5 Description'},
{ key: 'Employment Type6', description: 'Employment Type6 Description'},
{ key: 'Employment Type7', description: 'Employment Type7 Description'},
{ key: 'Employment Type8', description: 'Employment Type8 Description'},
{ key: 'Employment Type9', description: 'Employment Type9 Description'},
{ key: 'Employment Type10', description: 'Employment Type10 Description'},
{ key: 'Employment Type11', description: 'Employment Type11 Description'},
{ key: 'Employment Type12', description: 'Employment Type12 Description'},
] }
}
}
];
@ -152,7 +206,9 @@ This starts the project on port 3000 and you are ready to play around with the E
age:32,
designation:'Designation1',
salary:57000,
dateofjoining:'2010-04-01T14:57:10'
dateofjoining:'2010-04-01T14:57:10',
payrolltype: 'Weekly',
employmenttype: 'Employment Type11'
},
{
id: "2",
@ -160,7 +216,9 @@ This starts the project on port 3000 and you are ready to play around with the E
age:27,
designation:'Designation2',
salary:42000,
dateofjoining:'2014-06-09T14:57:10'
dateofjoining:'2014-06-09T14:57:10',
payrolltype: 'Monthly',
employmenttype: 'Employment Type4'
},
{
id: "3",
@ -168,7 +226,9 @@ This starts the project on port 3000 and you are ready to play around with the E
age:35,
designation:'Designation3',
salary:75000,
dateofjoining:'2005-07-02T14:57:10'
dateofjoining:'2005-07-02T14:57:10',
payrolltype: 'Weekly',
employmenttype: 'Employment Type7'
},
{
id: "4",
@ -176,7 +236,9 @@ This starts the project on port 3000 and you are ready to play around with the E
age:30,
designation:'Designation4',
salary:49000,
dateofjoining:'2019-04-01T14:57:10'
dateofjoining:'2019-04-01T14:57:10',
payrolltype: 'Bi-Weekly',
employmenttype: 'Employment Type2'
}
];
setItems(dummyData);

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "fluentui-editable-grid",
"version": "1.1.0",
"version": "1.2.0",
"license": "MIT",
"description": "Wrapper over the existing DetailsList that makes in-place editability work like a dream(among many other new features)",
"main": "dist/index.js",

Просмотреть файл

@ -4,7 +4,6 @@
import { NumberAndDateOperators, StringOperators } from "../../libs/types/cellstyleruletype";
import { IColumnConfig } from "../../libs/types/columnconfigtype";
import { EditControlType } from "../../libs/types/editcontroltype";
import { IGridItemsType } from "../../libs/types/griditemstype";
export const GridColumnConfig : IColumnConfig[] =
[
@ -96,6 +95,58 @@ export const GridColumnConfig : IColumnConfig[] =
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.Date
},
{
key: 'payrolltype',
name: 'Payroll Type',
text: 'Payroll Type',
editable: true,
dataType: 'string',
minWidth: 150,
maxWidth: 150,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.DropDown,
dropdownValues: [
{ key: 'weekly', text: 'Weekly' },
{ key: 'biweekly', text: 'Bi-Weekly' },
{ key: 'monthly', text: 'Monthly' }
]
},
{
key: 'employmenttype',
name: 'Employment Type',
text: 'Employment Type',
editable: true,
dataType: 'string',
minWidth: 200,
maxWidth: 200,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.Picker,
pickerOptions: {
pickerTags: ['Employment Type1', 'Employment Type2', 'Employment Type3', 'Employment Type4', 'Employment Type5', 'Employment Type6', 'Employment Type7', 'Employment Type8', 'Employment Type9', 'Employment Type10', 'Employment Type11', 'Employment Type12'],
minCharLimitForSuggestions: 2,
tagsLimit: 1,
pickerDescriptionOptions: {
enabled: true,
values: [
{ key: 'Employment Type1', description: 'Employment Type1 Description'},
{ key: 'Employment Type2', description: 'Employment Type2 Description'},
{ key: 'Employment Type3', description: 'Employment Type3 Description'},
{ key: 'Employment Type4', description: 'Employment Type4 Description'},
{ key: 'Employment Type5', description: 'Employment Type5 Description'},
{ key: 'Employment Type6', description: 'Employment Type6 Description'},
{ key: 'Employment Type7', description: 'Employment Type7 Description'},
{ key: 'Employment Type8', description: 'Employment Type8 Description'},
{ key: 'Employment Type9', description: 'Employment Type9 Description'},
{ key: 'Employment Type10', description: 'Employment Type10 Description'},
{ key: 'Employment Type11', description: 'Employment Type11 Description'},
{ key: 'Employment Type12', description: 'Employment Type12 Description'},
] }
}
}
];
@ -106,4 +157,6 @@ export interface GridItemsType {
designation: string;
salary: number;
dateofjoining: string;
payrolltype: string;
employmenttype: string
};

Просмотреть файл

@ -37,13 +37,16 @@ const Consumer = () => {
const SetDummyData = () : void => {
var dummyData : GridItemsType[] = []
for(var i = 1; i <= 100; i++){
var randomInt = GetRandomInt(1,3);
dummyData.push({
id: i,
name: 'Name'+ GetRandomInt(1, 10),
age: GetRandomInt(20,40),
designation: 'Designation' + GetRandomInt(1, 15),
salary: GetRandomInt(35000, 75000),
dateofjoining: '2010-10-10T14:57:10'
dateofjoining: '2010-10-10T14:57:10',
payrolltype: randomInt % 3 == 0 ? 'Weekly' : randomInt % 3 == 1 ? 'Bi-Weekly' : 'Monthly',
employmenttype: 'Employment Type' + GetRandomInt(1,12)
});
}
@ -95,7 +98,7 @@ const Consumer = () => {
enableGridRowsDelete={true}
enableGridRowsAdd={true}
height={'70vh'}
width={'140vh'}
width={'160vh'}
position={'relative'}
enableUnsavedEditIndicator={true}
onGridSave={onGridSave}

Просмотреть файл

@ -1,9 +1,10 @@
import { ConstrainMode, DatePicker, IStackStyles, IStackTokens, ITextFieldStyles, mergeStyleSets, Position, PrimaryButton, SpinButton, Stack, TextField } from "office-ui-fabric-react";
import { ConstrainMode, DatePicker, Dropdown, IDropdownOption, IStackStyles, IStackTokens, ITag, ITextFieldStyles, mergeStyleSets, Position, PrimaryButton, SpinButton, Stack, TextField } from "office-ui-fabric-react";
import React, { useState } from "react";
import { IColumnConfig } from "../types/columnconfigtype";
import { EditControlType } from "../types/editcontroltype";
import { DayPickerStrings } from "./datepickerconfig";
import { controlClass, horizontalGapStackTokens, stackStyles, textFieldStyles, verticalGapStackTokens } from "./editablegridstyles";
import PickerControl from "./pickercontrol/picker";
interface Props {
onChange: any;
@ -16,6 +17,10 @@ const AddRowPanel = (props: Props) => {
const updateObj : any = {};
const onDropDownChange = (event: React.FormEvent<HTMLDivElement>, selectedDropdownItem: IDropdownOption | undefined, item : any): void => {
updateObj[item.key] = selectedDropdownItem?.text;
}
const onTextUpdate = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text: string): void => {
updateObj[(ev.target as Element).id] = text;
//console.log(updateObj);
@ -25,6 +30,15 @@ const AddRowPanel = (props: Props) => {
props.onChange(updateObj, props.enableRowsCounterField ? AddSpinRef.current.value : 1);
};
const onCellPickerTagListChanged = (cellPickerTagList: ITag[] | undefined, item : any) : void => {
if(cellPickerTagList && cellPickerTagList[0] && cellPickerTagList[0].name){
updateObj[item.key] = cellPickerTagList[0].name;
}
else{
updateObj[item.key] = '';
}
}
const onCellDateChange = (date: Date | null | undefined, item : any): void => {
updateObj[item.key] = date;
};
@ -44,6 +58,26 @@ const AddRowPanel = (props: Props) => {
value={new Date()}
/>);
break;
case EditControlType.DropDown:
tmpRenderObj.push(
<Dropdown
label={item.text}
options={item.dropdownValues ?? []}
onChange={(ev, selected) => onDropDownChange(ev, selected, item)}
/>
);
break;
case EditControlType.Picker:
tmpRenderObj.push(<div>
<span className={controlClass.pickerLabel}>{item.text}</span>
<PickerControl
selectedItemsLimit={1}
pickerTags={item.pickerOptions?.pickerTags ?? []}
minCharLimitForSuggestions={2}
onTaglistChanged={(selectedItem: ITag[] | undefined) => onCellPickerTagListChanged(selectedItem, item)}
pickerDescriptionOptions={item.pickerOptions?.pickerDescriptionOptions}
/></div>);
break;
default:
tmpRenderObj.push(<TextField
name={item.text}

Просмотреть файл

@ -18,7 +18,7 @@ import { TextField, ITextFieldStyles, ITextField } from 'office-ui-fabric-react/
import { ContextualMenu, DirectionalHint, IContextualMenu, IContextualMenuProps } from 'office-ui-fabric-react/lib/ContextualMenu';
import { useBoolean } from '@uifabric/react-hooks';
import { IColumnConfig } from '../types/columnconfigtype';
import { controlClass, GetDynamicSpanStyles, textFieldStyles } from './editablegridstyles';
import { controlClass, dropdownStyles, GetDynamicSpanStyles, textFieldStyles } from './editablegridstyles';
import { IGridItemsType } from '../types/griditemstype';
import { Operation } from '../types/operation';
import { InitializeInternalGrid, InitializeInternalGridEditStructure, ResetGridRowID, ShallowCopyDefaultGridToEditGrid, ShallowCopyEditGridToDefaultGrid } from './editablegridinitialize';
@ -40,6 +40,8 @@ import FilterCallout from './columnfiltercallout/filtercallout';
import { IRowAddWithValues } from '../types/rowaddtype';
import AddRowPanel from './addrowpanel';
import { Props } from '../types/editabledetailslistprops';
import SearchableDropdown from './searchabledropdown/searchabledropdown';
import PickerControl from './pickercontrol/picker';
const EditableGrid = (props: Props) => {
const [editMode, setEditMode] = React.useState(false);
@ -157,8 +159,8 @@ const EditableGrid = (props: Props) => {
useEffect(() => {
UpdateGridEditStatus();
// console.log('activate cell edit');
// console.log(activateCellEdit);
//console.log('activate cell edit');
//console.log(activateCellEdit);
}, [activateCellEdit]);
useEffect(() => {
@ -560,6 +562,14 @@ const EditableGrid = (props: Props) => {
EditCellValue(key, rowNum, activateCurrentCell);
}
const onCellPickerDoubleClickEvent = (key : string, rowNum : number, activateCurrentCell : boolean) : void => {
EditCellValue(key, rowNum, activateCurrentCell);
}
const onDropdownDoubleClickEvent = (key : string, rowNum : number, activateCurrentCell : boolean) : void => {
EditCellValue(key, rowNum, activateCurrentCell);
}
const onKeyDownEvent = (event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>, column : IColumnConfig, rowNum : number, activateCurrentCell : boolean) : void => {
if(event.key == "Enter"){
if(!activateCellEdit[rowNum].isActivated){
@ -584,6 +594,36 @@ const EditableGrid = (props: Props) => {
setActivateCellEdit(activateCellEditTmp);
};
const onCellPickerTagListChanged = (cellPickerTagList: ITag[] | undefined, row : number, column : IColumnConfig) : void => {
setGridEditState(true);
let activateCellEditTmp : any[] = [];
activateCellEdit.forEach((item, index) => {
if(row == index){
item.properties[column.key].value = (cellPickerTagList && cellPickerTagList[0] && cellPickerTagList[0].name) ? cellPickerTagList![0].name : '';
}
activateCellEditTmp.push(item);
});
setActivateCellEdit(activateCellEditTmp);
}
const onDropDownChange = (event: React.FormEvent<HTMLDivElement>, selectedDropdownItem: IDropdownOption | undefined, row : number, column : IColumnConfig): void => {
setGridEditState(true);
let activateCellEditTmp : any[] = [];
activateCellEdit.forEach((item, index) => {
if(row == index){
item.properties[column.key].value = selectedDropdownItem?.text;
}
activateCellEditTmp.push(item);
});
setActivateCellEdit(activateCellEditTmp);
};
const ChangeCellState = (key : string, rowNum : number, activateCurrentCell : boolean, activateCellEditArr : any[]) : any[] => {
let activateCellEditTmp : any[] = [];
activateCellEditTmp = [...activateCellEditArr];
@ -1037,6 +1077,68 @@ const EditableGrid = (props: Props) => {
/>
}</span>
break;
case EditControlType.DropDown:
return <span className={'row-' + rowNum! + '-col-' + index}>{
(
(!column.editable) || !(activateCellEdit && activateCellEdit[rowNum!] && activateCellEdit[rowNum!]['properties'][column.key] && activateCellEdit[rowNum!]['properties'][column.key].activated))
?
<span
className={GetDynamicSpanStyles(column, item[column.key])}
onClick={() => (props.enableCellEdit == true && column.editable == true && props.enableSingleClickCellEdit)
?
EditCellValue(column.key, rowNum!, true)
:
null}
onDoubleClick={() => (props.enableCellEdit == true && column.editable == true && !props.enableSingleClickCellEdit)
?
EditCellValue(column.key, rowNum!, true)
:
null}
>
{item[column.key]}
</span>
:
<Dropdown
placeholder={column.dropdownValues?.filter(x => x.text == item[column.key])[0]?.text ?? 'Select an option'}
options={column.dropdownValues ?? []}
styles={dropdownStyles}
onChange={(ev,selectedItem) => onDropDownChange(ev, selectedItem, rowNum!, column)}
onDoubleClick={() => !activateCellEdit[rowNum!].isActivated ? onDropdownDoubleClickEvent(column.key, rowNum!, false) : null}
/>
}</span>
break;
case EditControlType.Picker:
return <span>{
((!column.editable) || !(activateCellEdit && activateCellEdit[rowNum!] && activateCellEdit[rowNum!]['properties'][column.key] && activateCellEdit[rowNum!]['properties'][column.key].activated))
?
<span
className={GetDynamicSpanStyles(column, item[column.key])}
onClick={() => (props.enableCellEdit == true && column.editable == true && props.enableSingleClickCellEdit)
?
EditCellValue(column.key, rowNum!, true)
:
null}
onDoubleClick={() => (props.enableCellEdit == true && column.editable == true && !props.enableSingleClickCellEdit)
?
EditCellValue(column.key, rowNum!, true)
:
null}
>
{item[column.key]}
</span>
:
<span onDoubleClick = {() => !activateCellEdit[rowNum!].isActivated ? onCellPickerDoubleClickEvent(column.key, rowNum!, false) : null}>
<PickerControl
selectedItemsLimit={column.pickerOptions?.tagsLimit}
pickerTags={column.pickerOptions?.pickerTags ?? []}
defaultTags={item[column.key] ? [item[column.key]] : []}
minCharLimitForSuggestions={column.pickerOptions?.minCharLimitForSuggestions}
onTaglistChanged={(selectedItem: ITag[] | undefined) => onCellPickerTagListChanged(selectedItem, rowNum!, column)}
pickerDescriptionOptions={column.pickerOptions?.pickerDescriptionOptions}
/>
</span>
}</span>
break;
default:
return <span>{
(

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { getTheme, IDetailsColumnStyles, IStackStyles, IStackTokens, ITextFieldStyles, mergeStyleSets } from "office-ui-fabric-react";
import { getTheme, IDetailsColumnStyles, IDropdownStyles, IStackStyles, IStackTokens, ITextFieldStyles, mergeStyleSets } from "office-ui-fabric-react";
import { ICellStyleRulesType } from "../types/cellstyleruletype";
import { IColumnConfig } from "../types/columnconfigtype";
import { EvaluateRule } from "./helper";
@ -48,6 +48,12 @@ export const controlClass = mergeStyleSets({
},
labelValue: {
fontWeight: 'bold',
},
pickerLabel: {
color: '#323130',
fontWeight:600,
padding: '5px 0px',
margin: '5px 0px'
}
});
@ -80,4 +86,8 @@ export const horizontalGapStackTokens: IStackTokens = {
padding: 10,
};
export const textFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: {} };
export const textFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: {} };
export const dropdownStyles: Partial<IDropdownStyles> = {
dropdown: { width: '90%' },
};

Просмотреть файл

@ -1,13 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { DatePicker, IStackStyles, IStackTokens, ITextFieldStyles, mergeStyleSets, PrimaryButton, Stack, TextField } from "office-ui-fabric-react";
import { DatePicker, divProperties, Dropdown, IDropdownOption, IStackStyles, IStackTokens, ITag, ITextFieldStyles, mergeStyleSets, PrimaryButton, Stack, TextField } from "office-ui-fabric-react";
import React, { useState } from "react";
import { IColumnConfig } from "../types/columnconfigtype";
import { EditControlType } from "../types/editcontroltype";
import { DayPickerStrings } from "./datepickerconfig";
import { controlClass, horizontalGapStackTokens, stackStyles, textFieldStyles, verticalGapStackTokens } from "./editablegridstyles";
import { IsValidDataType } from "./helper";
import PickerControl from "./pickercontrol/picker";
import SearchableDropdown from "./searchabledropdown/searchabledropdown";
interface Props {
onChange: any;
@ -17,6 +19,10 @@ interface Props {
const EditPanel = (props: Props) => {
const updateObj : any = {};
const onDropDownChange = (event: React.FormEvent<HTMLDivElement>, selectedDropdownItem: IDropdownOption | undefined, item : any): void => {
updateObj[item.key] = selectedDropdownItem?.text;
}
const onTextUpdate = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text: string, column : IColumnConfig): void => {
debugger;
if(!IsValidDataType(column.dataType, text) || text.trim() == ''){
@ -35,6 +41,15 @@ const EditPanel = (props: Props) => {
updateObj[item.key] = date;
};
const onCellPickerTagListChanged = (cellPickerTagList: ITag[] | undefined, item : any) : void => {
if(cellPickerTagList && cellPickerTagList[0] && cellPickerTagList[0].name){
updateObj[item.key] = cellPickerTagList[0].name;
}
else{
updateObj[item.key] = '';
}
}
const createTextFields = () : any[] => {
let tmpRenderObj : any[] = [];
props.columnConfigurationData.forEach((item, index) => {
@ -51,6 +66,26 @@ const EditPanel = (props: Props) => {
value={new Date()}
/>);
break;
case EditControlType.Picker:
tmpRenderObj.push(<div>
<span className={controlClass.pickerLabel}>{item.text}</span>
<PickerControl
selectedItemsLimit={1}
pickerTags={item.pickerOptions?.pickerTags ?? []}
minCharLimitForSuggestions={2}
onTaglistChanged={(selectedItem: ITag[] | undefined) => onCellPickerTagListChanged(selectedItem, item)}
pickerDescriptionOptions={item.pickerOptions?.pickerDescriptionOptions}
/></div>);
break;
case EditControlType.DropDown:
tmpRenderObj.push(
<Dropdown
label={item.text}
options={item.dropdownValues ?? []}
onChange={(ev, selected) => onDropDownChange(ev, selected, item)}
/>
);
break;
default:
tmpRenderObj.push(<TextField
name={item.text}

Просмотреть файл

@ -0,0 +1,12 @@
import { mergeStyleSets } from "office-ui-fabric-react";
export const classNames = mergeStyleSets({
plainCard: {
width: 200,
height: 140,
display: 'flex',
padding: '10px',
alignItems: 'center',
justifyContent: 'center',
},
});

Просмотреть файл

@ -0,0 +1,128 @@
import { HoverCard, HoverCardType, IBasePickerSuggestionsProps, IInputProps, IPlainCardProps, ISuggestionItemProps, ITag, TagPicker } from "office-ui-fabric-react"
import React, { MouseEventHandler } from "react";
import { useEffect } from "react";
import { IPickerDescriptionOption, IPickerTagDescription } from "../../types/columnconfigtype";
import { classNames } from "./picker.styles";
interface Props {
selectedItemsLimit? : number;
pickerTags : string[];
defaultTags?: string[];
minCharLimitForSuggestions?: number;
onTaglistChanged?: any;
pickerDescriptionOptions?: IPickerDescriptionOption;
}
const PickerControl = (props: Props) => {
const [pickerTags, setPickerTags] = React.useState<ITag[]>([]);
const [defaultTags, setdefaultTags] = React.useState<ITag[]>([]);
const [pickerDescriptions, setPickerDescriptions] = React.useState<IPickerTagDescription[]>([]);
const [pickerFilteredText, setPickerFilteredText] = React.useState<string>('');
useEffect(() => {
if(props.pickerTags && props.pickerTags.length > 0){
setPickerTags(props.pickerTags.map(item => ({ key: item, name: item })));
setdefaultTags(props?.defaultTags?.map(item => ({ key: item, name: item })) ?? []);
}
}, [props.pickerTags]);
useEffect(() => {
if(props && props.pickerDescriptionOptions && props.pickerDescriptionOptions.enabled && props.pickerDescriptionOptions.values){
setPickerDescriptions(props.pickerDescriptionOptions.values);
}
}, [props.pickerDescriptionOptions]);
const pickerSuggestionsProps: IBasePickerSuggestionsProps = {
suggestionsHeaderText: !props.minCharLimitForSuggestions ? 'Suggested tags' : (pickerFilteredText.length >= props.minCharLimitForSuggestions ? 'Suggested tags' : ''),
noResultsFoundText: !props.minCharLimitForSuggestions ? 'No suggested tags found' : (pickerFilteredText.length >= props.minCharLimitForSuggestions ? 'No suggested tags found' : ''),
};
const getTextFromItem = (item: ITag) => item.name;
const listContainsTagList = (tag: ITag, tagList?: ITag[]) => {
if (!tagList || !tagList.length || tagList.length === 0) {
return false;
}
return tagList.some(compareTag => compareTag.key === tag.key);
};
const filterSuggestedTags = (filterText: string, tagList: ITag[] | undefined): ITag[] => {
setPickerFilteredText(filterText);
if(!props.minCharLimitForSuggestions){
return filterText
? pickerTags.filter(
tag => tag.name.toLowerCase().indexOf(filterText.toLowerCase()) === 0 && !listContainsTagList(tag, tagList),
)
: [];
}
if(filterText.length >= props.minCharLimitForSuggestions){
return filterText
? pickerTags.filter(
tag => tag.name.toLowerCase().indexOf(filterText.toLowerCase()) === 0 && !listContainsTagList(tag, tagList),
)
: [];
}
return [];
};
const inputProps: IInputProps = {
'aria-label': 'Tag picker'
};
const onFilterTagListChanged = React.useCallback((tagList: ITag[] | undefined): void => {
setdefaultTags(tagList!);
if(props.onTaglistChanged){
props.onTaglistChanged(tagList);
}
},[]);
const onRenderPlainCard = (item : ITag): JSX.Element => {
return (
<div className={classNames.plainCard}>
{pickerDescriptions.filter(x => x.key == item.key)[0].description}
</div>
);
};
const onRenderSuggestionsItem = (tag: ITag, itemProps: ISuggestionItemProps<ITag>) : JSX.Element => {
const plainCardProps: IPlainCardProps = {
onRenderPlainCard: onRenderPlainCard,
renderData: tag
};
if(pickerDescriptions && pickerDescriptions.length > 0){
return (<HoverCard
type={HoverCardType.plain}
plainCardProps={plainCardProps}
instantOpenOnClick
>
<div style={{ padding:'10px' }} key={tag.key}>{tag.name}</div>
</HoverCard>);
}
return <div style={{ padding:'10px' }} key={tag.key}>{tag.name}</div>
}
return (
<>
<TagPicker
removeButtonAriaLabel="Remove"
onResolveSuggestions={filterSuggestedTags}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={pickerSuggestionsProps}
itemLimit={props.selectedItemsLimit ?? 1}
onChange={onFilterTagListChanged}
selectedItems={defaultTags}
inputProps={inputProps}
onRenderSuggestionsItem={onRenderSuggestionsItem}
/>
</>
);
}
export default PickerControl

Просмотреть файл

@ -0,0 +1,74 @@
import { IDropdownProps } from "@fluentui/react";
import { Callout, DirectionalHint, Dropdown, DropdownMenuItemType, IDropdownOption, mergeStyles, ScrollablePane, ScrollbarVisibility, Stack, TextField } from "office-ui-fabric-react";
import { dropdownStyles, stackTokens, styles } from "./searchabledropdownstyles";
import { useId } from '@uifabric/react-hooks';
import { useEffect } from "react";
import React from "react";
interface Props extends IDropdownProps {
field?: string;
minCharLengthBeforeSuggestion?: number;
}
const SearchableDropdown = (props: Props) => {
const [dropdownOptions, setDropdownOptions] = React.useState<IDropdownOption[]>([]);
const [placeholder, setPlaceHolder] = React.useState<string>();
useEffect(() => {
setDropdownOptions(props.options);
setPlaceHolder(props.placeholder);
}, [props.options]);
const onFilterTextUpdate = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, searchText: string | undefined): void => {
debugger;
var dropdownOptionsTmp : IDropdownOption[] = [...props.options.filter(x => x.itemType != DropdownMenuItemType.Header)];
console.log('filtered');
console.log(dropdownOptionsTmp.filter(x => x.text.toLowerCase().indexOf(searchText?.toLowerCase() ?? '') > -1));
var matches : IDropdownOption[] = dropdownOptionsTmp.filter(x => x.text.toLowerCase().indexOf(searchText?.toLowerCase() ?? '') > -1);
setPlaceHolder(`[${matches.length.toString()} match${matches.length != 1 ? 'es' : ''} found]`);
setDropdownOptions(matches);
}
const labelId: string = useId('dropdown-callout-label');
const descriptionId: string = useId('dropdown-callout-description');
return (
<>
<Callout
className={styles.callout}
ariaLabelledBy={labelId}
ariaDescribedBy={descriptionId}
role="filtercallout"
gapSpace={10}
target={`.${props.className}`}
isBeakVisible={true}
directionalHint={DirectionalHint.bottomCenter}
>
<Stack verticalAlign="start" tokens={stackTokens}>
<TextField
id={`id-${props.className}`}
className={styles.textFieldClass}
placeholder={`Search ${props.field ?? ''}`}
onChange={(ev, text) => onFilterTextUpdate(ev, text)}
/>
<div className={mergeStyles({ height: '10vh', width: '30vh', position: 'relative', backgroundColor: 'white' })}>
<ScrollablePane scrollbarVisibility={ScrollbarVisibility.auto}>
<Dropdown
label={props.label}
placeholder={placeholder}
options={dropdownOptions ?? []}
styles={props.styles}
onChange={props.onChange}
onDoubleClick={props.onDoubleClick}
/>
</ScrollablePane>
</div>
</Stack>
</Callout>
</>
);
}
export default SearchableDropdown;

Просмотреть файл

@ -0,0 +1,22 @@
import { IDropdownStyles, IStackTokens, mergeStyleSets } from "office-ui-fabric-react";
export const dropdownStyles: Partial<IDropdownStyles> = {
dropdown: { width: '90%', margin:10 },
};
export const styles = mergeStyleSets({
callout: {
maxWidth: 500,
padding: 30
},
textFieldClass:{
display: 'block',
marginTop: 10,
marginLeft: 10,
marginRight: 10,
width: '90%',
}
});
export const stackTokens: IStackTokens = { childrenGap: 20, maxWidth:1000 };

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT License.
import { ConstrainMode, IColumn, IDetailsHeaderProps } from 'office-ui-fabric-react/lib/components/DetailsList/DetailsList.types';
import { IDropdownOption } from "office-ui-fabric-react";
import { CalculationType } from "./calculationtype";
import { ICellStyleRulesType } from './cellstyleruletype';
import { EditControlType } from "./editcontroltype";
@ -20,4 +21,23 @@ export interface IColumnConfig extends IColumn {
maxLength?: number;
applyColumnFilter?: boolean;
cellStyleRule?: ICellStyleRulesType;
};
dropdownValues?: IDropdownOption[];
pickerOptions?: IPickerOptions;
};
export interface IPickerOptions {
tagsLimit?: number;
minCharLimitForSuggestions?: number;
pickerTags: string[];
pickerDescriptionOptions?: IPickerDescriptionOption;
}
export interface IPickerDescriptionOption {
enabled: boolean;
values: IPickerTagDescription[];
}
export interface IPickerTagDescription {
key: string;
description: string;
}

Просмотреть файл

@ -7,5 +7,6 @@ export enum EditControlType {
DropDown,
Date,
MultilineTextField,
DateTime
DateTime,
Picker
}

Просмотреть файл

@ -17,7 +17,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"noEmit": false,
"jsx": "react-jsx",
"downlevelIteration": true,
"declaration": true,