chore: add project
This commit is contained in:
Родитель
be2ea1a893
Коммит
ae7d1c333e
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "kendo-react-template-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@progress/kendo-data-query": "latest",
|
||||
"@progress/kendo-drawing": "latest",
|
||||
"@progress/kendo-react-charts": "latest",
|
||||
"@progress/kendo-react-dateinputs": "latest",
|
||||
"@progress/kendo-react-dialogs": "latest",
|
||||
"@progress/kendo-react-dropdowns": "latest",
|
||||
"@progress/kendo-react-grid": "latest",
|
||||
"@progress/kendo-react-inputs": "latest",
|
||||
"@progress/kendo-react-intl": "latest",
|
||||
"@progress/kendo-react-layout": "latest",
|
||||
"@progress/kendo-react-tooltip": "latest",
|
||||
"@progress/kendo-theme-default": "latest",
|
||||
"bootstrap": "^4.3.1",
|
||||
"hammerjs": "^2.0.8",
|
||||
"node-sass": "^4.12.0",
|
||||
"react": "^16.9.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-scripts": "3.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import React, { useState } from 'react';
|
||||
import './App.scss';
|
||||
import { Header } from './components/Header'
|
||||
import { BrowserRouter as Router, Route } from "react-router-dom";
|
||||
import Home from './components/Home';
|
||||
import GridPage from './components/GridPage';
|
||||
import ChartPage from './components/ChartPage';
|
||||
import FormsPage from './components/FormsPage';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
const App = () => {
|
||||
const [theme, setTheme] = useState("https://unpkg.com/@progress/kendo-theme-default@latest/dist/all.css");
|
||||
|
||||
const changeTheme = (theme) => {
|
||||
setTheme(theme);
|
||||
}
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<link rel="stylesheet" href={theme}></link>
|
||||
<div className="content">
|
||||
<Header projectName={'Project Name'} />
|
||||
<Route exact path="/" component={Home} key={1}/>
|
||||
<Route exact path="/grid" render={(props) => <GridPage {...props} changeTheme={changeTheme} theme={theme}/>} key={2}/>
|
||||
<Route exact path="/chart" render={(props) => <ChartPage {...props} changeTheme={changeTheme} theme={theme}/>} key={3}/>
|
||||
<Route exact path="/forms" render={(props) => <FormsPage {...props} changeTheme={changeTheme} theme={theme}/>} key={4}/>
|
||||
</div>
|
||||
<div className="footer">
|
||||
<Footer />
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,154 @@
|
|||
@import './styles/bootstrap-custom.scss';
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
.app-header>.project-name{
|
||||
text-align: left;
|
||||
letter-spacing: 0;
|
||||
color: #000000;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.app-header>.nav{
|
||||
float: righnpmt;
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
text-align: center;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0;
|
||||
color: #000000;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.message{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sub-header {
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
color: #919191;
|
||||
opacity: 1;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.get-started {
|
||||
text-align: center;
|
||||
font-weight: 900;
|
||||
font-size: 78px;
|
||||
letter-spacing: 0;
|
||||
color: #000000;
|
||||
opacity: 1;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.kendoka {
|
||||
width: 12.5rem;
|
||||
height: 12.5rem;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.change-theme {
|
||||
font-size: 14px;
|
||||
color: #9098A5;
|
||||
}
|
||||
|
||||
.components-list>p>a {
|
||||
margin-left: 0.6em;
|
||||
}
|
||||
.components-list>p>img {
|
||||
width: 1.25rem;
|
||||
height: 1.3125rem;
|
||||
}
|
||||
.footer {
|
||||
font-size: 11px;
|
||||
background: #646871 0% 0% no-repeat padding-box;
|
||||
opacity: 1;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
.footer .links {
|
||||
text-align: left;
|
||||
letter-spacing: 0.33px;
|
||||
color: #FFFFFF;
|
||||
font-weight: 700;
|
||||
opacity: 1;
|
||||
}
|
||||
.divider {
|
||||
margin-left: 0.4rem;
|
||||
margin-right: 0.4rem;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.copyright{
|
||||
letter-spacing: 0.33px;
|
||||
color: #FFFFFF;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.k-widget.k-menu {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.k-widget.k-dropdown, .k-list .k-item {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.social img {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
.k-menu:not(.k-context-menu)>.k-item>.k-link::before {
|
||||
pointer-events: none;
|
||||
}
|
||||
a, a:visited {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
}
|
||||
.k-tooltip {
|
||||
width: 15rem;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,69 @@
|
|||
import React, { useState } from 'react';
|
||||
import {
|
||||
Chart,
|
||||
ChartLegend,
|
||||
ChartSeries,
|
||||
ChartSeriesItem,
|
||||
ChartTitle,
|
||||
ChartValueAxis,
|
||||
ChartValueAxisItem
|
||||
} from '@progress/kendo-react-charts'
|
||||
import ThemeChooser from './ThemeChooser';
|
||||
|
||||
const series = [
|
||||
{ category: 'EUROPE', value: 0.3 },
|
||||
{ category: 'NORTH AMERICA', value: 0.23 },
|
||||
{ category: 'AUSTRALIA', value: 0.18 },
|
||||
{ category: 'ASIA', value: 0.15 },
|
||||
{ category: 'SOUTH AMERICA', value: 0.09 },
|
||||
{ category: 'AFRICA', value: 0.05 }
|
||||
];
|
||||
const loadingPanel = (
|
||||
<div className="k-loading-mask">
|
||||
<span className="k-loading-text">Loading</span>
|
||||
<div className="k-loading-image"></div>
|
||||
<div className="k-loading-color"></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ChartPage = (props) => {
|
||||
const [theme, setTheme] = useState(props.theme);
|
||||
const [loading, setLoading] = useState(false)
|
||||
if (props.theme !== theme) {
|
||||
setTheme(props.theme);
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
}, 100);
|
||||
}
|
||||
return (
|
||||
<div className='container-fluid'>
|
||||
<ThemeChooser changeTheme={props.changeTheme} theme={props.theme}/>
|
||||
<div className='row my-4'>
|
||||
<div className='col-12 col-lg-9 border-right' key={props.theme + Math.random()}>
|
||||
{loading && loadingPanel}
|
||||
{!loading && <Chart theme='sass' transitions={false}>
|
||||
{/*<ChartTooltip /> uncomment to enable Tooltips */}
|
||||
<ChartTitle text="Production" font="19pt sans-serif" margin={{ bottom: 50 }} />
|
||||
<ChartLegend position="bottom" />
|
||||
<ChartSeries>
|
||||
<ChartSeriesItem type="column" data={series} field="value" categoryField="category" />
|
||||
</ChartSeries>
|
||||
<ChartValueAxis>
|
||||
<ChartValueAxisItem labels={{ format: 'p0' }} />
|
||||
</ChartValueAxis>}
|
||||
</Chart>}
|
||||
</div>
|
||||
<div className='col-12 col-lg-3 mt-3 mt-lg-0'>
|
||||
<h3>KendoReact Chart</h3>
|
||||
<p>The KendoReact Charts provide high quality graphical data visualization options.</p>
|
||||
<p>They include a variety of chart types and styles that have extensive configuration options. This flexibility allows you to quickly and easily create the exact chart you need to fit your specific requirements for functionality and appearance.</p>
|
||||
<p>The KendoReact Chart Components are built from the ground up, specifically for React, so that you get high performance chart components that integrate with your React app and with the rest of your React UI components.</p>
|
||||
<p>For documentation and demos of all available Chart types (Bar, Line, Donut, Sparkline, StockChart etc), please visit the <a href="https://www.telerik.com/kendo-react-ui/components/charts/?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-trial-charts">KendoReact Chart documentation page.</a> </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChartPage;
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
|
||||
const span = (<span className='divider'> | </span>)
|
||||
const Footer = (props) => {
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<div className='d-flex'>
|
||||
<div>
|
||||
<div className='mb-2 links'>
|
||||
<a href="https://www.telerik.com/purchase/license-agreement/progress-kendoreact?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-awareness-license-agreement">License Agreement</a>
|
||||
</div>
|
||||
<div className='copyright'>
|
||||
Copyright © 2019 Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
||||
</div>
|
||||
</div>
|
||||
<div className='d-flex ml-auto'>
|
||||
<div className="social">
|
||||
<a href="https://www.facebook.com/KendoUI/"><img src="./img/facebook.png" alt="facebook" /></a>
|
||||
<a href="https://twitter.com/kendoreact"><img src="./img/twitter.png" alt="twitter" /></a>
|
||||
<a href="https://www.youtube.com/results?search_query=kendoreact"><img src="./img/youtube.png" alt="youtube" /></a>
|
||||
<a href="https://www.linkedin.com/showcase/telerik/"><img src="./img/linkedin.png" alt="linkedin" /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer;
|
|
@ -0,0 +1,123 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Input } from '@progress/kendo-react-inputs';
|
||||
import { DatePicker } from '@progress/kendo-react-dateinputs';
|
||||
import { Dialog, DialogActionsBar } from '@progress/kendo-react-dialogs';
|
||||
import ThemeChooser from './ThemeChooser';
|
||||
const FormsPage = (props) => {
|
||||
const [formData, setFormData] = useState({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
dateOfBirth: new Date(),
|
||||
email: '',
|
||||
company: '',
|
||||
userName: '',
|
||||
password: '',
|
||||
twoFactor: false
|
||||
});
|
||||
const [showDialog, setShowDialog ] = useState(false)
|
||||
const clearForm = () => {
|
||||
setFormData({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
dateOfBirth: new Date(),
|
||||
email: '',
|
||||
company: '',
|
||||
userName: '',
|
||||
password: '',
|
||||
twoFactor: false
|
||||
});
|
||||
}
|
||||
|
||||
const onFormFieldChange = (e, field) => {
|
||||
setFormData({ ...formData, [field]: e.target.value });
|
||||
}
|
||||
|
||||
const toggleDialog = () => {
|
||||
setShowDialog(!showDialog);
|
||||
}
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
clearForm();
|
||||
setShowDialog(!showDialog);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<ThemeChooser changeTheme={props.changeTheme} theme={props.theme}/>
|
||||
{showDialog && <Dialog onClose={toggleDialog}>
|
||||
<p style={{ margin: "25px", textAlign: "center" }}>The form is successfully submitted!</p>
|
||||
<DialogActionsBar>
|
||||
<button className="k-button" onClick={toggleDialog}>OK</button>
|
||||
</DialogActionsBar>
|
||||
</Dialog>}
|
||||
<div className='row my-4'>
|
||||
<div className='col-12 col-lg-9 border-right'>
|
||||
<div className="row example-wrapper">
|
||||
<div className="col-xs-12 col-sm-6 offset-sm-3 example-col">
|
||||
<div className="card">
|
||||
<div className="card-block">
|
||||
<form className="k-form" onSubmit={handleSubmit}>
|
||||
<fieldset>
|
||||
<legend>User Details</legend>
|
||||
|
||||
<label className="k-form-field">
|
||||
<span>First Name</span>
|
||||
<Input placeholder="Your Name" value={formData.firstName} onChange={(e) => onFormFieldChange(e, "firstName")} />
|
||||
</label>
|
||||
<label className="k-form-field">
|
||||
<span>Last Name</span>
|
||||
<Input placeholder="Your Last Name" value={formData.lastName} onChange={(e) => onFormFieldChange(e, "lastName")} />
|
||||
</label>
|
||||
<div className="k-form-field">
|
||||
<span>Date of Birth</span>
|
||||
<DatePicker value={formData.dateOfBirth} onChange={(e) => onFormFieldChange(e, "dateOfBirth")} />
|
||||
</div>
|
||||
<label className="k-form-field">
|
||||
<span>Email <span className="k-required">*</span></span>
|
||||
<Input type="email" required={true} placeholder="Your Email" value={formData.email} onChange={(e) => onFormFieldChange(e, "email")} />
|
||||
</label>
|
||||
<label className="k-form-field">
|
||||
<span>Company<span className="k-field-info">optional</span></span>
|
||||
<Input placeholder="Your Company" value={formData.company} onChange={(e) => onFormFieldChange(e, "company")} />
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Credentials</legend>
|
||||
<label className="k-form-field">
|
||||
<span>Username</span>
|
||||
<Input required={true} placeholder="Your username" value={formData.userName} onChange={(e) => onFormFieldChange(e, "userName")} />
|
||||
</label>
|
||||
<label className="k-form-field">
|
||||
<span>Password</span>
|
||||
<Input type="password" required={true} placeholder="Your password" value={formData.password} onChange={(e) => onFormFieldChange(e, "password")} />
|
||||
</label>
|
||||
<label className="k-form-field">
|
||||
<input type="checkbox" id="auth-2fa" className="k-checkbox" checked={formData.twoFactor} onChange={(e)=> setFormData({...formData, twoFactor: e.target.checked})}/>
|
||||
<label className="k-checkbox-label" htmlFor="auth-2fa">Enable two-factor authentication</label>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div className="text-right">
|
||||
<button type="button" className="k-button" onClick={clearForm}>Clear</button>
|
||||
<button type="submit" className="k-button k-primary">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='col-12 col-lg-3 mt-3 mt-lg-0'>
|
||||
<h3>KendoReact Forms</h3>
|
||||
<p>KendoReact includes a wide offering of UI components that can be used to build forms, including CSS classes to easily create and structure gorgeous forms.</p>
|
||||
<p>The required inputs get validated upon form submission and if the validation fails, the form submission is prevented. Out of the box, KendoReact delivers components which support the HTML5 form validation and also provide props for configuring a set of minimal requirements for a component to be in a valid state.</p>
|
||||
<p>For documentation and demos of the many form friendly components please visit their documentation(<a href="https://www.telerik.com/kendo-react-ui/components/dateinputs/?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-trial-dateinputs">Date Inputs</a>, <a href="https://www.telerik.com/kendo-react-ui/components/dropdowns/?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-trial-dropdowns">DropDowns</a>, <a href="https://www.telerik.com/kendo-react-ui/components/inputs/?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-trial-inputs">Inputs</a> etc).</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormsPage;
|
|
@ -0,0 +1,157 @@
|
|||
import React, { useState } from 'react';
|
||||
import { sampleProducts } from '../common/sample-products';
|
||||
import { MyCommandCell } from './MyCommandCell.jsx';
|
||||
import { Grid, GridColumn as Column, GridToolbar } from '@progress/kendo-react-grid';
|
||||
import { process } from '@progress/kendo-data-query';
|
||||
import ThemeChooser from './ThemeChooser';
|
||||
|
||||
const GridPage = (props) => {
|
||||
const editField = "inEdit";
|
||||
const [data, setData] = useState(sampleProducts);
|
||||
const [dataState, setDataState ] = useState({skip: 0, take: 10 })
|
||||
|
||||
const generateId = data => data.reduce((acc, current) => Math.max(acc, current.ProductID), 0) + 1;
|
||||
|
||||
const removeItem = (data, item) => {
|
||||
let index = data.findIndex(p => p === item || item.ProductID && p.ProductID === item.ProductID);
|
||||
if (index >= 0) {
|
||||
data.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const enterEdit = (dataItem) => {
|
||||
setData(data.map(item =>
|
||||
item.ProductID === dataItem.ProductID ?
|
||||
{ ...item, inEdit: true } : item
|
||||
));
|
||||
}
|
||||
|
||||
const remove = (dataItem) => {
|
||||
|
||||
const newData = [...data];
|
||||
removeItem(newData, dataItem);
|
||||
removeItem(sampleProducts, dataItem);
|
||||
setData([...newData]);
|
||||
}
|
||||
|
||||
const add = (dataItem) => {
|
||||
dataItem.inEdit = undefined;
|
||||
dataItem.ProductID = generateId(sampleProducts);
|
||||
|
||||
sampleProducts.unshift(dataItem);
|
||||
setData([...data])
|
||||
}
|
||||
|
||||
const discard = (dataItem) => {
|
||||
const newData = [data];
|
||||
removeItem(newData, dataItem);
|
||||
|
||||
setData(newData);
|
||||
}
|
||||
|
||||
const update = (dataItem) => {
|
||||
const newData = [...data]
|
||||
const updatedItem = { ...dataItem, inEdit: undefined };
|
||||
|
||||
updateItem(newData, updatedItem);
|
||||
updateItem(sampleProducts, updatedItem);
|
||||
|
||||
setData(newData);
|
||||
}
|
||||
|
||||
const cancel = (dataItem) => {
|
||||
const originalItem = sampleProducts.find(p => p.ProductID === dataItem.ProductID);
|
||||
const newData = data.map(item => item.ProductID === originalItem.ProductID ? originalItem : item);
|
||||
|
||||
setData(newData);
|
||||
}
|
||||
|
||||
const updateItem = (data, item) => {
|
||||
let index = data.findIndex(p => p === item || (item.ProductID && p.ProductID === item.ProductID));
|
||||
if (index >= 0) {
|
||||
data[index] = { ...item };
|
||||
}
|
||||
}
|
||||
|
||||
const itemChange = (event) => {
|
||||
const newData = data.map(item =>
|
||||
item.ProductID === event.dataItem.ProductID ?
|
||||
{ ...item, [event.field]: event.value } : item
|
||||
);
|
||||
setData(newData);
|
||||
}
|
||||
|
||||
const addNew = () => {
|
||||
const newDataItem = { inEdit: true, Discontinued: false };
|
||||
setData([newDataItem, ...data]);
|
||||
}
|
||||
|
||||
const cancelCurrentChanges = () => {
|
||||
setData([...sampleProducts]);
|
||||
}
|
||||
let CommandCell = MyCommandCell({
|
||||
edit: enterEdit,
|
||||
remove: remove,
|
||||
|
||||
add: add,
|
||||
discard: discard,
|
||||
|
||||
update: update,
|
||||
cancel: cancel,
|
||||
|
||||
editField: editField
|
||||
});
|
||||
const hasEditedItem = data.some(p => p.inEdit);
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<ThemeChooser changeTheme={props.changeTheme} theme={props.theme}/>
|
||||
<div className='row my-4'>
|
||||
<div className='col-12 col-lg-9 border-right'>
|
||||
<Grid
|
||||
data={process(data, dataState)}
|
||||
onItemChange={itemChange}
|
||||
editField={editField}
|
||||
// pageable // uncomment to enable paging
|
||||
// sortable // uncomment to enable sorting
|
||||
// filterable // uncomment to enable filtering
|
||||
// onDataStateChange={(e) => setDataState(e.data)} // uncomment to enable data operations
|
||||
// {...dataState} // uncomment to enable data operations
|
||||
>
|
||||
<GridToolbar>
|
||||
<button
|
||||
title="Add new"
|
||||
className="k-button k-primary"
|
||||
onClick={addNew}
|
||||
>
|
||||
Add new
|
||||
</button>
|
||||
{hasEditedItem && (
|
||||
<button
|
||||
title="Cancel current changes"
|
||||
className="k-button"
|
||||
onClick={cancelCurrentChanges}
|
||||
>
|
||||
Cancel current changes
|
||||
</button>
|
||||
)}
|
||||
</GridToolbar>
|
||||
<Column field="ProductID" title="Id" width="50px" editable={false} />
|
||||
<Column field="ProductName" title="Product Name" />
|
||||
<Column field="FirstOrderedOn" title="First Ordered" editor="date" format="{0:d}" />
|
||||
<Column field="UnitsInStock" title="Units" width="150px" editor="numeric" />
|
||||
<Column field="Discontinued" title="Discontinued" editor="boolean" />
|
||||
<Column cell={CommandCell} width="240px" />
|
||||
</Grid>
|
||||
</div>
|
||||
<div className='col-12 col-lg-3 mt-3 mt-lg-0'>
|
||||
<h3>KendoReact Grid</h3>
|
||||
<p>The KendoReact Data Grid (Table) provides 100+ ready-to-use features covering everything from paging, sorting, filtering, editing, and grouping to row and column virtualization, export to PDF and Excel and accessibility.</p>
|
||||
<p>For documentation and demos of all available Grid features (filtering, sorting, paging, editing etc), please visit the <a href="https://www.telerik.com/kendo-react-ui/components/grid/?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-trial-grid">KendoReact Grid documentation page.</a> </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GridPage;
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react'
|
||||
import MenuNavContainer from './MenuNavContainer.jsx'
|
||||
|
||||
export const Header = (props) => {
|
||||
return (
|
||||
<div className='container-fluid'>
|
||||
<div className="d-flex border-bottom py-2">
|
||||
<div className="d-flex">
|
||||
<div className='project-name d-flex'>
|
||||
<span className="mx-0 my-auto logo">
|
||||
{props.projectName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='d-flex ml-auto nav'>
|
||||
<MenuNavContainer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import React from 'react';
|
||||
|
||||
const Home = (props) => {
|
||||
return (
|
||||
<div className="container mt-5">
|
||||
<div className='row'>
|
||||
<div className='col-12'>
|
||||
<h1 className='welcome mb-0'>Welcome to KendoReact</h1>
|
||||
<h2 className='sub-header mt-0'>This is a sample application built with KendoReact - a set of over 60 UI components built from the ground-up specifically for React.</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className='row'>
|
||||
<div className='col-12'>
|
||||
<h1 className='get-started'>Get Started</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className='row justify-content-center'>
|
||||
<div className='col-4 text-right'>
|
||||
<div>
|
||||
<img className='kendoka' src='./img/kendoka.png' alt='kendoka' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='col-4 components-list'>
|
||||
<p>
|
||||
<img src='./img/components.png' alt='components' />
|
||||
<a href='https://www.telerik.com/kendo-react-ui/components/?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-trial-docs-and-demos'>Components & Documentation</a>
|
||||
</p>
|
||||
<p>
|
||||
<img src='./img/styles.png' alt='styles' />
|
||||
<a href='https://www.telerik.com/kendo-react-ui/components/styling/?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-trial-styling'>KendoReact Themes Overview</a>
|
||||
</p>
|
||||
<p>
|
||||
<img src='./img/blogs.png' alt='blogs' />
|
||||
<a href='https://www.telerik.com/blogs/tag/react?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-blog-react-blogs'>Blog Posts</a>
|
||||
</p>
|
||||
<p>
|
||||
<img src='./img/docs.png' alt='tutorials' />
|
||||
<a href='https://www.telerik.com/kendo-react-ui/react-hooks-guide/?utm_medium=cpm&utm_source=stackblitz-app&utm_campaign=kendo-ui-react-blog-react-hooks-pp'>Tutorials</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home;
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Menu, MenuItem } from '@progress/kendo-react-layout';
|
||||
|
||||
|
||||
const MenuNavContainer = (props) => {
|
||||
return (
|
||||
<Menu>
|
||||
<MenuItem text="Home" render={()=> <Link to={'/'}>Home</Link>}/>
|
||||
<MenuItem text="Grid" render={()=> <Link to={'/grid'}>Grid</Link>}/>
|
||||
<MenuItem text="Chart" render={()=> <Link to={'/chart'}>Chart</Link>}/>
|
||||
<MenuItem text="Forms" render={()=> <Link to={'/forms'}>Forms</Link>}/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
export default MenuNavContainer;
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
import React from 'react';
|
||||
import { GridCell } from '@progress/kendo-react-grid';
|
||||
|
||||
export function MyCommandCell({ edit, remove, add, update, discard, cancel, editField }) {
|
||||
return class extends GridCell {
|
||||
render() {
|
||||
const { dataItem } = this.props;
|
||||
const inEdit = dataItem[editField];
|
||||
const isNewItem = dataItem.ProductID === undefined;
|
||||
|
||||
return inEdit ? (
|
||||
<td className="k-command-cell">
|
||||
<button
|
||||
className="k-button k-grid-save-command"
|
||||
onClick={() => isNewItem ? add(dataItem) : update(dataItem)}
|
||||
>
|
||||
{isNewItem ? 'Add' : 'Update'}
|
||||
</button>
|
||||
<button
|
||||
className="k-button k-grid-cancel-command"
|
||||
onClick={() => isNewItem ? discard(dataItem) : cancel(dataItem)}
|
||||
>
|
||||
{isNewItem ? 'Discard' : 'Cancel'}
|
||||
</button>
|
||||
</td>
|
||||
) : (
|
||||
<td className="k-command-cell">
|
||||
<button
|
||||
className="k-primary k-button k-grid-edit-command"
|
||||
onClick={() => edit(dataItem)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="k-button k-grid-remove-command"
|
||||
onClick={() =>
|
||||
remove(dataItem)
|
||||
}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
import React, { useRef } from 'react';
|
||||
import { DropDownList } from '@progress/kendo-react-dropdowns';
|
||||
import { Tooltip } from '@progress/kendo-react-tooltip';
|
||||
|
||||
const valueRender = (element, value) => {
|
||||
if (!value) {
|
||||
return element;
|
||||
}
|
||||
|
||||
const children = [
|
||||
<span
|
||||
key={1}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '18px',
|
||||
height: '18px',
|
||||
color: 'red'
|
||||
}}
|
||||
>
|
||||
<img src="./img/palete.png" alt="palete" />
|
||||
</span>,
|
||||
<span key={2}> {element.props.children}</span>
|
||||
];
|
||||
|
||||
return React.cloneElement(element, { ...element.props }, children);
|
||||
}
|
||||
|
||||
const ThemeChooser = (props) => {
|
||||
let tooltip = useRef(null);
|
||||
const themes = [
|
||||
{ id: 1, text: 'default', link: "https://unpkg.com/@progress/kendo-theme-default@latest/dist/all.css" },
|
||||
{ id: 2, text: 'material', link: "https://unpkg.com/@progress/kendo-theme-material@latest/dist/all.css" },
|
||||
{ id: 3, text: 'bootstrap', link: "https://unpkg.com/@progress/kendo-theme-bootstrap@latest/dist/all.css" }
|
||||
]
|
||||
return (
|
||||
<div className='container-fluid'>
|
||||
<Tooltip ref={tooltip} position="left" anchorElement='target'/>
|
||||
<div className="d-flex my-2" >
|
||||
<div className="ml-auto d-flex ">
|
||||
<div className='d-flex mr-1 mr-lg-3 change-theme'
|
||||
onMouseOver={event => {tooltip && tooltip.current.handleMouseOver(event)}}
|
||||
onMouseOut={event => tooltip && tooltip.current.handleMouseOut(event)}>
|
||||
<span className="d-block my-auto" title='Тhis will re-render the KendoReact components with the chosen theme.'>Change theme</span>
|
||||
</div>
|
||||
<DropDownList defaultValue={themes.find(x => x.link === props.theme)}
|
||||
textField={'text'} data={themes}
|
||||
onChange={(e) => props.changeTheme(e.target.value.link)}
|
||||
valueRender={valueRender}
|
||||
className="d-block my-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ThemeChooser;
|
|
@ -0,0 +1,12 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.scss';
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
|
@ -0,0 +1,17 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: "Roboto",-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;
|
||||
}
|
||||
body, html, #root {
|
||||
height: 100%;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<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,135 @@
|
|||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl, config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl, config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
@import "~bootstrap/scss/functions";
|
||||
@import "~bootstrap/scss/variables";
|
||||
@import "~bootstrap/scss/mixins";
|
||||
|
||||
@import "~bootstrap/scss/reboot";
|
||||
@import "~bootstrap/scss/utilities";
|
||||
@import "~bootstrap/scss/card";
|
||||
|
||||
|
||||
$grid-columns: 12;
|
||||
$grid-gutter-width: 48px;
|
||||
$grid-column-width: 48px;
|
||||
|
||||
$grid-breakpoints: (
|
||||
xs: 0,
|
||||
sm: 576px,
|
||||
md: 768px,
|
||||
lg: 992px,
|
||||
xl: 1200px
|
||||
);
|
||||
|
||||
$container-max-widths: (
|
||||
sm: 540px,
|
||||
md: 720px,
|
||||
lg: 960px,
|
||||
xl: 1140px
|
||||
);
|
||||
|
||||
@import "~bootstrap/scss/grid";
|
Загрузка…
Ссылка в новой задаче