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

1. Repo restructuring - updated code

This commit is contained in:
Kushal Mehrotra 2021-08-08 11:49:09 +05:30
Родитель c468d88812
Коммит 2f19be0926
54 изменённых файлов: 27771 добавлений и 0 удалений

24
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,24 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.eslintcache

35
.npmignore Normal file
Просмотреть файл

@ -0,0 +1,35 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
/dist
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/.vs
/public
/src/Examples
/src/App.css
/src/App.tsx
/src/index.tsx
/src/index.css
/src/App.test.tsx
/.npmrc
/package-lock.json
/tsconfig.json

21
LICENSE Normal file
Просмотреть файл

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

224
README.md Normal file
Просмотреть файл

@ -0,0 +1,224 @@
# FluidUI Editable DetailsList
## Overview
FluentUI is a great UI library with some really cool controls, all adhering to Accessibility Standards.
DetailsList control of FluidUI is great when your requirement is a read-only grid. However, it does not offer any in-place editability functionality just yet.
This component(Editable DetailsList) is a wrapper over the existing DetailsList that makes in-place editability work like a dream(among many other new features).
Some of the features of the Editable Grid are:-
>- Single Cell Edit (in-place)
>- Single Row Edit (in-place)
>- Single Column Edit
>- Multi-Column, multi-row edit (Bulk Edit)
>- Full Edit (Edit Mode)
>- Deleting Rows
>- Adding Rows
>- Default Data Export (to Excel, CSV)
>- Implement Custom Export functionality
>- Callback hook to recieve grid data in the consuming component(for Save etc.)
>- Support for various controls in grid in-place edit like TextField, Multiline TextField, DatePicker (Support for Dropdown will be released soon)
>- 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
>- The component is completely Accessible
## Clone & Run
- clone the repository on your local machine.
- open the project
- open terminal and change directory to your project path
- type '***npm install***'
- after the installation is complete, type '***npm start***'
This starts the project on port 8080 and you are ready to play around with the Editable DetailsList
## NPM Install
npm i fluentui-editable-grid
## Usage
import { DetailsListLayoutMode, SelectionMode } from '@fluentui/react';
import { EditableGrid, EditControlType, IColumnConfig } from 'fluentui-editable-grid';
import { Fabric } from 'office-ui-fabric-react';
import * as React from 'react';
import { useState } from 'react';
const Consumer = () => {
const [items, setItems] = useState<any[]>([]);
const columns: IColumnConfig[] = [
{
key: 'id',
name: 'ID',
text: 'ID',
editable: false,
dataType: 'number',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
applyColumnFilter: true
},
{
key: 'name',
name: 'Name',
text: 'Name',
editable: true,
dataType: 'string',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
applyColumnFilter: true
},
{
key: 'age',
name: 'Age',
text: 'Age',
editable: true,
dataType: 'number',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
applyColumnFilter: true
},
{
key: 'designation',
name: 'Designation',
text: 'Designation',
editable: true,
dataType: 'string',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.MultilineTextField,
applyColumnFilter: true
},
{
key: 'salary',
name: 'Salary',
text: 'Salary',
editable: true,
dataType: 'number',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: false,
includeColumnInSearch: true,
maxLength:5,
applyColumnFilter: true
},
{
key: 'dateofjoining',
name: 'Date of Joining',
text: 'Date of Joining',
editable: true,
dataType: 'date',
minWidth: 150,
maxWidth: 150,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.Date
}
];
const SetDummyData = () : void => {
const dummyData = [
{
id: "1",
name: "Name1",
age:32,
designation:'Designation1',
salary:75000,
dateofjoining:'2010-04-01T14:57:10'
},
{
id: "2",
name: "Name2",
age:32,
designation:'Designation2',
salary:75000,
dateofjoining:'2014-06-09T14:57:10'
},
{
id: "3",
name: "Name3",
age:32,
designation:'Designation3',
salary:75000,
dateofjoining:'2005-07-02T14:57:10'
},
{
id: "4",
name: "Name4",
age:32,
designation:'Designation4',
salary:75000,
dateofjoining:'2019-04-01T14:57:10'
}
];
setItems(dummyData);
}
React.useEffect(() => {
SetDummyData();
}, []);
return (
<Fabric>
<EditableGrid
id={1}
columns={columns}
items={items}
enableCellEdit={true}
enableExport={true}
enableTextFieldEditMode={true}
enableTextFieldEditModeCancel={true}
enableGridRowsDelete={true}
enableGridRowsAdd={true}
height={'70vh'}
width={'140vh'}
position={'relative'}
enableUnsavedEditIndicator={true}
//onGridSave={onGridSave}
enableGridReset={true}
enableColumnFilters={true}
enableColumnFilterRules={true}
enableRowAddWithValues={{enable : true, enableRowsCounterInPanel : true}}
layoutMode={DetailsListLayoutMode.justified}
selectionMode={SelectionMode.multiple}
enableRowEdit={true}
enableRowEditCancel={true}
enableBulkEdit={true}
enableColumnEdit={true}
enableSave={true}
/>
</Fabric>
);
};
export default Consumer;
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
---
_For more details please check out [Fluent UI Editable DetailsList Wiki](https://github.com/microsoft/FluentUIEditableDetailsList/wiki)._

41
SECURITY.md Normal file
Просмотреть файл

@ -0,0 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->

25
SUPPORT.md Normal file
Просмотреть файл

@ -0,0 +1,25 @@
# TODO: The maintainer of this repo has not yet edited this file
**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project?
- **No CSS support:** Fill out this template with information about how to file issues and get help.
- **Yes CSS support:** Fill out an intake form at [aka.ms/spot](https://aka.ms/spot). CSS will work with/help you to determine next steps. More details also available at [aka.ms/onboardsupport](https://aka.ms/onboardsupport).
- **Not sure?** Fill out a SPOT intake as though the answer were "Yes". CSS will help you decide.
*Then remove this first heading from this SUPPORT.MD file before publishing your repo.*
# Support
## How to file issues and get help
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
issues before filing new issues to avoid duplicates. For new issues, file your bug or
feature request as a new Issue.
For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE
FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER
CHANNEL. WHERE WILL YOU HELP PEOPLE?**.
## Microsoft Support Policy
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.

23771
package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

93
package.json Normal file
Просмотреть файл

@ -0,0 +1,93 @@
{
"name": "fluentui-editable-grid",
"version": "1.1.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",
"typings": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/FluentUIEditableDetailsList"
},
"keywords": [
"editable",
"grid",
"detailslist",
"fluentui"
],
"bugs": "https://github.com/microsoft/FluentUIEditableDetailsList/issues",
"dependencies": {
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
"@babel/cli": "7.12.1",
"@babel/core": "^7.14.8",
"@babel/preset-env": "^7.14.9",
"@babel/preset-react": "^7.14.5",
"@babel/preset-stage-0": "^7.8.3",
"@fluentui/react": "^8.26.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"@types/file-saver": "^2.0.1",
"@types/jest": "^26.0.24",
"@types/node": "^12.20.17",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"cpx": "^1.5.0",
"file-saver": "^2.0.2",
"office-ui-fabric-react": "^7.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^4.0.3",
"typescript": "^4.3.5",
"web-vitals": "^1.1.2",
"webpack-cli": "^4.7.2",
"xlsx": "^0.16.6",
"y18n": "^3.2.2"
},
"devDependencies": {
"just-scripts": "^0.27.0",
"just-stack-react": "^1.0.0"
},
"scripts": {
"start": "react-scripts start",
"build-site": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"clean": "if exist dist rd /s /q dist",
"prepareBuild": "npm run clean && mkdir dist",
"compile": "set NODE_ENV=production & babel src/libs --out-dir dist --extensions \".ts,.tsx\" --copy-files",
"copy": "cpx \"{package.json,README.md}\" dist",
"build": "npm run prepareBuild && npm run compile && tsc && npm run copy",
"prepare": "npm run build"
},
"babel": {
"presets": [
"@babel/env",
"@babel/preset-react",
"@babel/preset-typescript"
],
"ignore": [
"**/*.test.js",
"**/stories.js",
"**/*.stories.js"
]
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Двоичные данные
public/favicon.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.8 KiB

38
public/index.html Normal file
Просмотреть файл

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

15
public/manifest.json Normal file
Просмотреть файл

@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

38
src/App.css Normal file
Просмотреть файл

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

9
src/App.test.tsx Normal file
Просмотреть файл

@ -0,0 +1,9 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { App } from './App';
it('renders "Welcome to Your Fluent UI App"', () => {
render(<App />);
const linkElement = screen.getByText(/Welcome to Your Fluent UI App/i);
expect(linkElement).toBeInTheDocument();
});

24
src/App.tsx Normal file
Просмотреть файл

@ -0,0 +1,24 @@
import React from 'react';
import { Stack, Text, Link, FontWeights, IStackTokens, IStackStyles, ITextStyles } from '@fluentui/react';
import logo from './logo.svg';
import './App.css';
import Consumer from './Examples/gridconsumer/gridconsumer';
const boldStyle: Partial<ITextStyles> = { root: { fontWeight: FontWeights.semibold } };
const stackTokens: IStackTokens = { childrenGap: 15 };
const stackStyles: Partial<IStackStyles> = {
root: {
width: '960px',
margin: '0 auto',
textAlign: 'center',
color: '#605e5c',
},
};
export const App: React.FunctionComponent = () => {
return (
<Stack horizontalAlign="center" verticalAlign="center" verticalFill styles={stackStyles} tokens={stackTokens}>
<Consumer />
</Stack>
);
};

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

@ -0,0 +1,109 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
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[] =
[
{
key: 'id',
name: 'ID',
text: 'ID',
editable: false,
dataType: 'number',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
applyColumnFilter: true
},
{
key: 'name',
name: 'Name',
text: 'Name',
editable: true,
dataType: 'string',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
applyColumnFilter: true
},
{
key: 'age',
name: 'Age',
text: 'Age',
editable: true,
dataType: 'number',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
applyColumnFilter: true
},
{
key: 'designation',
name: 'Designation',
text: 'Designation',
editable: true,
dataType: 'string',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.MultilineTextField,
applyColumnFilter: true
},
{
key: 'salary',
name: 'Salary',
text: 'Salary',
editable: true,
dataType: 'number',
minWidth: 100,
maxWidth: 100,
isResizable: true,
includeColumnInExport: false,
includeColumnInSearch: true,
maxLength:5,
applyColumnFilter: true,
cellStyleRule: {
enable: true,
rule: {
operator : NumberAndDateOperators.LESSTHAN,
value: 50000
},
whenTrue: { textColor: '#EF5350', fontWeight: 'bold' },
whenFalse: { textColor: '#9CCC65' }
}
},
{
key: 'dateofjoining',
name: 'Date of Joining',
text: 'Date of Joining',
editable: true,
dataType: 'date',
minWidth: 150,
maxWidth: 150,
isResizable: true,
includeColumnInExport: true,
includeColumnInSearch: true,
inputType: EditControlType.Date
}
];
export interface GridItemsType {
id: number;
name: string;
age: number;
designation: string;
salary: number;
dateofjoining: string;
};

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

@ -0,0 +1,111 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { DefaultButton, DetailsList, DetailsListLayoutMode, Fabric, mergeStyles, mergeStyleSets, SelectionMode, TextField } from 'office-ui-fabric-react';
import * as React from 'react';
import { useState } from 'react';
import EditableGrid from '../../libs/editablegrid/editablegrid';
import { ICallBackParams, ICallBackRequestParams } from '../../libs/types/callbackparams';
import { IColumnConfig } from '../../libs/types/columnconfigtype';
import { GridColumnConfig, GridItemsType } from './gridconfig';
import { EventEmitter, EventType } from '../../libs/eventemitter/EventEmitter.js';
const Consumer = () => {
const [items, setItems] = useState<GridItemsType[]>([]);
const classNames = mergeStyleSets({
controlWrapper: {
display: 'flex',
flexWrap: 'wrap',
}
});
const GetRandomDate = (start : Date, end : Date) : Date => {
var diff = end.getTime() - start.getTime();
var new_diff = diff * Math.random();
var date = new Date(start.getTime() + new_diff);
return date;
}
const GetRandomInt = (min : number, max : number) : number => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
const SetDummyData = () : void => {
var dummyData : GridItemsType[] = []
for(var i = 1; i <= 100; i++){
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'
});
}
setItems(dummyData);
}
React.useEffect(() => {
SetDummyData();
}, []);
const onGridSave = (data: any[]): void => {
alert('Grid Data Saved');
setItems([...data]);
};
const onDesignationChanged = (callbackRequestParamObj : ICallBackParams): any[] => {
callbackRequestParamObj.rowindex.forEach((index) => {
callbackRequestParamObj.data.filter((item) => item._grid_row_id_ == index).map((item) => item.salary = 30000);
});
return callbackRequestParamObj.data;
}
const attachGridValueChangeCallbacks = (columnConfig : IColumnConfig[]) : IColumnConfig[] => {
columnConfig.filter((item) => item.key == 'designation').map((item) => item.onChange = onDesignationChanged);
return columnConfig;
};
return (
<Fabric>
<div className={classNames.controlWrapper}>
<TextField placeholder='Search Grid' className={mergeStyles({ width: '60vh', paddingBottom:'10px' })} onChange={(event) => EventEmitter.dispatch(EventType.onSearch, event)}/>
</div>
<EditableGrid
id={1}
enableColumnEdit={true}
enableSave={true}
columns={attachGridValueChangeCallbacks(GridColumnConfig)}
layoutMode={DetailsListLayoutMode.justified}
selectionMode={SelectionMode.multiple}
enableRowEdit={true}
enableRowEditCancel={true}
enableBulkEdit={true}
items={items}
enableCellEdit={true}
enableExport={true}
enableTextFieldEditMode={true}
enableTextFieldEditModeCancel={true}
enableGridRowsDelete={true}
enableGridRowsAdd={true}
height={'70vh'}
width={'140vh'}
position={'relative'}
enableUnsavedEditIndicator={true}
onGridSave={onGridSave}
enableGridReset={true}
enableColumnFilters={true}
enableColumnFilterRules={true}
enableRowAddWithValues={{enable : true, enableRowsCounterInPanel : true}}
/>
</Fabric>
);
};
export default Consumer;

11
src/index.css Normal file
Просмотреть файл

@ -0,0 +1,11 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}

21
src/index.tsx Normal file
Просмотреть файл

@ -0,0 +1,21 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from './App';
import { mergeStyles } from '@fluentui/react';
import reportWebVitals from './reportWebVitals';
// Inject some global styles
mergeStyles({
':global(body,html,#root)': {
margin: 0,
padding: 0,
height: '100vh',
},
});
ReactDOM.render(<App />, document.getElementById('root'));
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

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

@ -0,0 +1,98 @@
import { ConstrainMode, DatePicker, IStackStyles, IStackTokens, 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";
interface Props {
onChange: any;
columnConfigurationData: IColumnConfig[];
enableRowsCounterField?: boolean;
}
const AddRowPanel = (props: Props) => {
let AddSpinRef: any = React.createRef();
const updateObj : any = {};
const onTextUpdate = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text: string): void => {
updateObj[(ev.target as Element).id] = text;
//console.log(updateObj);
};
const onPanelSubmit = (): void => {
props.onChange(updateObj, props.enableRowsCounterField ? AddSpinRef.current.value : 1);
};
const onCellDateChange = (date: Date | null | undefined, item : any): void => {
updateObj[item.key] = date;
};
const createTextFields = () : any[] => {
let tmpRenderObj : any[] = [];
props.columnConfigurationData.forEach((item, index) => {
switch(item.inputType){
case EditControlType.Date:
tmpRenderObj.push(<DatePicker
label={item.text}
strings={DayPickerStrings}
placeholder="Select a date..."
ariaLabel="Select a date"
onSelectDate={(date) => onCellDateChange(date, item)}
//value={props != null && props.panelValues != null ? new Date(props.panelValues[item.key]) : new Date()}
value={new Date()}
/>);
break;
default:
tmpRenderObj.push(<TextField
name={item.text}
id={item.key}
label={item.text}
styles={textFieldStyles}
onChange={(ev, text) => onTextUpdate(ev, text!)}
defaultValue = { '' }
/>);
break;
}
});
if(props.enableRowsCounterField){
tmpRenderObj.push(
<SpinButton
componentRef = {AddSpinRef}
label="# of Rows to Add"
labelPosition={Position.top}
defaultValue="0"
min={0}
max={100}
step={1}
incrementButtonAriaLabel="Increase value by 1"
decrementButtonAriaLabel="Decrease value by 1"
styles={{ spinButtonWrapper: { width: 75 } }}
/>
);
}
console.log(tmpRenderObj);
return tmpRenderObj;
}
return (
<Stack>
<Stack tokens={verticalGapStackTokens}>
{createTextFields()}
</Stack>
<Stack horizontal disableShrink styles={stackStyles} tokens={horizontalGapStackTokens}>
<PrimaryButton
text="Save To Grid"
className={controlClass.submitStylesEditpanel}
onClick={onPanelSubmit}
allowDisabledFocus
/>
</Stack>
</Stack>
);
};
export default AddRowPanel;

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

@ -0,0 +1,35 @@
import { Callout, DirectionalHint } from "office-ui-fabric-react";
import React from "react";
import { useId } from '@uifabric/react-hooks';
import { styles } from "./filtercalloutstyles";
import FilterList from "./filterlist";
import { IFilterCalloutProps } from "../../types/columnfilterstype";
interface Props extends IFilterCalloutProps {
onApply: any;
onCancel: any;
}
const FilterCallout = (props : Props) => {
const labelId: string = useId('callout-label');
const descriptionId: string = useId('callout-description');
console.log(props.columnClass);
return(
<>
<Callout
className={styles.callout}
ariaLabelledBy={labelId}
ariaDescribedBy={descriptionId}
role="filtercallout"
gapSpace={5}
target={`.${props.columnClass}`}
isBeakVisible={true}
directionalHint={DirectionalHint.topCenter}
>
<FilterList onCancel={props.onCancel} onApply={props.onApply} columnKey={props.columnKey} columnName={props.columnName} filterList={props.filterList} />
</Callout>
</>
);
}
export default FilterCallout;

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

@ -0,0 +1,8 @@
import { mergeStyleSets } from "office-ui-fabric-react";
export const styles = mergeStyleSets({
callout: {
maxWidth: 500,
padding: 30
}
});

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

@ -0,0 +1,189 @@
import { ActionButton, Checkbox, DefaultButton, Dropdown, mergeStyles, PrimaryButton, ScrollablePane, ScrollbarVisibility, Stack, TextField } from "office-ui-fabric-react";
import React, { useEffect, useState } from "react";
import { IFilterCalloutProps, IFilterItem, IFilterListItem, IFilterListProps } from "../../types/columnfilterstype";
import { styles, stackTokens } from "./filterliststyles";
interface Props extends IFilterListProps {
onApply: any;
onCancel: any;
}
const FilterList = (props : Props) => {
const [filterItemsList, setFilterItemsList] = React.useState<IFilterListItem[]>([]);
const [filterListContent, setFilterListContent] = React.useState<JSX.Element[] | undefined>([]);
const [appliedFilters, setAppliedFilters] = React.useState<IFilterItem[]>([]);
const [isSelectAllIndeterminate, setIsSelectAllIndeterminate] = React.useState(true);
const [isSelectAllChecked, setIsSelectAllChecked] = React.useState(true);
useEffect(() => {
debugger;
if(props && props.filterList && props.filterList.length > 0){
setFilterItemsList(props.filterList.map((item, index) => {
return {key: index, text : item.text, isFilteredIn : true, isChecked : item.isChecked};
}))
setAppliedFilters(props.filterList.map((item, index) => {
return {text : item.text, isChecked : item.isChecked};
}));
}
else{
setFilterItemsList([]);
}
},[props.filterList])
useEffect(() => {
}, [appliedFilters]);
useEffect(() => {
debugger;
SetIndeterminate(filterItemsList);
if(filterItemsList && filterItemsList.length > 0){
let tmpRenderObj : any[] = [];
filterItemsList.filter((item) => item.isFilteredIn == true).forEach((item, index) => {
tmpRenderObj.push(<Checkbox
label={item.text}
key={item.key}
onChange={(ev, checked) => onCheckChanged(ev!, checked!, item.key!, item.text)}
//defaultChecked={item.isChecked}
className={styles.checkbox}
checked={item.isChecked}
/>);
});
setFilterListContent(tmpRenderObj);
}
else{
setFilterListContent(undefined);
}
},[filterItemsList])
const SetIndeterminate = (filterItemsList : IFilterListItem[]) : void => {
var checkedCount = filterItemsList.filter((item) => item.isChecked == true).length;
var totalCount = filterItemsList.length;
var uncheckedCount = totalCount - checkedCount;
if(checkedCount == totalCount){
setIsSelectAllIndeterminate(false);
setIsSelectAllChecked(true);
}
else if(uncheckedCount == totalCount){
setIsSelectAllIndeterminate(false);
setIsSelectAllChecked(false);
}
else{
setIsSelectAllIndeterminate(true);
setIsSelectAllChecked(false);
}
}
function onCheckChanged(ev: React.FormEvent<HTMLElement>, isChecked: boolean, key : number, text: string) {
debugger;
var filterItemsListTmp : IFilterListItem[] = [...filterItemsList];
filterItemsListTmp.filter((item) => item.key == key).map((item) => item.isChecked = isChecked);
setFilterItemsList(filterItemsListTmp);
var appliedFiltersTmp : IFilterItem[] = [...appliedFilters];
appliedFiltersTmp.filter((item) => item.text == text).map((item) => item.isChecked = isChecked);
setAppliedFilters(appliedFiltersTmp);
}
const onSelectAllCheckChanged = (ev: React.FormEvent<HTMLElement>, isChecked: boolean) : void => {
var filterItemsListTmp : IFilterListItem[] = [...filterItemsList];
filterItemsListTmp.map((item) => item.isChecked = isChecked);
setFilterItemsList(filterItemsListTmp);
var appliedFiltersTmp : IFilterItem[] = [...appliedFilters];
appliedFiltersTmp.map((item) => item.isChecked = isChecked);
setAppliedFilters(appliedFiltersTmp);
}
const onReset = (): void => {
var filterItemsListTmp : IFilterListItem[] = [...filterItemsList];
filterItemsListTmp.map((item) => item.isChecked = false);
setFilterItemsList(filterItemsListTmp);
var appliedFiltersTmp : IFilterItem[] = [...appliedFilters];
appliedFiltersTmp.map((item) => item.isChecked = false);
setAppliedFilters(appliedFiltersTmp);
};
const onApply = (): void => {
if(props.onApply){
var onApplyParams : IFilterListProps = { columnKey: props.columnKey, columnName: props.columnName, filterList: appliedFilters }
props.onApply(onApplyParams);
}
};
const onFilterTextUpdate = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text: string | undefined): void => {
if(text){
let searchResult : IFilterListItem[] = [...filterItemsList];
searchResult.filter(
(_data, index) => {
var BreakException = {};
try{
if(_data.text.toString().toLowerCase().includes(text.trim().toLowerCase())){
_data.isFilteredIn = true;
throw BreakException;
}
else{
_data.isFilteredIn = false;
}
} catch (e) {
// if (e !== BreakException) throw e;
}
}
);
setFilterItemsList(searchResult);
}
else{
var filterItemsListTmp : IFilterListItem[] = [...filterItemsList];
filterItemsListTmp.map((item) => item.isFilteredIn = true);
setFilterItemsList(filterItemsListTmp);
}
};
return(
<>
<Stack verticalAlign="start" tokens={stackTokens}>
<TextField
placeholder={`Filter ${props.columnName}`}
onChange={(ev, text) => onFilterTextUpdate(ev, text)}
/>
<div className={mergeStyles({ height: '25vh', width: '30vh', position: 'relative', backgroundColor: 'white' })}>
<ScrollablePane scrollbarVisibility={ScrollbarVisibility.auto}>
<Checkbox
label="(Select All)"
key={'SelectAll'}
indeterminate={isSelectAllIndeterminate}
checked={isSelectAllChecked}
className={styles.selectAllCheckbox}
onChange={(ev, checked) => onSelectAllCheckChanged(ev!, checked!)}
/>
{filterListContent}
</ScrollablePane>
</div>
<Stack horizontal horizontalAlign="start">
<ActionButton
className={styles.button}
onClick={onApply}
text="Apply" />
<ActionButton
text="Clear All"
className={styles.button}
onClick={onReset}
disabled={appliedFilters.filter((item) => item.isChecked == true).length == 0} />
<ActionButton
text="Cancel"
className={styles.button}
onClick={props.onCancel} />
</Stack>
</Stack>
</>
);
};
export default FilterList;

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

@ -0,0 +1,15 @@
import { IStackTokens, mergeStyleSets } from "office-ui-fabric-react";
export const stackTokens: IStackTokens = { childrenGap: 20, maxWidth:1000 };
export const styles = mergeStyleSets({
checkbox: {
padding: 5,
},
selectAllCheckbox:{
padding: 5
},
button: {
margin: 10
}
});

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

@ -0,0 +1,221 @@
import { DefaultButton, Dialog, DialogFooter, Dropdown, IDialogStyleProps, IDialogStyles, IDropdownOption, IDropdownStyles, IStackTokens, ITextFieldStyles, mergeStyleSets, PrimaryButton, Stack, TextField } from "office-ui-fabric-react";
import React, { useEffect, useState } from "react";
import { IColumnConfig } from "../../types/columnconfigtype";
import { IFilter, IOperators, operatorsArr } from "../../types/filterstype";
import { controlClass, dropdownStyles, modelProps, stackTokens, textFieldStyles } from "./columnfilterdialogStyles";
interface Props {
columnConfigurationData: IColumnConfig[];
gridData: any[];
onDialogCancel?: any;
onDialogSave?: any;
}
const ColumnFilterDialog = (props : Props) => {
//const [gridColumn, setGridColumn] = useState('');
const [gridColumn, setGridColumn] = useState<IColumnConfig>();
const [operator, setOperator] = useState('');
const [value, setValue] = useState('');
//const [filter, setFilter] = useState<IFilter>({ column:'', operator:'', value: '' });
const onSelectGridColumn = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined, index: number | undefined): void => {
console.log(item)
//setGridColumn(item!.key.toString());
setGridColumn(props.columnConfigurationData.filter((val) => val.key == item!.key)[0]);
// var filterTmp : IFilter = {...filter};
// filterTmp.column = item!.key.toString();
// setFilter(filterTmp);
};
const onSelectOperator = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined, index: number | undefined): void => {
debugger;
console.log(item)
setOperator(item!.text.toString());
//var filterTmp : IFilter = {...filter};
//filterTmp.operator = item!.text.toString();
//setFilter(filterTmp);
};
const onSelectValue = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined, index: number | undefined): void => {
console.log(item)
setValue(item!.key.toString());
// var filterTmp : IFilter = {...filter};
// filterTmp.value = item!.key.toString();
// setFilter(filterTmp);
};
const onTextUpdate = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text: string): void => {
console.log('Text Changed: ' + text);
setValue(text);
// var filterTmp : IFilter = {...filter};
// filterTmp.value = text;
// setFilter(filterTmp);
};
useEffect(() => {
if(gridColumn && gridColumn.key && gridColumn.key.length > 0){
var column = props.columnConfigurationData.filter(x => x.key == gridColumn!.key);
if(column.length > 0){
var valueOptions = createValueOptions(column[0]);
switch(column[0].dataType){
case 'number':
setInputFieldContent(
<TextField
className={controlClass.textFieldClass}
placeholder="Value"
onChange={(ev, text) => onTextUpdate(ev, text!)}
styles={textFieldStyles}
/>
);
setOperatorDropDownContent(<Dropdown
placeholder="Select the Column"
options={createCompareOptions()}
styles={dropdownStyles}
onChange={onSelectOperator}
/>);
break;
case 'string':
setInputFieldContent(
<TextField
className={controlClass.textFieldClass}
placeholder="Value"
onChange={(ev, text) => onTextUpdate(ev, text!)}
styles={textFieldStyles}
/>
);
setOperatorDropDownContent(<Dropdown
placeholder="Select the Column"
options={createCompareOptions()}
styles={dropdownStyles}
onChange={onSelectOperator}
/>);
break;
case 'date':
setInputFieldContent(<Dropdown
placeholder="Select the Column"
options={valueOptions}
styles={dropdownStyles}
onChange={onSelectValue}
/>)
setOperatorDropDownContent(<Dropdown
placeholder="Select the Column"
options={createCompareOptions()}
styles={dropdownStyles}
onChange={onSelectOperator}
/>);
break;
}
}
}
}, [gridColumn]);
const createDropDownOptions = () : IDropdownOption[] => {
let dropdownOptions: IDropdownOption[] = [];
props.columnConfigurationData.forEach((item, index) => {
dropdownOptions.push({ key: item.key, text: item.text});
});
return dropdownOptions;
}
const options = createDropDownOptions();
const createCompareOptions = () : IDropdownOption[] => {
debugger;
if(!(gridColumn && gridColumn.key && gridColumn.key.length > 0)){
return [];
}
let dataType = props.columnConfigurationData.filter(x => x.key == gridColumn.key)[0].dataType;
let dropdownOptions: IDropdownOption[] = [];
let operatorsOptions : any[] = [];
switch(dataType){
case 'string':
operatorsOptions = operatorsArr.filter((item) => item.type == 'string')[0].value;
break;
case 'number':
operatorsOptions = operatorsArr.filter((item) => item.type == 'number')[0].value;
break;
}
operatorsOptions.forEach((item, index) => {
dropdownOptions.push({ key: item+index, text: item});
});
return dropdownOptions;
}
const createValueOptions = (column : IColumnConfig) : IDropdownOption[] => {
var columnData = props.gridData.map((item) => item[column.key]);
let dropdownOptions: IDropdownOption[] = [];
columnData.forEach((item, index) => {
dropdownOptions.push({ key: item+index, text: item});
});
return dropdownOptions;
};
//const compareOptions = createCompareOptions();
const [inputFieldContent, setInputFieldContent] = React.useState<JSX.Element | undefined>(
<Dropdown
placeholder="Select the Column"
options={options}
styles={dropdownStyles}
onChange={onSelectValue}
/>
);
const [operatorDropDownContent, setOperatorDropDownContent] = React.useState<JSX.Element | undefined>(
<Dropdown
placeholder="Select the Column"
options={createCompareOptions()}
styles={dropdownStyles}
onChange={onSelectValue}
/>
);
const closeDialog = React.useCallback((): void => {
if(props.onDialogCancel){
props.onDialogCancel();
}
setInputFieldContent(undefined)
}, []);
const saveDialog = (): void => {
debugger;
var filterObj : IFilter = { column: gridColumn!, operator: operator, value: value }
if(props.onDialogSave){
props.onDialogSave(filterObj);
}
setInputFieldContent(undefined);
};
return(
<Dialog modalProps={modelProps} hidden={!inputFieldContent} onDismiss={closeDialog} closeButtonAriaLabel="Close">
<Stack verticalAlign="start" tokens={stackTokens}>
<Dropdown
placeholder="Select the Column"
options={options}
styles={dropdownStyles}
onChange={onSelectGridColumn}
/>
{operatorDropDownContent}
{inputFieldContent}
</Stack>
<DialogFooter>
<PrimaryButton
// eslint-disable-next-line react/jsx-no-bind
onClick={saveDialog}
text="Save"
/>
<DefaultButton
//onClick={closeDialog}
text="Cancel" />
</DialogFooter>
</Dialog>
);
}
export default ColumnFilterDialog;

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

@ -0,0 +1,25 @@
import { IDropdownStyles, IStackTokens, ITextFieldStyles, mergeStyleSets } from "office-ui-fabric-react";
export const dropdownStyles: Partial<IDropdownStyles> = {
dropdown: { width: '90%', margin:10 },
};
export const controlClass = mergeStyleSets({
textFieldClass:{
display: 'block',
margin: 10,
width: '90%'
},
datePickerClass:{
display: 'block',
margin: 10,
width: '90%'
}
});
export const stackTokens: IStackTokens = { childrenGap: 20, maxWidth:1000 };
export const textFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: {} };
export const modelProps = {
isBlocking: false,
styles: { main: { maxWidth: '100vh' }},
};

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

@ -0,0 +1,161 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { DatePicker, DefaultButton, Dialog, DialogFooter, Dropdown, IDropdownOption, IDropdownStyles, IStackTokens, ITextFieldStyles, mergeStyleSets, PrimaryButton, Stack, TextField } from "office-ui-fabric-react";
import React, { useEffect, useState } from "react";
import { IColumnConfig } from "../types/columnconfigtype";
import { EditControlType } from "../types/editcontroltype";
import { DayPickerStrings } from "./datepickerconfig";
interface Props {
columnConfigurationData: IColumnConfig[];
onDialogCancel?: any;
onDialogSave?: any;
}
const ColumnUpdateDialog = (props : Props) => {
const controlClass = mergeStyleSets({
textFieldClass:{
display: 'block',
margin: 10,
width: '90%'
},
datePickerClass:{
display: 'block',
margin: 10,
width: '90%'
}
});
const textFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: {} };
const [gridColumn, setGridColumn] = useState('');
const [columnDialogValues, setColumnDialogValues] = useState({
});
const stackTokens: IStackTokens = { childrenGap: 10 };
const dropdownStyles: Partial<IDropdownStyles> = {
dropdown: { width: '90%', margin:10 },
};
const onTextUpdate = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text: string): void => {
console.log('Text Changed: ' + text);
setColumnDialogValues({[gridColumn]: text});
};
const [inputFieldContent, setInputFieldContent] = React.useState<JSX.Element | undefined>(
<TextField
className={controlClass.textFieldClass}
placeholder="Value"
onChange={(ev, text) => onTextUpdate(ev, text!)}
styles={textFieldStyles}
/>
);
const onSelectDate = (date: Date | null | undefined): void => {
setColumnDialogValues({[gridColumn] : date!.toDateString()});
};
const onSelectGridColumn = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined): void => {
console.log(item)
setGridColumn(item!.key.toString());
};
const closeDialog = React.useCallback((): void => {
if(props.onDialogCancel){
props.onDialogCancel();
}
setInputFieldContent(undefined)
}, []);
const saveDialog = (): void => {
debugger;
if(props.onDialogSave){
props.onDialogSave(columnDialogValues);
}
setInputFieldContent(undefined);
};
const createDropDownOptions = () : IDropdownOption[] => {
let dropdownOptions: IDropdownOption[] = [];
props.columnConfigurationData.forEach((item, index) => {
if(item.editable == true){
dropdownOptions.push({ key: item.key, text: item.text});
}
});
return dropdownOptions;
}
const options = createDropDownOptions();
useEffect(() => {
},[columnDialogValues]);
useEffect(() => {
//debugger;
setColumnDialogValues({[gridColumn]:''});
var column = props.columnConfigurationData.filter(x => x.key == gridColumn);
if(column.length > 0){
switch(column[0].inputType){
case EditControlType.TextField:
setInputFieldContent(
<TextField
className={controlClass.textFieldClass}
placeholder="Value"
onChange={(ev, text) => onTextUpdate(ev, text!)}
styles={textFieldStyles}
/>
);
break;
case EditControlType.Date:
setInputFieldContent(<DatePicker
strings={DayPickerStrings}
placeholder="Select a date..."
ariaLabel="Select a date"
className={controlClass.datePickerClass}
onSelectDate={onSelectDate}
/>);
break;
default:
setInputFieldContent(
<TextField
className={controlClass.textFieldClass}
placeholder="Value"
onChange={(ev, text) => onTextUpdate(ev, text!)}
styles={textFieldStyles}
/>
);
break;
}
}
}, [gridColumn]);
return(
<Dialog hidden={!inputFieldContent} onDismiss={closeDialog} closeButtonAriaLabel="Close">
<Stack verticalAlign="start" tokens={stackTokens}>
<Dropdown
placeholder="Select the Column"
options={options}
styles={dropdownStyles}
onChange={onSelectGridColumn}
/>
{inputFieldContent}
</Stack>
<DialogFooter>
<PrimaryButton
// eslint-disable-next-line react/jsx-no-bind
onClick={saveDialog}
text="Save"
/>
<DefaultButton onClick={closeDialog} text="Cancel" />
</DialogFooter>
</Dialog>
);
};
export default ColumnUpdateDialog;

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

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IDatePickerStrings } from "office-ui-fabric-react";
export const DayPickerStrings: IDatePickerStrings = {
months: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
shortDays: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
goToToday: 'Go to today',
prevMonthAriaLabel: 'Go to previous month',
nextMonthAriaLabel: 'Go to next month',
prevYearAriaLabel: 'Go to previous year',
nextYearAriaLabel: 'Go to next year',
closeButtonAriaLabel: 'Close date picker',
isRequiredErrorMessage: 'Field is required.',
invalidInputErrorMessage: 'Invalid date format.',
};
export function dateToISOLikeButLocal(date: any) {
const offsetMs = date.getTimezoneOffset() * 60 * 1000;
const msLocal = date.getTime() - offsetMs;
const dateLocal = new Date(msLocal);
const iso = dateLocal.toISOString();
const isoLocal = iso.slice(0, 10);
return isoLocal;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IColumn, ICommandBarItemProps, IconButton, TextField } from "office-ui-fabric-react";
import React from "react";
import { IColumnConfig } from "../types/columnconfigtype";
import { Operation } from "../types/operation";
import { controlClass, textFieldStyles } from "./editablegridstyles";
import { initializeIcons } from 'office-ui-fabric-react';
import { EditControlType } from "../types/editcontroltype";
initializeIcons(/* optional base url */);
export const InitializeInternalGrid = (items : any[]) : any[] => {
return items.map((obj, index) => {
if(Object.keys(obj).indexOf('_grid_row_id_') == -1 && Object.keys(obj).indexOf('_grid_row_operation_') == -1)
{
obj._grid_row_id_ = index;
obj._grid_row_operation_ = Operation.None;
obj._is_filtered_in_ = true;
obj._is_filtered_in_grid_search_ = true;
obj._is_filtered_in_column_filter_ = true;
}
return obj;
})
};
export const ResetGridRowID = (items : any[]) : any[] => {
return items.map((obj, index) => {
obj._grid_row_id_ = index;
return obj;
});
};
export const InitializeInternalGridEditStructure = (items : any[]) : any[] => {
let activateCellEditTmp : any[] = [];
items.forEach((item, index) => {
let activateCellEditRowTmp : any = {'isActivated' : false, properties : {}};
var objectKeys = Object.keys(item);
objectKeys.forEach((objKey) => {
activateCellEditRowTmp.properties[objKey] = {'activated' : false, 'value' : item[objKey]};
})
activateCellEditTmp.push(activateCellEditRowTmp);
});
console.log(activateCellEditTmp);
return activateCellEditTmp;
};
export const ShallowCopyDefaultGridToEditGrid = (defaultGrid : any[], editGrid : any[]) : any[] => {
debugger;
defaultGrid.forEach((item, index) => {
var objectKeys = Object.keys(item);
objectKeys.forEach((objKey) => {
editGrid[index].properties[objKey]['value'] = item[objKey];
})
});
return editGrid;
};
export const ShallowCopyEditGridToDefaultGrid = (defaultGrid : any[], editGrid : any[]) : any[] => {
editGrid.forEach((item) => {
var index = defaultGrid.findIndex((row) => row._grid_row_id_ == item.properties._grid_row_id_.value);
if(index >= 0){
var objectKeys = Object.keys(item.properties);
objectKeys.forEach((objKey) => {
if(defaultGrid[index][objKey] != item.properties[objKey].value){
defaultGrid[index][objKey] = item.properties[objKey].value;
if(defaultGrid[index]['_grid_row_operation_'] != Operation.Add && defaultGrid[index]['_grid_row_operation_'] != Operation.Update){
defaultGrid[index]['_grid_row_operation_'] = Operation.Update;
}
}
})
}
});
return defaultGrid;
};

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

@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { getTheme, IDetailsColumnStyles, IStackStyles, IStackTokens, ITextFieldStyles, mergeStyleSets } from "office-ui-fabric-react";
import { ICellStyleRulesType } from "../types/cellstyleruletype";
import { IColumnConfig } from "../types/columnconfigtype";
import { EvaluateRule } from "./helper";
export const stackStyles: Partial<IStackStyles> = { root: { width: 500 } };
export const controlClass = mergeStyleSets({
control: {
marginBottom: '10px',
marginRight: '30px',
maxWidth: '300px',
},
searchStyles: {
marginTop: '5px',
},
submitStyles: {
marginTop: '20px',
marginLeft: '10px',
},
buttonStyles: {
margin: 5
},
textFieldClass:{
display: 'block',
margin: 10
},
spanStyles:{
display:'inline-block',
width:'100%',
height:'100%',
//lineHeight:'250%'
},
dialogSubMessageStyles : {
margin: 10,
},
dialogHeaderStyles : {
margin: 10,
},
submitStylesEditpanel: {
marginTop: '20px',
marginLeft: '10px',
marginRight: '10px',
maxWidth: '300px',
},
labelValue: {
fontWeight: 'bold',
}
});
export const GetDynamicSpanStyles = (column : IColumnConfig, cellValue : number | string | undefined) : string => {
var styleRule = column.cellStyleRule ?? undefined;
var isRuleTrue : boolean = EvaluateRule(column.dataType ?? 'string', cellValue, styleRule);
var styles = mergeStyleSets({
dynamicSpanStyle: {
display:'inline-block',
width:'100%',
height:'100%',
//textAlign:'center',
color:(!column.cellStyleRule || !column.cellStyleRule.enable) ? undefined : (isRuleTrue ? styleRule?.whenTrue?.textColor : styleRule?.whenFalse?.textColor),
//backgroundColor: (!column.cellStyleRule || !column.cellStyleRule.enable) ? undefined : (isRuleTrue ? styleRule?.whenTrue?.backgroundColor : styleRule?.whenFalse?.backgroundColor),
//lineHeight:'250%',
fontWeight:(!column.cellStyleRule || !column.cellStyleRule.enable) ? undefined : (isRuleTrue ? styleRule?.whenTrue?.fontWeight : styleRule?.whenFalse?.fontWeight)
}
});
return styles.dynamicSpanStyle;
}
export const verticalGapStackTokens: IStackTokens = {
childrenGap: 15,
padding: 10,
};
export const horizontalGapStackTokens: IStackTokens = {
childrenGap: 10,
padding: 10,
};
export const textFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: {} };

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

@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { DatePicker, IStackStyles, IStackTokens, 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";
interface Props {
onChange: any;
columnConfigurationData: IColumnConfig[];
}
const EditPanel = (props: Props) => {
const updateObj : any = {};
const onTextUpdate = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text: string, column : IColumnConfig): void => {
debugger;
if(!IsValidDataType(column.dataType, text) || text.trim() == ''){
return;
}
updateObj[(ev.target as Element).id] = text;
};
const onPanelSubmit = (): void => {
console.log(updateObj);
props.onChange(updateObj);
};
const onCellDateChange = (date: Date | null | undefined, item : any): void => {
updateObj[item.key] = date;
};
const createTextFields = () : any[] => {
let tmpRenderObj : any[] = [];
props.columnConfigurationData.forEach((item, index) => {
if(item.editable == true){
switch(item.inputType){
case EditControlType.Date:
tmpRenderObj.push(<DatePicker
label={item.text}
strings={DayPickerStrings}
placeholder="Select a date..."
ariaLabel="Select a date"
onSelectDate={(date) => onCellDateChange(date, item)}
//value={props != null && props.panelValues != null ? new Date(props.panelValues[item.key]) : new Date()}
value={new Date()}
/>);
break;
default:
tmpRenderObj.push(<TextField
name={item.text}
id={item.key}
label={item.text}
styles={textFieldStyles}
onChange={(ev, text) => onTextUpdate(ev, text!, item)}
defaultValue = { '' }
/>);
break;
}
}
});
console.log(tmpRenderObj);
return tmpRenderObj;
}
return (
<Stack>
<Stack tokens={verticalGapStackTokens}>
{createTextFields()}
</Stack>
<Stack horizontal disableShrink styles={stackStyles} tokens={horizontalGapStackTokens}>
<PrimaryButton
text="Save To Grid"
className={controlClass.submitStylesEditpanel}
onClick={onPanelSubmit}
allowDisabledFocus
/>
</Stack>
</Stack>
);
};
export default EditPanel;

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

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { ExportType } from "../types/exporttype";
import * as XLSX from 'xlsx';
import * as FileSaver from 'file-saver';
export const ExportToExcelUtil = (exportData : any[], fileName : string): void =>
{
let fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8';
const ws = XLSX.utils.json_to_sheet(exportData);
const wb = { Sheets: { 'data': ws }, SheetNames: ['data'] };
const excelBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
const data = new Blob([excelBuffer], {type: fileType});
FileSaver.saveAs(data, fileName);
}
export const ExportToCSVUtil = (exportData : any[], fileName : string) : void => {
if (!exportData || !exportData.length) {
return;
}
const separator = ',';
const keys = Object.keys(exportData[0]);
const csvContent =
keys.join(separator) +
'\n' +
exportData.map(row => {
return keys.map(k => {
let cell = row[k] === null || row[k] === undefined ? '' : row[k];
cell = cell instanceof Date
? cell.toLocaleString()
: cell.toString().replace(/"/g, '""');
if (cell.search(/("|,|\n)/g) >= 0) {
cell = `"${cell}"`;
}
return cell;
}).join(separator);
}).join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
if (navigator.msSaveBlob) { // IE 10+
navigator.msSaveBlob(blob, fileName);
} else {
const link = document.createElement('a');
if (link.download !== undefined) {
// Browsers that support HTML5 download attribute
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', fileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}

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

@ -0,0 +1,88 @@
import { ICellStyleRulesType } from "../types/cellstyleruletype";
import { IGridColumnFilter } from "../types/columnfilterstype";
import { dateOperatorEval, IFilter, numberOperatorEval, stringOperatorEval } from "../types/filterstype";
export const filterGridData = (data : any[], filters : IFilter[]) : any[] => {
debugger;
var dataTmp : any[] = [...data];
dataTmp.forEach((row) => {
var isRowIncluded : boolean = true;
filters.forEach((item) => {
if(isRowIncluded){
var columnType = item.column.dataType;
switch(columnType){
case 'number':
isRowIncluded = isRowIncluded && numberOperatorEval(row[item.column.key], item.value, item.operator);
break;
case 'string':
isRowIncluded = isRowIncluded && stringOperatorEval(row[item.column.key], item.value, item.operator);
break;
}
}
});
if(isRowIncluded){
row._is_filtered_in_ = true;
}
else{
row._is_filtered_in_ = false;
}
});
return dataTmp;
}
export const applyGridColumnFilter = (data : any[], gridColumnFilterArr : IGridColumnFilter[]) : any[] => {
debugger;
var dataTmp : any[] = [...data];
if(gridColumnFilterArr.filter((item) => item.isApplied == true).length > 0){
dataTmp.map((row) => row._is_filtered_in_column_filter_ = true);
}
gridColumnFilterArr.filter((gridColumnFilter) => gridColumnFilter.isApplied == true).forEach((gridColumnFilter, index) => {
dataTmp.filter((row) => row._is_filtered_in_column_filter_ == true).forEach((row, i) => {
row._is_filtered_in_column_filter_ = gridColumnFilter.filterCalloutProps!.filterList.filter(a => a.isChecked == true).map(a => a.text).includes(row[gridColumnFilter.column.key]);
});
});
return dataTmp;
}
export const isColumnDataTypeSupportedForFilter = (datatype : string | undefined) : boolean => {
switch(datatype){
case 'number':
return true;
case 'string':
return true;
default:
return false;
}
}
export const IsValidDataType = (type : string | undefined, text : string) : boolean => {
var isValid = true;
switch(type){
case 'number':
isValid = !isNaN(Number(text));
break;
}
return isValid;
};
export const EvaluateRule = (datatType : string, cellValue: string | number | undefined, styleRule: ICellStyleRulesType | undefined): boolean => {
if(!styleRule){
return false;
}
switch(datatType){
case 'number':
return numberOperatorEval(Number(cellValue), styleRule?.rule!.value as number, styleRule?.rule!.operator);
case 'string':
return stringOperatorEval(String(cellValue), styleRule?.rule!.value as string, styleRule?.rule!.operator)
case 'date':
return dateOperatorEval(new Date(String(cellValue)), new Date(styleRule?.rule!.value), styleRule?.rule!.operator);
default:
return false;
}
}

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

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { DefaultButton, Dialog, DialogFooter, mergeStyleSets } from "office-ui-fabric-react";
import React, { useEffect } from "react";
interface Props {
message?: string;
subMessage?: string;
onDialogClose?: any;
}
const MessageDialog = (props : Props) => {
const [messageDialogContent, setMessageDialogContent] = React.useState<JSX.Element | undefined>(undefined);
const closeDialog = React.useCallback((): void => {
if(props.onDialogClose){
props.onDialogClose();
}
setMessageDialogContent(undefined);
}, []);
const controlClass = mergeStyleSets({
dialogSubMessageStyles : {
margin: 10,
},
dialogHeaderStyles : {
margin: 10,
}
});
useEffect(() => {
setMessageDialogContent(
<>
<div>
<h3 className={controlClass.dialogHeaderStyles}>{props && props.message ? props.message : ''}</h3>
<div className={controlClass.dialogSubMessageStyles}>
{props && props.subMessage ? props.subMessage : ''}
</div>
</div>
<DialogFooter>
<DefaultButton onClick={() => closeDialog()} text="Close" />
</DialogFooter>
</>,
);
}, [props]);
return(
<Dialog hidden={!messageDialogContent} onDismiss={closeDialog} closeButtonAriaLabel="Close">
{messageDialogContent}
</Dialog>
);
};
export default MessageDialog;

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

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export const EventType = {
onSearch: 'onSearch',
onClick: 'onClick'
};
export const EventEmitter = {
events: {},
subscribe: function(event, callback) {
if (!this.events[event]) this.events[event] = [];
if (this.events[event].some(c => c === callback)) {
console.warn(event + ' Already subscribed by ');
console.log(callback);
return;
}
this.events[event].push(callback);
},
unsubscribe: function(event, callback) {
this.events[event] = this.events[event].filter(c => c !== callback);
},
dispatch: function(event, data) {
if (!this.events[event]) return;
this.events[event].forEach(callback => callback(data));
}
};

8
src/libs/index.tsx Normal file
Просмотреть файл

@ -0,0 +1,8 @@
export { default as EditableGrid } from "./editablegrid/editablegrid";
export type { ICallBackParams, ICallBackRequestParams } from "./types/callbackparams";
export type { IColumnConfig } from "./types/columnconfigtype";
export { EditControlType } from "./types/editcontroltype";
export type { IGridItemsType } from "./types/griditemstype";
export { EventEmitter, EventType } from "./eventemitter/EventEmitter";
export { NumberAndDateOperators, StringOperators } from './types/cellstyleruletype';
export type { ICellStyleRulesType } from './types/cellstyleruletype';

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

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export enum CalculationType {
Addition,
Subtraction,
Multiplication,
Division
}

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

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export interface ICallBackRequestParams extends ICallBackParams {
callbackhook : any;
}
export interface ICallBackParams {
data : any[];
rowindex: Number[];
triggerkey: string;
activatetriggercell: boolean;
}

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

@ -0,0 +1,39 @@
export interface ICellStyleRulesType {
enable?: boolean;
rule?: IStringCellValueRule | INumberCellValueRule;
whenTrue?: ICellStyle;
whenFalse?: ICellStyle;
}
export interface IStringCellValueRule {
operator: StringOperators;
value: string;
}
export interface INumberCellValueRule {
operator: NumberAndDateOperators;
value: number | Date;
}
export enum StringOperators {
EQUALS = 'equals',
CONTAINS = 'contains',
STARTSWITH = 'starts with',
ENDSWITH = 'ends with',
NOTEQUALTO = 'not equal to'
}
export enum NumberAndDateOperators {
GREATERTHAN = '>',
LESSTHAN = '<',
GREATERTHANOREQUALTO = '>=',
LESSTHANOREQUALTO = '<=',
EQUALTO = '=',
NOTEQUALTO = '!='
}
export interface ICellStyle {
textColor?: string;
//backgroundColor: string;
fontWeight?: string;
}

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

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { ConstrainMode, IColumn, IDetailsHeaderProps } from 'office-ui-fabric-react/lib/components/DetailsList/DetailsList.types';
import { CalculationType } from "./calculationtype";
import { ICellStyleRulesType } from './cellstyleruletype';
import { EditControlType } from "./editcontroltype";
export interface IColumnConfig extends IColumn {
key: string;
text: string;
editable?: boolean;
dataType?: string;
isResizable?: boolean;
includeColumnInExport?: boolean;
includeColumnInSearch?: boolean;
inputType?: EditControlType;
calculatedColumn?: { type: CalculationType, fields: any[] };
onChange?: any;
maxLength?: number;
applyColumnFilter?: boolean;
cellStyleRule?: ICellStyleRulesType;
};

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

@ -0,0 +1,34 @@
import { IColumnConfig } from "./columnconfigtype";
export interface IFilterItem {
text: any;
isChecked: boolean;
}
export interface IFilterListItem extends IFilterItem {
key : number;
isFilteredIn : boolean;
}
export interface IFilterListProps {
columnKey: string;
columnName: string;
filterList : IFilterItem[];
}
export interface IFilterCalloutProps extends IFilterListProps {
columnClass: string;
}
export interface IGridColumnFilter {
index: number;
column: IColumnConfig;
isApplied: boolean;
isHidden: boolean;
filterCalloutProps?: IFilterCalloutProps;
}
export interface IColumnFilterValues{
column: IColumnConfig;
value: any[];
}

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

@ -0,0 +1,39 @@
import { ConstrainMode } from "office-ui-fabric-react/lib/components/DetailsList";
import { IDetailsListProps } from "office-ui-fabric-react/lib/components/DetailsList/DetailsList";
import { IColumnConfig } from "./columnconfigtype";
import { IRowAddWithValues } from "./rowaddtype";
export interface Props extends IDetailsListProps {
id: number;
items: any[];
columns: IColumnConfig[];
enableExport?: boolean;
exportFileName?: string;
enableSave?: boolean;
enableRowEdit?: boolean;
enableRowEditCancel?: boolean;
enableColumnEdit?: boolean;
enableBulkEdit?: boolean;
enableCellEdit?: boolean;
onGridSelectionChange?: any;
onGridUpdate?:any;
onGridSave?:any
enableGridRowsDelete? : boolean;
enableGridRowsAdd?: boolean;
enableRowAddWithValues?: IRowAddWithValues;
enableTextFieldEditMode?: boolean;
enableTextFieldEditModeCancel?: boolean;
enablePagination?: boolean;
pageSize?: number;
onExcelExport?: any;
height?: string;
width? : string;
position?: string;
constrainMode?:ConstrainMode;
enableUnsavedEditIndicator?: boolean;
enableGridReset?: boolean;
enableColumnFilterRules?: boolean;
enableColumnFilters?: boolean;
enableCommandBar?: boolean;
enableSingleClickCellEdit?: boolean;
}

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

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export enum EditControlType {
None,
TextField,
DropDown,
Date,
MultilineTextField,
DateTime
}

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

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export enum EditType {
None,
ColumnEdit,
BulkEdit,
RowEdit,
DeleteRow,
AddRow,
ColumnFilter,
AddRowWithData
}

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

@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export enum ExportType {
XLSX,
CSV,
}

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

@ -0,0 +1,92 @@
import { IColumnConfig } from "./columnconfigtype";
export interface IOperators{
type: string;
value: string[];
}
export interface IFilter{
column: IColumnConfig;
operator: string;
value: any;
}
export const operatorsArr : IOperators[] = [
{
type:'string',
value:['equals', 'contains', 'starts with', 'ends with', 'not equal to']
},
{
type:'number',
value:['>', '<', '>=', '<=', '=', '!=']
}
]
export const operatorEval1 = {
'equals': (a : string, b : string) : boolean => { return a == b },
'contains': (a : string, b : string) : boolean => { return a.indexOf(b) >= 0 },
'starts with': (a : string, b : string) : boolean => { return a.startsWith(b) },
'ends with': (a : string, b : string) : boolean => { return a.endsWith(b) },
'not equal to': (a : string, b : string) : boolean => { return a != b },
'>': (a : number, b : number) : boolean => { return a > b },
'<': (a : number, b : number) : boolean => { return a < b },
'>=': (a : number, b : number) : boolean => { return a >= b },
'<=': (a : number, b : number) : boolean => { return a <= b },
'=': (a : number, b : number) : boolean => { return a == b },
'!=': (a : number, b : number) : boolean => { return a != b },
}
export const numberOperatorEval = (var1 : number, var2 : number, operator : string) : boolean => {
switch(operator){
case '>':
return var1 > var2;
case '<':
return var1 < var2;
case '>=':
return var1 >= var2;
case '<=':
return var1 <= var2;
case '=':
return var1 == var2;
case '!=':
return var1 != var2;
default:
return false;
}
}
export const dateOperatorEval = (var1 : Date, var2 : Date, operator : string) : boolean => {
switch(operator){
case '>':
return var1 > var2;
case '<':
return var1 < var2;
case '>=':
return var1 >= var2;
case '<=':
return var1 <= var2;
case '=':
return var1 == var2;
case '!=':
return var1 != var2;
default:
return false;
}
}
export const stringOperatorEval = (var1 : string, var2 : string, operator : string) : boolean => {
switch(operator){
case 'equals':
return var1 == var2;
case 'contains':
return var1.indexOf(var2) >= 0;
case 'starts with':
return var1.startsWith(var2);
case 'ends with':
return var1.endsWith(var2);
case 'not equal to':
return var1 != var2;
default:
return false;
}
}

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

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { Operation } from "./operation";
export interface IGridItemsType {
_grid_row_id_ : number;
_grid_row_operation_ : Operation;
};

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

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export enum Operation{
None = 1,
Add,
Update,
Delete
}

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

@ -0,0 +1,4 @@
export interface IRowAddWithValues {
enable? : boolean;
enableRowsCounterInPanel? : boolean;
}

8
src/logo.svg Normal file
Просмотреть файл

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path
d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z" />
<circle cx="420.9" cy="296.5" r="45.7" />
<path d="M520.5 78.1z" />
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 2.6 KiB

1
src/react-app-env.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
/// <reference types="react-scripts" />

15
src/reportWebVitals.ts Normal file
Просмотреть файл

@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.ts Normal file
Просмотреть файл

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

29
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"downlevelIteration": true,
"declaration": true,
"outDir": "dist"
},
"include": [
"src/libs"
]
}