1. Repo restructuring - updated code
This commit is contained in:
Родитель
c468d88812
Коммит
2f19be0926
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)._
|
|
@ -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 -->
|
|
@ -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.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.8 KiB |
|
@ -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>
|
|
@ -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"
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
});
|
|
@ -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;
|
|
@ -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;
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 |
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="react-scripts" />
|
|
@ -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;
|
|
@ -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';
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче