@mikezimm/fps-library-v2
Version:
Library of reusable typescript/javascript functions, interfaces and constants
777 lines (775 loc) • 46.1 kB
JavaScript
/**
* CodeAnalizerComment: Updated 5 imports on 2024-09-22 14:49:52
* Update:: import { IAnySourceItem } to '@mikezimm/fps-core-v7/lib/components/molecules/AnyContent/IAnyContent;'
* Update:: import { IAnySourceItemAny } to '@mikezimm/fps-core-v7/lib/components/molecules/AnyContent/IAnyContent;'
* Update:: import { getFilteredItems } to '@mikezimm/fps-core-v7/lib/components/molecules/SearchPage/functions/getFilteredV1;'
* Update:: import { IFPSAgeSliderProps } to '@mikezimm/fps-core-v7/lib/components/atoms/FPSAgeSlider/FPSAgeTypes;'
* Update:: import { FPSAgeSliderOptions7Years } to '@mikezimm/fps-core-v7/lib/components/atoms/FPSAgeSlider/FPSAgeSliderOptions7YearPart;'
*/
/**
* CodeAnalizerComment: Updated 6 imports on 2024-09-21 23:07:24
* Update:: import { check4This } to '@mikezimm/fps-core-v7/lib/logic/Links/CheckSearch;'
* Update:: import { IAnySourceItem } to '@mikezimm/fps-core-v7/lib/components/molecules/AnyContent/IAnyContent;'
* Update:: import { IAnySourceItemAny } to '@mikezimm/fps-core-v7/lib/components/molecules/AnyContent/IAnyContent;'
* Update:: import { makeid } to '@mikezimm/fps-core-v7/lib/logic/Strings/guids;'
* Update:: import { getFilteredItems } to '@mikezimm/fps-core-v7/lib/components/molecules/SearchPage/functions/getFilteredV1;'
* Update:: import { upperFirstLetter } to '@mikezimm/fps-core-v7/lib/logic/Strings/stringCase;'
*/
import * as React from 'react';
import { Icon, } from '@fluentui/react/lib/Icon';
import { DefaultMultiSelectCommands, } from './ISourcePagesProps';
import FPSAgeSliderHook from '../../atoms/FPSAgeSlider/FPSAgeHook';
import { check4This, Check4 } from "@mikezimm/fps-core-v7/lib/logic/Links/CheckSearch";
import { makeid } from '@mikezimm/fps-core-v7/lib/logic/Strings/guids';
import { getFilteredItems } from '@mikezimm/fps-core-v7/lib/components/molecules/SearchPage/functions/getFilteredV1';
import SourceSearchAgeHook from '../SearchPage/component/SearchBoxRowAge';
import { FPSAgeSliderOptions7Years } from '@mikezimm/fps-core-v7/lib/components/atoms/FPSAgeSlider/FPSAgeSliderOptions7YearPart';
import { GetGoToListLink } from '../../atoms/Links/GetGoToListLink';
import { FPSFetchStatus } from '../../atoms/FetchStatus/FPSFetchStatus';
import { sourceButtonRow } from './sourceButtonRow';
import { FPSReactJSON } from '../../atoms/ReactJSON/ReactJSONObjectV2';
import { upperFirstLetter } from '@mikezimm/fps-core-v7/lib/logic/Strings/stringCase';
import { stringifyFpsSpServiceObj } from '@mikezimm/fps-core-v7/lib/components/molecules/source-props/createSources/cloneSourceProps';
import { makeSmallTimeObject } from '@mikezimm/fps-core-v7/lib/logic/Time/smallTimeObject';
const AgeSliderOptions = [{ key: -1, maxAge: 0, text: 'all dates', }, ...FPSAgeSliderOptions7Years.map(item => {
item.text = `${item.text.replace('The past ', 'older than ')} ago`.replace(`ago ago`, `ago`); // fix double 'ago'
return item;
})];
const defaultMaxVisible = 20;
require('@mikezimm/fps-styles/dist/sourcePages.css');
export default class SourcePages extends React.Component {
_updateParentCmds() {
if (this.props.onParentRequestsFilteredCmds && this.props.onParentRequestsFilteredCmds.length > 0) {
this._onParentRequestsFilteredCmds = this.props.onParentRequestsFilteredCmds;
}
}
/***
* .o88b. .d88b. d8b db .d8888. d888888b d8888b. db db .o88b. d888888b .d88b. d8888b.
* d8P Y8 .8P Y8. 888o 88 88' YP `~~88~~' 88 `8D 88 88 d8P Y8 `~~88~~' .8P Y8. 88 `8D
* 8P 88 88 88V8o 88 `8bo. 88 88oobY' 88 88 8P 88 88 88 88oobY'
* 8b 88 88 88 V8o88 `Y8b. 88 88`8b 88 88 8b 88 88 88 88`8b
* Y8b d8 `8b d8' 88 V888 db 8D 88 88 `88. 88b d88 Y8b d8 88 `8b d8' 88 `88.
* `Y88P' `Y88P' VP V8P `8888Y' YP 88 YD ~Y8888P' `Y88P' YP `Y88P' 88 YD
*
*
*/
//Standards are really site pages, supporting docs are files
constructor(props) {
super(props);
this._itemsPerPage = defaultMaxVisible;
this._retainViewXdidUpdates = 0;
this._multiButtonLabels = [];
this._onParentRequestsFilteredCmds = ['_onParentRequestsFilteredCmds???'];
const { stateSource, } = this.props;
const refreshId = stateSource ? stateSource.refreshId : 'mzWasHere';
this._updateParentCmds();
const searchText = this.props.deepProps && this.props.deepProps.length >= 1 && this.props.deepProps[0] ? decodeURIComponent(this.props.deepProps[0]) : '';
const topSearchStr = this.props.deepProps && this.props.deepProps.length >= 2 && this.props.deepProps[1] ? decodeURIComponent(this.props.deepProps[1]) : '[]';
const topSearch = !topSearchStr ? [] : JSON.parse(topSearchStr);
const filtered = stateSource ? getFilteredItems(stateSource.items, searchText, [], [], [], topSearch) : [];
this._itemsPerPage = this.props.itemsPerPage ? this.props.itemsPerPage : this._itemsPerPage;
const enableAge = this.props.ageSlider === true && this.props.searchAgeProp && this.props.searchAgeOp ? true : false;
const ageIndex = this.props.ageIndexDefault ? this.props.ageIndexDefault : 0;
const searchAge = AgeSliderOptions[Math.abs(ageIndex)].maxAge; //ageIndex is negative... needs inverse to get array element
this.state = {
refreshId: refreshId ? refreshId : 'mzWasHere',
filtered: filtered,
topSearch: topSearch,
sortNum: 'asc',
sortName: '-',
sortGroup: '-',
searchTime: null,
searchText: searchText,
showItemPanel: false,
showCanvasContent1: false,
showPanelJSON: false,
showThisItem: filtered.length > 0 ? filtered[0] : null,
firstVisible: 0,
lastVisible: this._itemsPerPage - 1,
resetArrows: refreshId ? refreshId : 'mzWasHere',
detailToggle: false,
searchAge: searchAge,
enableAge: enableAge,
lastItemMultiClickIdx: -79,
renderJSON: this.props.jsonOption === 'Default' || this.props.jsonOption === 'Only' ? true : false,
infinateItems: this.props.jsonOption === 'Default' || this.props.jsonOption === 'Only' ? true : false,
parentRequestStatus0: '',
parentRequestStatus1: '',
parentRequestStatus2: '',
parentRequestStatus3: '',
};
}
async componentDidMount() {
await this.updateWebInfo();
}
//
/***
* d8888b. d888888b d8888b. db db d8888b. d8888b. .d8b. d888888b d88888b
* 88 `8D `88' 88 `8D 88 88 88 `8D 88 `8D d8' `8b `~~88~~' 88'
* 88 88 88 88 88 88 88 88oodD' 88 88 88ooo88 88 88ooooo
* 88 88 88 88 88 88 88 88~~~ 88 88 88~~~88 88 88~~~~~
* 88 .8D .88. 88 .8D 88b d88 88 88 .8D 88 88 88 88.
* Y8888D' Y888888P Y8888D' ~Y8888P' 88 Y8888D' YP YP YP Y88888P
*
* this._retainFilters = true;
*/
componentDidUpdate(prevProps) {
//Just rebuild the component
const { stateSource, topButtons, ageSlider, searchAgeProp, searchAgeOp, } = this.props;
const enableAge = ageSlider === true && searchAgeProp && searchAgeOp ? true : false;
const itemsLength = stateSource ? stateSource.items.length : 0;
let resetArrows = false;
if (prevProps.resetArrows !== this.props.resetArrows || prevProps.ageSlider !== ageSlider
|| prevProps.searchAgeProp !== searchAgeProp || prevProps.searchAgeOp !== searchAgeOp) {
resetArrows = true;
}
let lastVisible = this._itemsPerPage;
if (check4This(Check4.filtered_Eq_true) === true)
console.log('filtered SourePage', lastVisible, stateSource.items);
if (itemsLength < lastVisible)
lastVisible = itemsLength;
if (!stateSource || topButtons.join('-') !== prevProps.topButtons.join('-')) {
this.setState({
filtered: stateSource ? stateSource.items : [],
topSearch: [],
searchText: '',
firstVisible: 0,
lastVisible: lastVisible - 1,
refreshId: makeid(4),
resetArrows: makeid(4),
enableAge: enableAge,
lastItemMultiClickIdx: -79,
});
/**
* 2024-12-22: Had to clear the fpsSpService from the stringify process or else get an error like:
* 'Something went wrong'
* ERROR:
* Converting circular structure to json
* --> property '_registrations' -> object with constructure 'Object'
* --> property 'sp-page-context:PageContext_9' -> object with constructure 'Object'
* --> property 'service' -> object with constructure 'a'
*
* Originally looked like this:
* } else if ( JSON.stringify(this.props.primarySource ) !== JSON.stringify(prevProps.primarySource ) ) {
* Added the refreshId check first so it does not have to stringify the props
*/
}
else if (this.props.primarySource.refreshId !== prevProps.primarySource.refreshId
|| stringifyFpsSpServiceObj(this.props.primarySource) !== stringifyFpsSpServiceObj(prevProps.primarySource)) {
this.setState({
filtered: stateSource ? stateSource.items : [],
topSearch: [],
searchText: '',
firstVisible: 0,
lastVisible: lastVisible - 1,
refreshId: makeid(4),
resetArrows: makeid(4),
enableAge: enableAge,
lastItemMultiClickIdx: -79,
});
}
else if (this.props.forceRender !== prevProps.forceRender || this.props.stateSource.items.length !== prevProps.stateSource.items.length) {
this.setState({
topSearch: [],
filtered: stateSource.items,
// added searchText reset for https://github.com/mikezimm/Compliance/issues/126
searchText: '',
firstVisible: 0,
lastVisible: lastVisible - 1,
refreshId: makeid(4),
resetArrows: makeid(4),
enableAge: enableAge,
lastItemMultiClickIdx: -79,
});
}
else if (this.props.stateSource.refreshId !== prevProps.stateSource.refreshId || this.props.pageWidth !== prevProps.pageWidth || this.props.topButtons.length !== prevProps.topButtons.length) {
// Added this loop for https://github.com/fps-solutions/HubCon/issues/46
// Essentially allows to keep the same view items ( paging and items on the screen ) after you call onParentCall -- as in case where you 'select' an item but want to keep same items on the screen.
if (this._retainViewXdidUpdates > 0) {
this._retainViewXdidUpdates--;
const ageFilteredItems = this.state.enableAge === false ? this.props.stateSource.items : [];
if (this.state.enableAge === true) {
this.props.stateSource.items.map((item) => {
if (item.FPSItem && item.FPSItem.Stamp)
if (item[this.props.searchAgeProp] > this.state.searchAge) {
ageFilteredItems.push(item);
}
});
}
const filtered = getFilteredItems(ageFilteredItems, this.state.searchText, [], [], [], this.state.topSearch);
let lastVisible = this._itemsPerPage;
if (check4This(Check4.filtered_Eq_true) === true)
console.log('filtered SourePage', lastVisible, filtered);
this.setState({
filtered: filtered,
});
}
else {
this.setState({
topSearch: [],
filtered: this.props.stateSource.items,
firstVisible: 0,
lastVisible: lastVisible - 1,
refreshId: this.props.stateSource.refreshId,
resetArrows: makeid(4),
enableAge: enableAge,
lastItemMultiClickIdx: -79,
});
}
}
else if (resetArrows === true) {
this.setState({
firstVisible: 0,
lastVisible: lastVisible - 1,
resetArrows: makeid(4),
enableAge: enableAge,
lastItemMultiClickIdx: -79,
});
}
}
async updateWebInfo() {
}
/***
* d8888b. d88888b d8b db d8888b. d88888b d8888b.
* 88 `8D 88' 888o 88 88 `8D 88' 88 `8D
* 88oobY' 88ooooo 88V8o 88 88 88 88ooooo 88oobY'
* 88`8b 88~~~~~ 88 V8o88 88 88 88~~~~~ 88`8b
* 88 `88. 88. 88 V888 88 .8D 88. 88 `88.
* 88 YD Y88888P VP V8P Y8888D' Y88888P 88 YD
*
*
*/
render() {
var _a;
const { primarySource, topButtons, showItemType, stateSource, sourcePageClassName, disableSpinner, tableFractionalWidths = [], tableStyles = {} } = this.props; // canvasOptions
const { debugMode, selectedClass, tableHeaderElements, tableHeaderClassName, tableClassName } = this.props; // canvasOptions
const { headingElement, footerElement, multiSelectCall, multiSelectCommands, multiSelectKeyword, ReactJSONSettings, onParentDisabled } = this.props; // canvasOptions
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { searchText, renderJSON } = this.state;
const StatusElement = FPSFetchStatus(primarySource, stateSource, disableSpinner); //
/**
* 2024-12-30: Added disablePage as a way to disable buttons, search etc which should not work due to data not being loaded.
*/
const disablePage = StatusElement || stateSource.ok !== true ? true : false;
const topSearch = []; //All major future to be grid components
topButtons.map(searchObjectFull => {
// Added this if then so only buttons with valid value will create button... empty, undefined and null will not create button
if (searchObjectFull) {
const searchObjectArray = searchObjectFull.split('==');
const searchObject = searchObjectArray[0];
const classNames = ['button'];
if (this.state.topSearch.indexOf(searchObjectFull) > -1) {
classNames.push('isSelected');
classNames.push(selectedClass);
}
/**
* This topSearch using arrow function did not work
*/
topSearch.push(React.createElement("div", { className: classNames.join(' '), style: null, onClick: this._clickTop.bind(this, searchObjectFull), title: searchObjectArray.length > 0 ? searchObjectFull : '' }, searchObject)); //Only show Title prop if the button has special search
}
});
const topSearchContent = React.createElement("div", { className: 'topSearch', style: { background: debugMode === true ? 'pink' : null } }, topSearch);
const renderAsTable = tableHeaderElements && tableHeaderElements.length > 0 ? true : false;
let tableElement = undefined;
const filtered = [];
let tableHeaderRow = undefined;
if (renderAsTable === true) {
tableHeaderRow = React.createElement("tr", { className: tableHeaderClassName }, tableHeaderElements.map((item, index) => { return React.createElement("th", { key: index }, item); }));
}
/**
* 2024-12-30: https://github.com/fps-solutions/HubCon/issues/115
* I considered adding stateSource.ok === true in the if but decided against it because in HubCon for instance,
* the 1 item in the state.filtered array was the error tile so I will let it show.
*/
const { optionalColumns1, optionalColumns2, optionalColumns3, optionalColumns4, optionalColumns5, optionalColumns6, optionalColumns7, expand, openItemButton } = this.props; // canvasOptions
const { rowParamBoo1, rowParamStr1, rowParamNum1, rowParamObj1 } = this.props; // canvasOptions
if (renderJSON === false && stateSource.loaded === true) {
this.state.filtered.map((item, idx) => {
if (idx >= this.state.firstVisible && idx <= this.state.lastVisible) {
filtered.push(this.props.renderRow({
item: item,
searchText: searchText,
onClick: this.clickListItem.bind(this),
onTextFilter: this.clickTextFilter.bind(this),
onPropFilter: this.clickPropFilter.bind(this),
details: this.state.detailToggle,
showItemType: showItemType,
onParentCall: this._parentCall.bind(this),
sendItemToParent: this._sendItemToParent.bind(this),
optionalColumns1: optionalColumns1,
optionalColumns2: optionalColumns2,
optionalColumns3: optionalColumns3,
optionalColumns4: optionalColumns4,
optionalColumns5: optionalColumns5,
optionalColumns6: optionalColumns6,
optionalColumns7: optionalColumns7,
/**
* 2025-03-04: Only adding some of the optional params down until the additional ones are needed
*/
rowParamBoo1: rowParamBoo1,
rowParamStr1: rowParamStr1,
rowParamNum1: rowParamNum1,
rowParamObj1: rowParamObj1,
expand: expand,
openItemButton: openItemButton,
}));
}
});
}
else { // push spinner element
// if ( this.props.disableSpinner !== true ) filtered.push( FPSFetchSpinner( stateSource, primarySource ));
}
if (renderJSON === false && (!stateSource || stateSource.items.length === 0)) {
// This is duplicated in SearchPage.tsx and SourcePages.tsx as well
// const FetchingSpinner = this.props.showSpinner === false ? null : <div style={{display: 'inline'}}><Spinner size={SpinnerSize.large} label={"Fetching more information ..."} style={{ padding: 30 }} /></div>;
// const spinnerStyles : ISpinnerStyles = { label: {fontSize: '20px', fontWeight: '600', }};
// const FetchingSpinner = this.props.showSpinner === false ? null : <div style={{display: 'inline', top: -10, position: 'relative'}}>
// <Spinner size={SpinnerSize.medium} label={"Fetching more information ..."} labelPosition= 'right'
// style={{ paddingBottom: 10, backgroundColor: 'lightyellow' }} styles={ spinnerStyles }/></div>;
filtered.push(undefined);
}
/**
* https://github.com/fps-solutions/HubCon/issues/115
* 2024-12-30: DELETED THIS section because after I added
* stateSource.ok === true to only show after valid loaded results
* I realized it was the exact same conditions and result content as noResults: JSX.Element below and a duplicate
*
*
*/
// } else if ( stateSource.ok === true && stateSource.items.length > 0 && filtered.length === 0 ) {
// filtered.push( <div>
// <h2>Hmmm... I could not find any items with</h2>
// <h3>Search text: </h3>
// <div style={{ fontWeight: 'bold', color: 'darkred' }}>{ searchText ? searchText : 'Does not look like you typed anything in the search box...' }</div>
// <h3>With any of these words (top buttons)</h3>
// {this.state.topSearch.length === 0 ?
// <div>
// No top buttons were selected.
// </div>
// :
// <div style={{ fontWeight: 'bold', color: 'blue' }}>{ this.state.topSearch.map( (str: string, idx: number ) => {
// return <li key={idx} >{ str }</li>
// })}</div>
// }
// </div>
// )
// }
const noResults = stateSource.ok === true && stateSource.items.length > 0 && filtered.length === 0 ?
React.createElement("div", null,
React.createElement("h2", null, "Hmmm... I could not find any items with"),
React.createElement("h3", null, "Search text: "),
React.createElement("div", { style: { fontWeight: 'bold', color: 'darkred' } }, searchText ? searchText : 'Does not look like you typed anything in the search box...'),
React.createElement("h3", null, "With any of these words (top buttons)"),
this.state.topSearch.length === 0 ?
React.createElement("div", null, "No top buttons were selected.")
:
React.createElement("div", { style: { fontWeight: 'bold', color: 'blue' } }, this.state.topSearch.map((str, idx) => {
return React.createElement("li", { key: idx }, str);
}))) : undefined;
const tableClasses = ['sourceTable', tableClassName];
if (renderJSON !== true && renderAsTable === true) {
tableElement = React.createElement("table", { className: tableClasses.join(' '), style: tableStyles },
tableHeaderRow,
filtered);
}
const { ageSlider, searchAgeProp, jsonOption, jsonObject, jsonFiltered } = this.props; // canvasOptions
const AgeSliderWPProps = ageSlider === false ? undefined : {
FPSAgeIsVisible: true,
disabled: disablePage,
// https://github.com/fps-solutions/PagePal/issues/7
FPSAgeColumnName: searchAgeProp ? searchAgeProp : 'modifiedAge',
FPSAgeColumnTitle: searchAgeProp ? upperFirstLetter(searchAgeProp, true) : 'Modified',
FPSAgeDefault: 0,
onChange: (value) => this._onAgeChange(value),
alternateOptions: AgeSliderOptions,
};
const AgeSlider = ageSlider === false ? undefined : React.createElement(FPSAgeSliderHook, { props: AgeSliderWPProps });
const gotoListLink = GetGoToListLink({ primarySource: primarySource, }); // GetGoToListLink, GetGoToItemLink, GetGoToWebLink
const debugContent = debugMode !== true ? null : React.createElement("div", { style: { cursor: 'default', marginLeft: '20px' } },
"App in debugMode - Change in Web Part Properties - Page Preferences. ",
React.createElement("b", null,
React.createElement("em", null,
"Currently in ",
primarySource.listTitle)));
const searchSourceDesc = !primarySource.searchSourceDesc ? null : React.createElement("div", { className: 'searchSourceDesc' },
React.createElement("div", { className: 'sourceDesc' }, primarySource.searchSourceDesc),
gotoListLink);
const jsonIconCSS = { fontSize: 'x-large', padding: '7px', width: '18px', borderRadius: '7px', marginRight: '10px', cursor: 'pointer' }; // background: this.state.renderJSON === true ? 'lightgreen' : 'white',
const InfinateIcon = jsonOption === 'Never' || ((_a = stateSource === null || stateSource === void 0 ? void 0 : stateSource.items) === null || _a === void 0 ? void 0 : _a.length) <= defaultMaxVisible ? undefined : React.createElement(Icon, { title: 'Show up to 1000 Filtered items', style: { ...jsonIconCSS, ...{ fontWeight: this.state.infinateItems === true ? 900 : 300, } }, iconName: 'LineSpacing', onClick: disablePage === true ? null : this._setInfinate.bind(this) });
const JSONIcon = jsonOption === 'Never' || jsonOption === 'Only' ? undefined : React.createElement(Icon, { title: 'Show items as JSON', style: { ...jsonIconCSS, ...{ fontWeight: this.state.renderJSON === true ? 900 : 300, } }, iconName: 'Code', onClick: disablePage === true ? null : () => this.setState({ renderJSON: !this.state.renderJSON }) });
const INJSONIcons = React.createElement("div", { style: { display: 'flex', alignItems: 'center', } },
JSONIcon,
InfinateIcon);
let MultiSelectEle = null;
// multiSelectCall, multiSelectCommands, multiSelectKeyword
if (multiSelectCall) {
let buttonLabels = [];
this._multiButtonLabels = multiSelectCommands && multiSelectCommands.length > 0 ? multiSelectCommands : DefaultMultiSelectCommands;
if (multiSelectKeyword) {
this._multiButtonLabels.map((item) => { buttonLabels.push(item ? item.replace(`Select`, multiSelectKeyword) : ''); });
}
else {
buttonLabels = this._multiButtonLabels;
}
const multiSelectButtonRowProps = {
title: '',
// heading?: JSX.Element; // Heading above row of buttons
Labels: buttonLabels,
onClick: this._multiCall.bind(this),
selected: null,
infoEle: INJSONIcons,
rowClass: null,
selectedClass: null,
rowCSS: { display: 'inline-flex', gap: '1em', paddingTop: '0px' },
buttonCSS: null,
disabled: disablePage === true ? ['all'] : null,
descEle: null, // Added as block element below buttons. strings are made to smaller font-size.
};
MultiSelectEle = sourceButtonRow(multiSelectButtonRowProps);
}
const searchBox = React.createElement(SourceSearchAgeHook, { disabled: disablePage, _onSearchChange: this._onSearchChange.bind(this), searchTime: this.state.searchTime, searchText: searchText, farLeftEle: MultiSelectEle ? MultiSelectEle : INJSONIcons, itemsPerPage: this._itemsPerPage, itemCount: this.state.filtered.length, _updateFirstLastVisible: this._updateFirstLastVisible.bind(this), debugMode: debugMode, resetArrows: this.state.resetArrows, layout: 'grid', ageElement: AgeSlider });
// const deepHistory = debugMode !== true ? null :
// <ReactJson src={ this.state.filtered } name={ primarySource.listTitle } collapsed={ false } displayDataTypes={ false } displayObjectSize={ false } enableClipboard={ true } style={{ padding: '20px 0px' }} theme= { 'rjv-default' } indentWidth={ 2}/>;
// // Moved entire panel to separate functional component so it can be reused from Search as well
// const thePanel = showItemPanel !== true ? null :
// ContentPanel( {
// showItemPanel: showItemPanel,
// showThisItem: showThisItem,
// primarySource: primarySource,
// onClosePanel: this._onClosePanel.bind(this),
// debugMode: debugMode,
// topButtons: topButtons,
// refreshId: refreshId,
// search: search,
// source: source,
// canvasOptions: canvasOptions,
// } );
// const errorMessage = stateSource && stateSource.errorInfo && stateSource.errorInfo.returnMess ? true : false;
// const theMessage = errorMessage === false ? '' : stateSource.errorInfo.returnMess.split('"value":"');
// const webUrl = itemList[ preLoadIndex ].Subsite;
// const listTitle = itemList[ preLoadIndex ].NoRecordsDeclared;
// const webLinkElemennt2 = <div style={{ paddingLeft: '20px' }}className={ [ 'searchSourceStatus', 'fps-gen-goToLink' ].join(' ')} onClick={ () => { window.open( `${primarySource.webUrl}`,'_blank' ) ; } }>
// Go to web <Icon iconName='OpenInNewTab'/></div>;
// const webUrlLink = <span className={ 'errorLink' } onClick={ () => { window.open( primarySource.webUrl, '_blank')}}>{ primarySource.webUrl }</span>;
// const webUrlLinkDiv = <div style={ { color: 'black', fontSize: 'large', paddingBottom: '10px', fontWeight: 600 }} >Site Url: { webUrlLink }</div>;
// const GoToWebLink1 = GetGoToWebLink( { primarySource: primarySource, altWebText: 'Site Url:', }); // GetGoToListLink, GetGoToItemLink, GetGoToWebLink
// const GoToWebLink2 = GetGoToWebLink( { primarySource: primarySource, altWebText: 'Go to site:', showWebIcon: true, webLinkCSS: { color: null, fontSize: null, marginRight: '60px'} }); // GetGoToListLink, GetGoToItemLink, GetGoToWebLink
// const errorElement = errorMessage !== true ? undefined : <div style={{ background: 'yellow', color: 'red', height: '200px', paddingTop: '25px', textAlign: 'center' }}>
// { GoToWebLink1 }
// {/* <div style={{ color: 'black', paddingBottom: '5px', fontSize: 'large', fontWeight: 600 }}>Site Url: { webUrlLink }</div>
// <div style={{ color: 'black', paddingBottom: '15px', fontSize: 'large', fontWeight: 600 }}>Library: { listUrlLink }</div> */}
// <div style={{ fontSize: 'x-large', paddingBottom: '5px', fontWeight: 700 }}>{ stateSource.errorInfo.friendly }</div>
// <div style={{ fontSize: 'large', paddingBottom: '15px' }}>{ theMessage[ theMessage.length -1 ].replace('"}}}', '') }</div>
// { GoToWebLink2 }
// { gotoListLink }
// </div>;
// Added this
const hasError = stateSource.status === 'Success' || stateSource.status === 'Unknown' ? false : true;
let JsonElementF = undefined;
let JsonElementU = undefined;
if (renderJSON === true && stateSource.status === 'Success') {
// jsonOption?: ISourceJsonOption; // Default = false
// jsonObject?: ISourceJsonObject; // default = items
// jsonFiltered?: ISourceJsonFilter; // default = filtered
const JSONLabel = `${jsonObject === 'items' ? jsonObject === 'items' : 'StateSource'}`;
if (jsonFiltered === 'both' || jsonFiltered === 'unfiltered') {
const useThisObject = jsonObject === 'items' ? stateSource.items : stateSource;
const JSONBase = { name: `UnFiltered ${JSONLabel} [ ${stateSource.items.length} ]`, jsonObject: useThisObject, collapsed: true, };
const AdjustedReactJSONSettings = { ...ReactJSONSettings, ...{ style: { paddingTop: '20px' } } };
const JSONSettings = AdjustedReactJSONSettings ? { ...JSONBase, ...AdjustedReactJSONSettings, } : JSONBase;
JsonElementU = FPSReactJSON(JSONSettings);
}
if (jsonFiltered === 'both' || jsonFiltered === 'filtered') {
const useThisObject = jsonObject === 'items' ? this.state.filtered : { ...stateSource, ...{ items: this.state.filtered } };
const JSONBase = { name: `Filtered ${JSONLabel} [ ${this.state.filtered.length} ]`, jsonObject: useThisObject, collapsed: true, };
const JSONSettings = ReactJSONSettings ? { ...JSONBase, ...ReactJSONSettings, } : JSONBase;
JsonElementF = FPSReactJSON(JSONSettings);
if (this.props.onParentRequestsFiltered) {
JsonElementF = React.createElement("div", { className: 'topSearch' },
JsonElementF,
this._onParentRequestsFilteredCmds.map((cmd, idx) => {
return React.createElement("div", { style: { paddingLeft: '2em', minWidth: '240px' } },
React.createElement("div", { style: { padding: '.5em 2em', height: '19px' }, className: `button${onParentDisabled === true ? ' isDisabled' : ''}`, onClick: onParentDisabled === true ? null : () => this._SendParentFiltered(cmd, idx) }, cmd),
React.createElement("div", { style: { padding: '1em 0em 0em 1em', paddingLeft: '1em' } }, this.state[`parentRequestStatus${idx}`]));
}));
}
}
}
return (React.createElement("div", { className: ['sourcePage', sourcePageClassName].join(' ') },
debugContent,
headingElement,
searchSourceDesc,
hasError === false ? searchBox : undefined,
hasError === false ? topSearchContent : undefined,
renderJSON === false && hasError === false && renderAsTable === true ? tableElement : undefined,
renderJSON === false && hasError === false && renderAsTable === false ? filtered : undefined,
JsonElementU,
JsonElementF,
noResults,
StatusElement,
footerElement));
}
_SendParentFiltered(cmd, idx) {
const timeNow = makeSmallTimeObject('');
const stateMessage = `${this.state.filtered.length} @ ${timeNow.now.toLocaleString()}`;
this.setState({ [`parentRequestStatus${idx}`]: stateMessage });
this.props.onParentRequestsFiltered(this.state.filtered, cmd, idx);
}
_multiCall(buttonIdx) {
// export const DefaultMultiSelectCommands: IMultiSelectCommand[] = [ 'Select Visible', 'Un-Select Visible', 'Clear All' ];
// this._multiButtonLabels
let command = null;
const sendThese = [];
if (this._multiButtonLabels[buttonIdx] === DefaultMultiSelectCommands[0]) {
// Selected Visible
command = DefaultMultiSelectCommands[0];
this._retainViewXdidUpdates = 1; // Skip render when Selecting or Un-Selecting Visible items
this.state.filtered.map((item, idx) => {
if (idx >= this.state.firstVisible && idx <= this.state.lastVisible)
sendThese.push(item);
});
}
else if (this._multiButtonLabels[buttonIdx] === DefaultMultiSelectCommands[1]) {
// Un-Selected Visible
command = DefaultMultiSelectCommands[1];
this._retainViewXdidUpdates = 1; // Skip render when Selecting or Un-Selecting Visible items
this.state.filtered.map((item, idx) => {
if (idx >= this.state.firstVisible && idx <= this.state.lastVisible)
sendThese.push(item);
});
}
else if (this._multiButtonLabels[buttonIdx] === DefaultMultiSelectCommands[2]) {
// Clear All
command = DefaultMultiSelectCommands[2];
this._retainViewXdidUpdates = 0; // Skip render when Selecting or Un-Selecting Visible items
}
else {
alert('SourcePages Error: _multiCall ~ 502');
}
if (this.props.multiSelectCall) {
this.props.multiSelectCall(command, sendThese);
}
else {
alert('SourcePages Error: _multiCall ~ 508');
}
}
_sendItemToParent(command, Id, type, item, event) {
const sendItemToParent = this.props.sendItemToParent;
if (!sendItemToParent) {
alert(`SourcePages _sendItemToParent:\n\nThere is no sendItemToParent function to call !!`);
return;
}
sendItemToParent(command, Id, type, item, event);
}
_parentCall(command, Id, type, item, retainViewXdidUpdates, event) {
if (retainViewXdidUpdates > 0)
this._retainViewXdidUpdates = retainViewXdidUpdates;
// As of 2023-10-08: Tested SPO Shift Click functionality and it is always 'Addative' and never shift Click unselects anything
const isShift = event && event.shiftKey ? true : false;
// This is a normal call:
// NOTE Have to test what happens if you hold Shift and click on the same item twice... Does it still keep selected?
// Need to verify both the visible status and the selected property of the item.
if (!this.props.onParentCallMultiClick || isShift === false || this.state.lastItemMultiClickIdx < 0) {
this.props.onParentCall(command, Id, type, item);
}
else { // This is a special call like shift-click
const selectedItems = this._getItemsBetweenIds(Id, this.state.lastItemMultiClickIdx, this.state.filtered);
this.props.onParentCallMultiClick(command, Id, type, selectedItems);
}
this.setState({ lastItemMultiClickIdx: Id });
}
// Id is the latest clicked item
_getItemsBetweenIds(Id1, Id2, items) {
const results = [];
let found1 = false;
let found2 = false;
let skipRest = false;
items.map((item, idx) => {
if (skipRest === false) {
if (item.Id === Id1)
found1 = true;
if (item.Id === Id2)
found2 = true;
// If this is either of the end points or in between, push it to results.
if (found1 === true || found2 === true)
results.push(item);
if (found1 === true && found2 === true)
skipRest = true;
}
});
return results;
}
_clickTop(itemC, event) {
const { stateSource, searchAgeProp, } = this.props; // canvasOptions
const { topSearch, enableAge, searchAge, searchText } = this.state;
const selected = this.toggleSearchInArray(topSearch, itemC, event.ctrlKey === true ? 'multi' : 'single');
console.log('_clickTop:', itemC, selected);
const ageFilteredItems = enableAge === false ? stateSource.items : [];
if (enableAge === true) {
stateSource.items.map((item) => {
if (item.FPSItem && item.FPSItem.Stamp)
if (item[searchAgeProp] > searchAge) {
ageFilteredItems.push(item);
}
});
}
const filtered = getFilteredItems(ageFilteredItems, searchText, [], [], [], selected);
let lastVisible = this._itemsPerPage;
if (check4This(Check4.filtered_Eq_true) === true)
console.log('filtered SourePage', lastVisible, filtered);
if (filtered.length < lastVisible)
lastVisible = filtered.length;
this.setState({
topSearch: selected,
filtered: filtered,
firstVisible: 0,
lastVisible: lastVisible - 1,
resetArrows: makeid(4),
});
}
// private _detailsToggle(): void {
// const newState = this.state.detailToggle === true ? false : true;
// this.setState({ detailToggle: newState });
// }
_setInfinate() {
const newSetting = this.state.infinateItems === true ? false : true;
this._itemsPerPage = newSetting === true ? 1000 : defaultMaxVisible;
let lastVisible = this.props.itemsPerPage ? this.props.itemsPerPage : defaultMaxVisible;
if (newSetting === true)
lastVisible = 1000;
if (this.state.filtered.length < lastVisible)
lastVisible = this.state.filtered.length;
this.setState({
firstVisible: 0,
lastVisible: lastVisible - 1,
refreshId: makeid(4),
resetArrows: makeid(4),
infinateItems: newSetting,
});
}
toggleSearchInArray(searchArray, value, doThis) {
let selected = JSON.parse(JSON.stringify(searchArray));
const idx = selected.indexOf(value);
if (doThis === 'multi') {
if (idx < 0) {
selected.push(value);
}
else {
delete selected[idx];
}
}
else if (doThis === 'single') {
if (selected.length > 1) {
selected = [value];
}
else if (idx < 0) {
selected = [value];
}
else if (idx > -1) {
selected = [];
}
else {
alert('toggleSearchInArrayError');
console.log('toggleSearchInArray Not triggered:', value, doThis, searchArray);
}
}
return selected;
}
/**
* https://www.kindacode.com/article/react-typescript-handling-onclick-event/
* React.MouseEvent<HTMLImageElement>
* @param NewSearch
*
*
* Found sample here:
* https://github.com/pnp/sp-dev-fx-webparts/blob/b139ba199cb57363a88f070dd9814e5af4fc3cbd/samples/react-my-sites/src/webparts/mySites/components/MySites/MySites.tsx#L168
* (event?: React.ChangeEvent<HTMLInputElement>, newValue?: string)
*/
_onAgeChange(ageIndex) {
const startTime = new Date();
const searchAge = AgeSliderOptions[Math.abs(ageIndex)].maxAge; //ageIndex is negative... needs inverse to get array element
// Defaults searchAgeProp to 'modifiedAge' if nothing is provided but ageSearch is enabled.
// Defaults searchAgeOp to 'show <' if nothing is provided but ageSearch is enabled.
const searchAgeProp = this.props.searchAgeProp ? this.props.searchAgeProp : 'modifiedAge';
const searchAgeOp = this.props.searchAgeOp ? this.props.searchAgeOp : 'show <';
const ageFilteredItems = this.state.enableAge === false ? this.props.stateSource.items : [];
if (this.state.enableAge === true) {
this.props.stateSource.items.map((item) => {
if (searchAgeOp === 'show >') {
if (item[searchAgeProp] > searchAge)
ageFilteredItems.push(item);
}
else if (searchAgeOp === 'show <') {
if (item[searchAgeProp] < searchAge)
ageFilteredItems.push(item);
}
});
}
const filtered = getFilteredItems(ageFilteredItems, this.state.searchText, [], [], [], this.state.topSearch);
const endTime = new Date();
const totalTime = endTime.getTime() - startTime.getTime();
let lastVisible = this.props.itemsPerPage ? this.props.itemsPerPage : defaultMaxVisible;
if (filtered.length < lastVisible)
lastVisible = filtered.length;
this.setState({ filtered: filtered,
searchText: this.state.searchText,
searchTime: totalTime,
firstVisible: 0,
lastVisible: lastVisible - 1,
refreshId: makeid(4),
resetArrows: makeid(4),
searchAge: searchAge,
});
}
_onSearchChange(event, NewSearch) {
const startTime = new Date();
const SearchValue = NewSearch;
const ageFilteredItems = this.state.enableAge === false ? this.props.stateSource.items : [];
if (this.state.enableAge === true) {
this.props.stateSource.items.map((item) => {
if (item[this.props.searchAgeProp] > this.state.searchAge)
ageFilteredItems.push(item);
});
}
const filtered = getFilteredItems(ageFilteredItems, NewSearch, [], [], [], this.state.topSearch);
const endTime = new Date();
const totalTime = endTime.getTime() - startTime.getTime();
let lastVisible = this.props.itemsPerPage ? this.props.itemsPerPage : defaultMaxVisible;
if (filtered.length < lastVisible)
lastVisible = filtered.length;
this.setState({ filtered: filtered,
searchText: !SearchValue ? '' : SearchValue,
searchTime: totalTime,
firstVisible: 0,
lastVisible: lastVisible - 1,
refreshId: makeid(4),
resetArrows: makeid(4),
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
clickListItem(ID, category, item, event) {
if (check4This(Check4.onClicks_Eq_true) === true)
console.log('onClicks clickNewsItem:', ID, item);
this.setState({ showItemPanel: true, showThisItem: item });
}
clickTextFilter(value, event) {
if (check4This(Check4.onClicks_Eq_true) === true)
console.log('onClicks clickTextFilter:', value);
this._onSearchChange(null, value);
}
clickPropFilter(prop, value, event) {
if (check4This(Check4.onClicks_Eq_true) === true)
console.log('onClicks clickPropFilter:', prop, value);
//Clear filter if it was already set to this value
if (value === this.state.searchText)
value = '';
this._onSearchChange(null, value);
}
_updateFirstLastVisible(firstVisible, lastVisible) {
this.setState({
firstVisible: firstVisible,
lastVisible: lastVisible,
});
}
}
//# sourceMappingURL=SourcePages.js.map