Preview: http://dashboards.webkom.co/react/airframe
This commit is contained in:
Tomasz Owczarczyk
2019-08-15 00:54:44 +02:00
parent f975443095
commit 37092d1d6c
626 changed files with 56691 additions and 0 deletions

View File

@@ -0,0 +1,439 @@
import React from 'react';
import { chain, reduce } from 'lodash';
import fetch from 'node-fetch';
import {
Container,
Card,
CardFooter,
CardHeader,
Input,
InputGroup,
} from './../../../components';
import {
AgGridReact,
AgGridColumn,
} from './../../../components/agGrid';
import {
HeaderMain,
} from './../../components/HeaderMain';
import colors from './../../../colors';
/*
CONSTS
*/
const DATA_URL = "https://api.myjson.com/bins/18oni9";
const COUNTRY_CODES = {
Ireland: "ie",
Spain: "es",
"United Kingdom": "gb",
France: "fr",
Germany: "de",
Sweden: "se",
Italy: "it",
Greece: "gr",
Iceland: "is",
Portugal: "pt",
Malta: "mt",
Norway: "no",
Brazil: "br",
Argentina: "ar",
Colombia: "co",
Peru: "pe",
Venezuela: "ve",
Uruguay: "uy"
};
const IT_SKILLS = ["android", "css", "html5", "mac", "windows"];
const IT_SKILLS_NAMES = ["Android", "CSS", "HTML 5", "Mac", "Windows"];
const PROFICIENCY_NONE = "none";
const PROFICIENCY_ABOVE40 = "above40";
const PROFICIENCY_ABOVE60 = "above60";
const PROFICIENCY_ABOVE80 = "above80";
const PROFICIENCY_NAMES = ["No Filter", "Above 40%", "Above 60%", "Above 80%"];
const PROFICIENCY_VALUES = [
PROFICIENCY_NONE,
PROFICIENCY_ABOVE40,
PROFICIENCY_ABOVE60,
PROFICIENCY_ABOVE80
];
/*
Custom Renderers
*/
const nameRenderer = ({ data }) => `
<span class="text-inverse">
${ data.name }
</span>
`;
const skillsCellRenderer = ({ data }) =>
chain(IT_SKILLS)
.map((skill) => data && data.skills[skill] ?
`<img src="//www.ag-grid.com/images/skills/${skill}.png" width="16px" title="${ skill }" />` : ''
)
.compact()
.join(' ')
.value();
const countryCellRenderer = ({ value }) => `
<img width="15" height="10" style="margin-bottom: 2px" src="https://flags.fmcdn.net/data/flags/mini/${COUNTRY_CODES[value]}.png" /> ${ value }
`;
const percentCellRenderer = ({ value }) => {
const eDivPercentBar = document.createElement('div');
eDivPercentBar.className = 'div-percent-bar';
eDivPercentBar.style.width = `${value}%`;
if (value < 20) {
eDivPercentBar.style.backgroundColor = colors['danger'];
} else if (value < 60) {
eDivPercentBar.style.backgroundColor = colors['warning'];
} else {
eDivPercentBar.style.backgroundColor = colors['success'];
}
const eValue = document.createElement('div');
eValue.className = 'div-percent-value';
eValue.innerHTML = `${value}%`;
const eOuterDiv = document.createElement('div');
eOuterDiv.className = 'div-outer-div';
eOuterDiv.appendChild(eDivPercentBar);
eOuterDiv.appendChild(eValue);
return eOuterDiv;
}
/*
Custom Filters
*/
class SkillFilter {
init({ filterChangedCallback }) {
this.filterChangedCallback = filterChangedCallback;
// Initial State
this.model = {
android: false,
css: false,
html5: false,
mac: false,
windows: false
}
}
getModel() { }
setModel() { }
getGui() {
const eGui = document.createElement("div");
const eInstructions = document.createElement("div");
eInstructions.className = "h6 dropdown-header";
eInstructions.innerText = "Custom Skills Filter";
eGui.appendChild(eInstructions);
const createCheckMarkElement = () => {
var eCheckMark = document.createElement('i');
eCheckMark.className = "fa fa-check fa-fw ml-auto text-success";
return eCheckMark;
}
IT_SKILLS.forEach((skill, index) => {
const skillName = IT_SKILLS_NAMES[index];
const eFilter = document.createElement("a");
eFilter.className = "dropdown-item d-flex align-items-center"
//eFilter.classList.toggle("active", this.model[skill]);
eFilter.href="javascript:;";
const eImg = document.createElement("img");
eImg.src = '//www.ag-grid.com/images/skills/' + skill + '.png';
eImg.height = 20;
eImg.className = "mr-2";
const eName = document.createElement('span');
eName.innerText = skillName;
eFilter.appendChild(eImg);
eFilter.appendChild(eName);
if (this.model[skill]) {
eFilter.appendChild(
createCheckMarkElement()
);
}
eGui.appendChild(eFilter);
eFilter.addEventListener("click", (e) => {
const element = e.currentTarget;
this.model[skill] = !this.model[skill];
this.filterChangedCallback();
// Toggle check marks
if (this.model[skill]) {
element.appendChild(
createCheckMarkElement()
);
} else {
const eCheckMark = element.querySelector('i');
if (eCheckMark) { eCheckMark.remove() }
}
return false;
});
});
return eGui;
}
doesFilterPass({ data }) {
const rowSkills = data.skills;
const { model } = this;
const passed = reduce(
IT_SKILLS,
(acc, skill) => acc || (rowSkills[skill] && model[skill]),
false
);
return passed;
}
isFilterActive() {
return (
this.model.android ||
this.model.css ||
this.model.html5 ||
this.model.mac ||
this.model.windows
);
}
}
class ProficiencyFilter {
init({ filterChangedCallback, valueGetter }) {
this.filterChangedCallback = filterChangedCallback;
this.valueGetter = valueGetter;
this.selected = PROFICIENCY_NONE;
}
getModel() { }
setModel() { }
getGui() {
const eGui = document.createElement("div");
const eInstructions = document.createElement("div");
eInstructions.className = "h6 dropdown-header";
eInstructions.innerText = "Custom Proficiency Filter";
eGui.appendChild(eInstructions);
PROFICIENCY_NAMES.forEach((name, index) => {
const eFilter = document.createElement("a");
eFilter.className = "dropdown-item"
eFilter.classList.toggle("active", PROFICIENCY_VALUES[index] === this.selected);
eFilter.href="javascript:;";
eFilter.innerText = name;
eGui.appendChild(eFilter);
eFilter.addEventListener("click", (e) => {
const element = e.currentTarget;
element.parentElement.childNodes.forEach(function(node) {
node.classList.toggle('active', false);
});
element.classList.toggle("active");
this.selected = PROFICIENCY_VALUES[index];
this.filterChangedCallback();
return false;
});
});
return eGui;
}
doesFilterPass(params) {
const value = this.valueGetter(params);
const valueAsNumber = parseFloat(value);
switch (this.selected) {
case PROFICIENCY_ABOVE40:
return valueAsNumber >= 40;
case PROFICIENCY_ABOVE60:
return valueAsNumber >= 60;
case PROFICIENCY_ABOVE80:
return valueAsNumber >= 80;
default:
return true;
}
}
isFilterActive() {
return this.selected !== PROFICIENCY_NONE;
}
}
export default class AgGridExample extends React.Component {
constructor(props) {
super(props);
this.state = {
rowData: [],
visibleCount: 0,
quickFilterValue: ''
};
this.gridApi = null;
this.onGridReady = this.onGridReady.bind(this);
this.onModelUpdated = this.onModelUpdated.bind(this);
this.onQuickFilterChange = this.onQuickFilterChange.bind(this);
}
componentDidMount() {
fetch(DATA_URL)
.then(res => res.json())
.then(fetchedData => {
this.setState({ rowData: fetchedData });
});
}
componentDidUpdate(prevProps, prevState) {
if (this.gridApi) {
if (this.state.quickFilterValue !== prevState.quickFilterValue) {
this.gridApi.setQuickFilter(this.state.quickFilterValue);
}
}
}
onModelUpdated() {
if (this.gridApi) {
const model = this.gridApi.getModel();
const visibleCount = model.getRowCount();
this.setState({ visibleCount });
}
}
onGridReady({ api }) {
this.gridApi = api;
}
onQuickFilterChange(e) {
this.setState({ quickFilterValue: e.target.value });
}
render() {
const { rowData, visibleCount, quickFilterValue } = this.state;
return (
<Container>
<HeaderMain
title="AgGrid"
className="mb-5 mt-4"
/>
<p className="pb-3">
<strong>Over 2,500 Companies use ag-Grid.</strong> The &quot;ag&quot; part of ag-Grid stands for &quot;agnostic&quot;. The internal ag-Grid engine is implemented in TypeScript with zero dependencies. ag-Grid supports Angular through a wrapper component.
The wrapper lets you use ag-Grid in your application like any other Angular component you pass configuration through property bindings and handle events through event bindings.
You can even use Angular components to customize the grid UI and cell contents / behavior.
</p>
<Card>
<CardHeader tag="h6" className="d-flex justify-content-between align-items-center bg-white bb-0">
<span>AgGrid Example</span>
<div className="d-flex align-items-center">
<span className="mr-3 text-nowrap small">
{ visibleCount } / { rowData.length }
</span>
<InputGroup size="sm">
<Input
type="text"
placeholder="Type text to filter..."
value={ quickFilterValue }
onChange={ this.onQuickFilterChange }
/>
</InputGroup>
</div>
</CardHeader>
<div className="ag-theme-bootstrap" style={{ height: '600px' }}>
<AgGridReact
rowData={ rowData }
rowSelection="multiple"
onGridReady={ this.onGridReady }
onModelUpdated={ this.onModelUpdated }
defaultColDef={{
sortable: true,
resizable: true,
filter: true,
}}
>
<AgGridColumn
headerName=""
width={ 70 }
checkboxSelection
suppressMenu
/>
<AgGridColumn headerName="Employee">
<AgGridColumn
headerName="Name"
field="name"
width={ 150 }
cellRenderer={ nameRenderer }
/>
<AgGridColumn
headerName="Country"
field="country"
width={ 150 }
cellRenderer={ countryCellRenderer }
filterParams={{
cellRenderer: countryCellRenderer,
cellHeight: 20
}}
/>
</AgGridColumn>
<AgGridColumn headerName="IT Skills">
<AgGridColumn
headerName="Skills"
width={ 125 }
sortable={ false }
cellRenderer={ skillsCellRenderer }
filter={ SkillFilter }
/>
<AgGridColumn
headerName="Proficiency"
field="proficiency"
width={ 150 }
cellRenderer={ percentCellRenderer }
filter={ ProficiencyFilter }
/>
</AgGridColumn>
<AgGridColumn headerName="Contact">
<AgGridColumn
headerName="Mobile"
field="mobile"
width={ 180 }
filter="agTextColumnFilter"
/>
<AgGridColumn
headerName="Land-line"
field="landline"
width={ 180 }
filter="agTextColumnFilter"
/>
<AgGridColumn
headerName="Address"
field="address"
width={ 180 }
filter="agTextColumnFilter"
/>
</AgGridColumn>
</AgGridReact>
</div>
<CardFooter className="bg-white text-center">
More examples of this table can be found <a href="https://www.ag-grid.com" target="_blank" rel="noopener noreferrer">Here</a>
</CardFooter>
</Card>
</Container>
);
}
}

View File

@@ -0,0 +1,3 @@
import AgGrid from './AgGrid';
export default AgGrid;

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { Container, Row, Col } from './../../../components';
import {
AdvancedTableA,
AdvancedTableB,
BasicTable,
BorderedTable,
CellEdit,
ClearSearch,
LargeTable,
SortTable
} from './components';
import { HeaderMain } from "../../components/HeaderMain";
export const ExtendedTable = () => (
<Container>
<HeaderMain
title="Extended Tables"
className="mb-5 mt-4"
/>
<Row className="mb-5">
<Col>
<AdvancedTableA />
</Col>
</Row>
<Row className="mb-5">
<Col>
<AdvancedTableB />
</Col>
</Row>
<Row className="mt-5">
<Col>
<BasicTable />
</Col>
</Row>
<Row className="mt-5">
<Col>
<LargeTable />
</Col>
</Row>
<Row className="mt-5">
<Col>
<BorderedTable />
</Col>
</Row>
<Row className="mt-5">
<Col>
<SortTable />
</Col>
</Row>
<Row className="mt-5">
<Col>
<ClearSearch />
</Col>
</Row>
<Row className="mt-5">
<Col>
<CellEdit />
</Col>
</Row>
</Container>
);

View File

@@ -0,0 +1,309 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
import filterFactory, { Comparator, dateFilter } from 'react-bootstrap-table2-filter'
import ToolkitProvider from 'react-bootstrap-table2-toolkit';
import _ from 'lodash';
import faker from 'faker/locale/en_US';
import moment from 'moment';
import {
Badge,
Button,
CustomInput,
StarRating,
ButtonGroup
} from './../../../../components';
import { CustomExportCSV } from './CustomExportButton';
import { CustomSearch } from './CustomSearch';
import { CustomPaginationPanel } from './CustomPaginationPanel';
import { CustomSizePerPageButton } from './CustomSizePerPageButton';
import { CustomPaginationTotal } from './CustomPaginationTotal';
import { randomArray } from './../../../../utilities';
import {
buildCustomTextFilter,
buildCustomSelectFilter,
buildCustomNumberFilter
} from './../filters';
const INITIAL_PRODUCTS_COUNT = 500;
const ProductQuality = {
Good: 'product-quality__good',
Bad: 'product-quality__bad',
Unknown: 'product-quality__unknown'
};
const sortCaret = (order) => {
if (!order)
return <i className="fa fa-fw fa-sort text-muted"></i>;
if (order)
return <i className={`fa fa-fw text-muted fa-sort-${order}`}></i>
}
const generateRow = (index) => ({
id: index,
name: faker.commerce.productName(),
quality: randomArray([
ProductQuality.Bad,
ProductQuality.Good,
ProductQuality.Unknown
]),
price: (1000 + Math.random() * 1000).toFixed(2),
satisfaction: Math.round(Math.random() * 6),
inStockDate: faker.date.past()
});
export class AdvancedTableA extends React.Component {
constructor() {
super();
this.state = {
products: _.times(INITIAL_PRODUCTS_COUNT, generateRow),
selected: []
};
this.headerCheckboxRef = React.createRef();
}
handleSelect(row, isSelected) {
if (isSelected) {
this.setState({ selected: [...this.state.selected, row.id] })
} else {
this.setState({
selected: this.state.selected.filter(itemId => itemId !== row.id)
})
}
}
handleSelectAll(isSelected, rows) {
if (isSelected) {
this.setState({ selected: _.map(rows, 'id') })
} else {
this.setState({ selected: [] });
}
}
handleAddRow() {
const currentSize = this.state.products.length;
this.setState({
products: [
generateRow(currentSize + 1),
...this.state.products,
]
});
}
handleDeleteRow() {
this.setState({
products: _.filter(this.state.products, product =>
!_.includes(this.state.selected, product.id))
})
}
handleResetFilters() {
this.nameFilter('');
this.qualityFilter('');
this.priceFilter('');
this.satisfactionFilter('');
}
createColumnDefinitions() {
return [{
dataField: 'id',
text: 'Product ID',
headerFormatter: column => (
<React.Fragment>
<span className="text-nowrap">{ column.text }</span>
<a
href="javascript:;"
className="d-block small text-decoration-none text-nowrap"
onClick={ this.handleResetFilters.bind(this) }
>
Reset Filters <i className="fa fa-times fa-fw text-danger"></i>
</a>
</React.Fragment>
)
}, {
dataField: 'name',
text: 'Product Name',
sort: true,
sortCaret,
formatter: (cell) => (
<span className="text-inverse">
{ cell }
</span>
),
...buildCustomTextFilter({
placeholder: 'Enter product name...',
getFilter: filter => { this.nameFilter = filter; }
})
}, {
dataField: 'quality',
text: 'Product Quality',
formatter: (cell) => {
let pqProps;
switch (cell) {
case ProductQuality.Good:
pqProps = {
color: 'success',
text: 'Good'
}
break;
case ProductQuality.Bad:
pqProps = {
color: 'danger',
text: 'Bad'
}
break;
case ProductQuality.Unknown:
default:
pqProps = {
color: 'secondary',
text: 'Unknown'
}
}
return (
<Badge color={pqProps.color}>
{ pqProps.text }
</Badge>
)
},
sort: true,
sortCaret,
...buildCustomSelectFilter({
placeholder: 'Select Quality',
options: [
{ value: ProductQuality.Good, label: 'Good' },
{ value: ProductQuality.Bad, label: 'Bad' },
{ value: ProductQuality.Unknown, label: 'Unknown' }
],
getFilter: filter => { this.qualityFilter = filter; }
})
}, {
dataField: 'price',
text: 'Product Price',
sort: true,
sortCaret,
...buildCustomNumberFilter({
comparators: [Comparator.EQ, Comparator.GT, Comparator.LT],
getFilter: filter => { this.priceFilter = filter; }
})
}, {
dataField: 'satisfaction',
text: 'Buyer Satisfaction',
sort: true,
sortCaret,
formatter: (cell) =>
<StarRating at={ cell } max={ 6 } />,
...buildCustomSelectFilter({
placeholder: 'Select Satisfaction',
options: _.times(6, (i) => ({ value: i + 1, label: i + 1 })),
getFilter: filter => { this.satisfactionFilter = filter; }
})
}, {
dataField: 'inStockDate',
text: 'In Stock From',
formatter: (cell) =>
moment(cell).format('DD/MM/YYYY'),
filter: dateFilter({
className: 'd-flex align-items-center',
comparatorClassName: 'd-none',
dateClassName: 'form-control form-control-sm',
comparator: Comparator.GT,
getFilter: filter => { this.stockDateFilter = filter; }
}),
sort: true,
sortCaret
}];
}
render() {
const columnDefs = this.createColumnDefinitions();
const paginationDef = paginationFactory({
paginationSize: 5,
showTotal: true,
pageListRenderer: (props) => (
<CustomPaginationPanel { ...props } size="sm" className="ml-md-auto mt-2 mt-md-0" />
),
sizePerPageRenderer: (props) => (
<CustomSizePerPageButton { ...props } />
),
paginationTotalRenderer: (from, to, size) => (
<CustomPaginationTotal { ...{ from, to, size } } />
)
});
const selectRowConfig = {
mode: 'checkbox',
selected: this.state.selected,
onSelect: this.handleSelect.bind(this),
onSelectAll: this.handleSelectAll.bind(this),
selectionRenderer: ({ mode, checked, disabled }) => (
<CustomInput type={ mode } checked={ checked } disabled={ disabled } />
),
selectionHeaderRenderer: ({ mode, checked, indeterminate }) => (
<CustomInput type={ mode } checked={ checked } innerRef={el => el && (el.indeterminate = indeterminate)} />
)
};
return (
<ToolkitProvider
keyField="id"
data={ this.state.products }
columns={ columnDefs }
search
exportCSV
>
{
props => (
<React.Fragment>
<div className="d-flex justify-content-end align-items-center mb-2">
<h6 className="my-0">
AdvancedTable A
</h6>
<div className="d-flex ml-auto">
<CustomSearch
className="mr-2"
{ ...props.searchProps }
/>
<ButtonGroup>
<CustomExportCSV
{ ...props.csvProps }
>
Export
</CustomExportCSV>
<Button
size="sm"
outline
onClick={ this.handleDeleteRow.bind(this) }
>
Delete
</Button>
<Button
size="sm"
outline
onClick={ this.handleAddRow.bind(this) }
>
<i className="fa fa-fw fa-plus"></i>
</Button>
</ButtonGroup>
</div>
</div>
<BootstrapTable
classes="table-responsive"
pagination={ paginationDef }
filter={ filterFactory() }
selectRow={ selectRowConfig }
bordered={ false }
responsive
{ ...props.baseProps }
/>
</React.Fragment>
)
}
</ToolkitProvider>
);
}
}

View File

@@ -0,0 +1,234 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider from 'react-bootstrap-table2-toolkit';
import moment from 'moment';
import _ from 'lodash';
import faker from 'faker/locale/en_US';
import {
Avatar,
Badge,
Button,
ButtonGroup,
Row,
Col
} from './../../../../components';
import { CustomExportCSV } from './CustomExportButton';
import { CustomSearch } from './CustomSearch';
import { randomArray, randomAvatar } from './../../../../utilities';
const generateRow = (id) => ({
id,
photo: randomAvatar(),
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
role: faker.name.jobType(),
status: randomArray([
'Active',
'Suspended',
'Waiting',
'Unknown'
]),
region: randomArray(['North', 'South', 'East', 'West']),
earnings: 500 + Math.random() * 1000,
earningsCurrencyIcon: randomArray([
<i className="fa fa-fw fa-euro text-muted" key="cur_eur"></i>,
<i className="fa fa-fw fa-dollar text-muted" key="cur_usd"></i>
]),
lastLoginDate: faker.date.recent(),
ipAddress: faker.internet.ip(),
browser: 'Safari 9.1.1(11601.6.17)',
os: 'OS X El Capitan',
planSelected: randomArray(['Basic', 'Premium', 'Enterprise']),
planEnd: faker.date.future()
});
const sortCaret = (order) => {
if (!order)
return <i className="fa fa-fw fa-sort text-muted"></i>;
if (order)
return <i className={`fa fa-fw text-muted fa-sort-${order}`}></i>
};
export class AdvancedTableB extends React.Component {
constructor(props) {
super(props);
this.state = {
users: _.times(10, generateRow)
}
}
handleAddRow() {
const usersLength = this.state.users.length;
this.setState({
users: [
generateRow(usersLength + 1),
...this.state.users
]
})
}
createColumnDefinitions() {
return [
{
dataField: 'photo',
text: 'Photo',
formatter: (cell) => (
<Avatar.Image src={ cell } />
)
}, {
dataField: 'firstName',
text: 'First Name',
sort: true,
sortCaret
}, {
dataField: 'lastName',
text: 'Last Name',
sort: true,
sortCaret
}, {
dataField: 'role',
text: 'Role',
sort: true,
sortCaret
}, {
dataField: 'status',
text: 'Status',
sort: true,
sortCaret,
formatter: (cell) => {
const color = (status) => {
const map = {
'Active': 'success',
'Suspended': 'danger',
'Waiting': 'info',
'Unknown': 'secondary'
};
return map[status];
}
return (
<Badge color={ color(cell) }>
{ cell }
</Badge>
);
}
}, {
dataField: 'region',
text: 'Region',
sort: true,
sortCaret
}, {
dataField: 'earnings',
text: 'Earnings',
sort: true,
sortCaret,
formatter: (cell, row) => (
<span>
{ row.earningsCurrencyIcon }
{ _.isNumber(cell) && cell.toFixed(2) }
</span>
)
}
];
}
render() {
const columnDefs = this.createColumnDefinitions();
const expandRow = {
renderer: row => (
<Row>
<Col md={ 6 }>
<dl className="row">
<dt className="col-sm-6 text-right">Last Login</dt>
<dd className="col-sm-6">{ moment(row.lastLoginDate).format('DD-MMM-YYYY') }</dd>
<dt className="col-sm-6 text-right">IP Address</dt>
<dd className="col-sm-6">{ row.ipAddress }</dd>
<dt className="col-sm-6 text-right">Browser</dt>
<dd className="col-sm-6">{ row.browser }</dd>
</dl>
</Col>
<Col md={ 6 }>
<dl className="row">
<dt className="col-sm-6 text-right">Operating System</dt>
<dd className="col-sm-6">{ row.os }</dd>
<dt className="col-sm-6 text-right">Selected Plan</dt>
<dd className="col-sm-6">{ row.planSelected }</dd>
<dt className="col-sm-6 text-right">Plan Expiriation</dt>
<dd className="col-sm-6">{ moment(row.planEnd).format('DD-MMM-YYYY') }</dd>
</dl>
</Col>
</Row>
),
showExpandColumn: true,
expandHeaderColumnRenderer: ({ isAnyExpands }) => isAnyExpands ? (
<i className="fa fa-angle-down fa-fw fa-lg text-muted"></i>
) : (
<i className="fa fa-angle-right fa-fw fa-lg text-muted"></i>
),
expandColumnRenderer: ({ expanded }) =>
expanded ? (
<i className="fa fa-angle-down fa-fw fa-lg text-muted"></i>
) : (
<i className="fa fa-angle-right fa-fw fa-lg text-muted"></i>
)
}
return (
<ToolkitProvider
keyField="id"
data={ this.state.users }
columns={ columnDefs }
search
exportCSV
>
{
props => (
<React.Fragment>
<div className="d-flex justify-content-end align-items-center mb-2">
<h6 className="my-0">
AdvancedTable B
</h6>
<div className="d-flex ml-auto">
<CustomSearch
className="mr-2"
{ ...props.searchProps }
/>
<ButtonGroup>
<CustomExportCSV
{ ...props.csvProps }
>
Export
</CustomExportCSV>
<Button
size="sm"
outline
onClick={ this.handleAddRow.bind(this) }
>
Add <i className="fa fa-fw fa-plus"></i>
</Button>
</ButtonGroup>
</div>
</div>
<BootstrapTable
classes="table-responsive-lg"
bordered={ false }
expandRow={ expandRow }
responsive
hover
{ ...props.baseProps }
/>
</React.Fragment>
)
}
</ToolkitProvider>
);
}
}

View File

@@ -0,0 +1,38 @@
import React from 'react';
import _ from 'lodash';
import BootstrapTable from 'react-bootstrap-table-next';
import faker from 'faker/locale/en_US';
const columns = [
{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}
];
const data = _.times(5, (index) => ({
id: index,
name: faker.commerce.productName(),
price: Math.round(2000 + Math.random() * 500)
}));
export const BasicTable = () => (
<React.Fragment>
<h6 className="mt-0">
Basic Table
</h6>
<BootstrapTable
classes="table-responsive-sm"
keyField='id'
data={ data }
columns={ columns }
bordered={ false }
/>
</React.Fragment>
);

View File

@@ -0,0 +1,38 @@
import React from 'react';
import _ from 'lodash';
import BootstrapTable from 'react-bootstrap-table-next';
import faker from 'faker/locale/en_US';
const columns = [
{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}
];
const data = _.times(5, (index) => ({
id: index,
name: faker.commerce.productName(),
price: Math.round(2000 + Math.random() * 500)
}));
export const BorderedTable = () => (
<React.Fragment>
<h6 className="mt-0">
Bordered Table
</h6>
<BootstrapTable
classes='table-responsive-sm'
keyField='id'
data={ data }
columns={ columns }
bordered
/>
</React.Fragment>
);

View File

@@ -0,0 +1,89 @@
import React from 'react';
import _ from 'lodash';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import faker from 'faker/locale/en_US';
import { randomArray } from './../../../../utilities';
const regions = [
{ value: 'Europe', label: 'Europe' },
{ value: 'North America', label: 'North America' },
{ value: 'Asia', label: 'Asia' },
{ value: 'Australia', label: 'Australia' },
];
const columns = [
{
dataField: 'id',
text: 'Product ID',
headerClasses: 'text-nowrap'
}, {
dataField: 'available',
text: 'Available',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
},
formatter: function AvailableFormatter(cell) {
return cell === 'Y' ?
<i className="fa fa-fw fa-check"></i> :
<i className="fa fa-fw fa-times"></i>;
},
headerClasses: 'text-nowrap'
}, {
dataField: 'name',
text: 'Product Name',
editor: {
type: Type.TEXT
},
headerClasses: 'text-nowrap'
}, {
dataField: 'description',
text: 'Product Description',
editor: {
type: Type.TEXTAREA
},
style: {
width: '40%'
},
headerClasses: 'text-nowrap'
}, {
dataField: 'price',
text: 'Product Price',
headerClasses: 'text-nowrap'
}, {
dataField: 'region',
text: 'Region',
headerClasses: 'text-nowrap',
editor: {
type: Type.SELECT,
options: regions
}
}
];
const data = _.times(5, (index) => ({
id: index,
available: !Math.round(Math.random()) ? 'Y' : 'N',
name: faker.commerce.productName(),
description: faker.lorem.paragraph(),
price: Math.round(2000 + Math.random() * 500),
region: randomArray(_.map(regions, 'value'))
}))
export const CellEdit = () => (
<React.Fragment>
<h6 className="mt-0">
Cell Edit
</h6>
<BootstrapTable
classes="table-responsive"
keyField='id'
data={ data }
columns={ columns }
bordered={ false }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
</React.Fragment>
);

View File

@@ -0,0 +1,59 @@
import React from 'react';
import _ from 'lodash';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider from 'react-bootstrap-table2-toolkit';
import faker from 'faker/locale/en_US';
import { CustomSearch } from './CustomSearch';
const columns = [
{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}
];
const data = _.times(5, (index) => ({
id: index,
name: faker.commerce.productName(),
price: Math.round(2000 + Math.random() * 500)
}));
export const ClearSearch = () => (
<ToolkitProvider
keyField="id"
data={ data }
columns={ columns }
search
>
{
props => (
<React.Fragment>
<div className="d-flex align-items-center justify-content-between">
<h6 className="mt-0">
Table Search with Clear
</h6>
<div>
<CustomSearch
className="mb-2"
{ ...props.searchProps }
/>
</div>
</div>
<BootstrapTable
classes="table-responsive-sm"
bordered={ false }
{ ...props.baseProps }
/>
</React.Fragment>
)
}
</ToolkitProvider>
);

View File

@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Button
} from './../../../../components';
export const CustomExportCSV = ({children, onExport, ...props}) => {
return (
<Button
{ ...props }
onClick={() => { onExport() }}
>
{ children }
</Button>
);
}
CustomExportCSV.propTypes = {
size: PropTypes.string,
outline: PropTypes.bool,
onExport: PropTypes.func,
children: PropTypes.node
}
CustomExportCSV.defaultProps = {
size: 'sm',
outline: true
}

View File

@@ -0,0 +1,31 @@
import React from 'react';
import PropTypes from 'prop-types';
import { map, isInteger } from 'lodash';
import { Pagination, PaginationItem, PaginationLink, Col } from './../../../../components';
const mapToFa = {
'<': <i className="fa fa-angle-left" />,
'<<': <i className="fa fa-angle-double-left" />,
'>': <i className="fa fa-angle-right" />,
'>>': <i className="fa fa-angle-double-right" />
}
export const CustomPaginationPanel = ({ onPageChange, pages, ...otherProps }) => (
<Col md={ 6 } className="d-flex">
<Pagination aria-label="Page navigation example" { ...otherProps } listClassName="my-0">
{
map(pages, page => (
<PaginationItem active={ page.active } disabled={ page.disabled }>
<PaginationLink onClick={() => onPageChange(page.page)}>
{ isInteger(page.page) ? page.page : mapToFa[page.page] }
</PaginationLink>
</PaginationItem>
))
}
</Pagination>
</Col>
);
CustomPaginationPanel.propTypes = {
pages: PropTypes.array,
onPageChange: PropTypes.func
};

View File

@@ -0,0 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
export const CustomPaginationTotal = ({ from, to, size }) => (
<span className="small ml-2">
Showing { from } to { to } of { size } Results
</span>
);
CustomPaginationTotal.propTypes = {
from: PropTypes.number,
to: PropTypes.number,
size: PropTypes.number,
};

View File

@@ -0,0 +1,57 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Input,
InputGroup,
Button,
InputGroupAddon
} from './../../../../components';
export class CustomSearch extends React.Component {
static propTypes = {
className: PropTypes.string,
onSearch: PropTypes.func
}
constructor(props) {
super(props);
this.state = {
value: ''
}
}
componentDidUpdate(prevProps, prevState) {
if (prevState.value !== this.state.value) {
this.props.onSearch(this.state.value);
}
}
render() {
return (
<InputGroup className={ this.props.className } size="sm">
<InputGroupAddon addonType="prepend">
<i className="fa fa-search fa-fw"></i>
</InputGroupAddon>
<Input
onChange={(e) => { this.setState({ value: e.target.value }) }}
value={ this.state.value }
className="bg-white"
placeholder="Type to search..."
/>
{
this.state.value && (
<InputGroupAddon addonType="append">
<Button
outline
onClick={() => { this.setState({value: ''}) }}
>
<i className="fa fa-fw fa-times"></i>
</Button>
</InputGroupAddon>
)
}
</InputGroup>
)
}
}

View File

@@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import { map } from 'lodash';
import {
UncontrolledButtonDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem
} from './../../../../components';
export const CustomSizePerPageButton = ({
options,
currSizePerPage,
onSizePerPageChange,
...ddProps,
}) => (
<UncontrolledButtonDropdown { ...ddProps }>
<DropdownToggle size="sm" color="link" className="text-decoration-none">
{ currSizePerPage }<i className="fa fa-angle-down ml-2" />
</DropdownToggle>
<DropdownMenu>
<DropdownItem header>Page Size</DropdownItem>
{
map(options, option => (
<DropdownItem
onClick={() => onSizePerPageChange(option.page)}
active={option.page === currSizePerPage}
>
{ option.text }
</DropdownItem>
))
}
</DropdownMenu>
</UncontrolledButtonDropdown>
);
CustomSizePerPageButton.propTypes = {
options: PropTypes.object,
currSizePerPage: PropTypes.number,
onSizePerPageChange: PropTypes.func
}

View File

@@ -0,0 +1,71 @@
import React from 'react';
import _ from 'lodash';
import BootstrapTable from 'react-bootstrap-table-next';
import faker from 'faker/locale/en_US';
import classes from './LargeTable.scss';
import { Card, CardHeader } from './../../../../components';
const columns = [
{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}
];
const data = _.times(500, (index) => ({
id: index,
name: faker.commerce.productName(),
price: Math.round(2000 + Math.random() * 500)
}));
const expandRow = {
showExpandColumn: true,
renderer: function ExtendedRowRender(row) {
return (
<div>
<p>{ `This Expand row is belong to rowKey ${row.id}` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
);
},
expandHeaderColumnRenderer: ({ isAnyExpands }) => isAnyExpands ? (
<i className="fa fa-angle-down fa-fw fa-lg text-muted"></i>
) : (
<i className="fa fa-angle-right fa-fw fa-lg text-muted"></i>
),
expandColumnRenderer: ({ expanded }) =>
expanded ? (
<i className="fa fa-angle-down fa-fw fa-lg text-muted"></i>
) : (
<i className="fa fa-angle-right fa-fw fa-lg text-muted"></i>
)
};
export const LargeTable = () => (
<React.Fragment>
<Card>
<CardHeader>
Large Table
</CardHeader>
<div className={ classes['table-scroll-wrap'] }>
<BootstrapTable
classes="table-responsive-sm"
keyField='id'
data={ data }
columns={ columns }
bordered={ false }
selectRow={ { mode: 'checkbox', clickToSelect: true } }
expandRow={ expandRow }
/>
</div>
</Card>
</React.Fragment>
);

View File

@@ -0,0 +1,4 @@
.table-scroll-wrap {
max-height: 380px;
overflow-y: auto;
}

View File

@@ -0,0 +1,44 @@
import React from 'react';
import _ from 'lodash';
import BootstrapTable from 'react-bootstrap-table-next';
import faker from 'faker/locale/en_US';
const columns = [
{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
}, {
dataField: 'price',
text: 'Product Price',
}
];
const data = _.times(5, (index) => ({
id: index,
name: faker.commerce.productName(),
price: Math.round(2000 + Math.random() * 500)
}));
const selectRow = {
mode: 'checkbox',
clickToSelect: true
};
export const SelectAll = () => (
<React.Fragment>
<h6 className="mt-0">
Select All
</h6>
<BootstrapTable
classes="table-responsive"
keyField='id'
data={ data }
columns={ columns }
bordered={ false }
selectRow={ selectRow }
/>
</React.Fragment>
);

View File

@@ -0,0 +1,49 @@
import React from 'react';
import _ from 'lodash';
import BootstrapTable from 'react-bootstrap-table-next';
import faker from 'faker/locale/en_US';
const sortCaret = (order) => {
if (!order)
return <i className="fa fa-fw fa-sort text-muted"></i>;
if (order)
return <i className={`fa fa-fw text-muted fa-sort-${order}`}></i>
}
const columns = [
{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
sort: true,
sortCaret
}, {
dataField: 'price',
text: 'Product Price',
sort: true,
sortCaret
}
];
const data = _.times(10, (index) => ({
id: index,
name: faker.commerce.productName(),
price: Math.round(2000 + Math.random() * 500)
}));
export const SortTable = () => (
<React.Fragment>
<h6 className="mt-0">
Sort Table
</h6>
<BootstrapTable
classes="table-responsive-sm"
keyField='id'
data={ data }
columns={ columns }
bordered={ false }
/>
</React.Fragment>
);

View File

@@ -0,0 +1,11 @@
export * from './AdvancedTableA';
export * from './AdvancedTableB';
export * from './BasicTable';
export * from './BorderedTable';
export * from './CellEdit';
export * from './ClearSearch';
export * from './CustomExportButton';
export * from './CustomSearch';
export * from './LargeTable';
export * from './SelectAll';
export * from './SortTable';

View File

@@ -0,0 +1,3 @@
export * from './textFilter';
export * from './selectFilter';
export * from './numberFilter';

View File

@@ -0,0 +1,143 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import uid from 'uuid/v4';
import { customFilter, Comparator, FILTER_TYPES } from 'react-bootstrap-table2-filter';
import { CustomInput, Input } from './../../../../components';
const comparatorSign = (comp) => {
switch (comp) {
case Comparator.EQ:
return '=';
case Comparator.GT:
return '>';
case Comparator.LT:
return '<';
}
}
class NumberFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired,
placeholder: PropTypes.string,
getFilter: PropTypes.func,
comparators: PropTypes.array,
comparator: PropTypes.string,
onClick: PropTypes.func
}
static defaultProps = {
comparators: [Comparator.EQ],
comparator: Comparator.EQ
}
constructor(props) {
super(props);
this.state = {
value: '',
comparator: props.comparator
}
this.comparatorInputId = uid();
this.valueInputId = uid();
}
componentDidMount() {
if (_.isFunction(this.props.getFilter)) {
this.props.getFilter((value) => {
this.setState({ value });
});
}
}
componentDidUpdate(prevProps, prevState) {
if (
prevState.value !== this.state.value ||
prevState.comparator !== this.state.comparator
) {
this.props.onFilter({
number: this.state.value,
comparator: this.state.comparator
});
}
if (prevProps.comparator !== this.props.comparator) {
this.setState({ comparator: this.props.comparator });
}
}
handleClick(e) {
e.stopPropagation();
if (this.props.onClick) {
this.props.onClick(e);
}
}
render() {
const { placeholder, comparators } = this.props;
const { comparator } = this.state;
return (
<div className='d-flex'>
{
(!_.isEmpty(comparators) && comparators.length > 0) && (
<CustomInput
type="select"
bsSize="sm"
onChange={(e) => { this.setState({ comparator: e.target.value }) }}
onClick={this.handleClick}
value={ comparator }
className="d-block bg-white mr-1"
id={this.comparatorInputId}
>
<option value=""></option>
{
_.map(comparators, (comparator, index) => (
<option
value={ comparator }
key={ index }
>
{ comparatorSign(comparator) }
</option>
))
}
</CustomInput>
)
}
<Input
type="number"
className="bg-white"
bsSize="sm"
onChange={(e) => { this.setState({ value: e.target.value }) }}
onClick={this.handleClick}
value={ this.state.value }
placeholder={ placeholder }
id={this.valueInputId}
/>
</div>
)
}
}
export const buildCustomNumberFilter = ({ placeholder, getFilter, comparators, ...other } = {}) => ({
filter: customFilter({
type: FILTER_TYPES.NUMBER,
...other
}),
filterRenderer: function NumberFilterWrap(onFilter, column) {
return (
<NumberFilter
{...{
onFilter,
column,
placeholder,
getFilter,
comparators
}}
/>
)
}
});

View File

@@ -0,0 +1,90 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { customFilter } from 'react-bootstrap-table2-filter';
import uid from 'uuid/v4';
import { CustomInput } from './../../../../components';
class SelectFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired,
options: PropTypes.array.isRequired,
placeholder: PropTypes.string,
getFilter: PropTypes.func,
onClick: PropTypes.func
}
constructor() {
super();
this.state = {
value: ''
}
this.inputId = uid();
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
if (_.isFunction(this.props.getFilter)) {
this.props.getFilter((value) => {
this.setState({ value });
});
}
}
componentDidUpdate(prevProps, prevState) {
if (prevState.value !== this.state.value) {
this.props.onFilter(this.state.value);
}
}
handleClick(e) {
e.stopPropagation();
if (this.props.onClick) {
this.props.onClick(e);
}
}
render() {
const { placeholder, options } = this.props;
return (
<CustomInput
type="select"
bsSize="sm"
onChange={(e) => { this.setState({ value: e.target.value }) }}
onClick={ this.handleClick }
value={ this.state.value }
className="d-block bg-white"
id={this.inputId}
>
<option value="">{ placeholder }</option>
{
_.map(options, ({ value, label }, index) => (
<option value={value} key={ index }>{ label }</option>
))
}
</CustomInput>
)
}
}
export const buildCustomSelectFilter = ({ placeholder, options, getFilter, ...other } = {}) => ({
filter: customFilter(other),
filterRenderer: function TextFilterWrap(onFilter, column) {
return (
<SelectFilter
{...{
onFilter,
column,
placeholder,
options,
getFilter
}}
/>
)
}
});

View File

@@ -0,0 +1,89 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { customFilter } from 'react-bootstrap-table2-filter';
import {
Form,
Input
} from './../../../../components';
class TextFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired,
placeholder: PropTypes.string,
getFilter: PropTypes.func,
onClick: PropTypes.func
}
constructor() {
super();
this.state = {
value: ''
}
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
if (_.isFunction(this.props.getFilter)) {
this.props.getFilter((value) => {
this.setState({ value });
});
}
}
componentDidUpdate(prevProps, prevState) {
if (prevState.value !== this.state.value) {
this.props.onFilter(this.state.value);
}
}
handleClick(e) {
e.stopPropagation();
if (this.props.onClick) {
this.props.onClick(e);
}
}
render() {
const { onFilter, placeholder } = this.props;
return (
<Form
onSubmit={(e) => {
e.preventDefault();
onFilter(this.state.value)
}}
>
<Input
type="text"
bsSize="sm"
className="bg-white"
onChange={(e) => this.setState({value: e.target.value})}
onClick={ this.handleClick }
value={ this.state.value }
placeholder={ placeholder }
/>
</Form>
)
}
}
export const buildCustomTextFilter = ({ placeholder, getFilter, ...other } = {}) => ({
filter: customFilter(other),
filterRenderer: function TextFilterWrap(onFilter, column) {
return (
<TextFilter
{...{
onFilter,
column,
placeholder,
getFilter
}}
/>
)
}
});

View File

@@ -0,0 +1,3 @@
import { ExtendedTable } from './ExtendedTable';
export default ExtendedTable;

View File

@@ -0,0 +1,483 @@
import React from 'react';
import {
Container,
Row,
Col,
Card,
CardTitle,
CardBody,
Table,
} from './../../../components';
import { HeaderMain } from "../../components/HeaderMain";
import {
HeaderDemo
} from "../../components/HeaderDemo";
import {
TrTableDefault
} from "./components/TrTableDefault";
import {
TrTableResponsive
} from "./components/TrTableResponsive";
import {
TrTableStriped
} from "./components/TrTableStriped";
import {
TrTableHoverable
} from "./components/TrTableHoverable";
import {
TrTableSmall
} from "./components/TrTableSmall";
import {
TrTableBorderless
} from "./components/TrTableBorderless";
import {
TrTableBordered
} from "./components/TrTableBordered";
import {
TrTableHeads
} from "./components/TrTableHeads";
import {
TrTableContextual
} from "./components/TrTableContextual";
const Tables = () => (
<React.Fragment>
<Container>
<HeaderMain
title="Tables"
className="mb-5 mt-4"
/>
{ /* START Header 1 */}
<Row>
<Col lg={ 12 }>
<HeaderDemo
no={1}
title="Basic Tables"
subTitle={(
<React.Fragment>
All table styles are inherited in Bootstrap 4, meaning any nested tables will be styled in the same manner as the parent.
</React.Fragment>
)}
/>
</Col>
</Row>
{ /* END Header 1 */}
{ /* START Section 1 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Default
<span className="small ml-1 text-muted">
#1.01
</span>
</CardTitle>
<p className="mb-0">
Using the most basic table markup, heres how <code>.table-based</code> tables look in Bootstrap.
All table styles are inherited in Bootstrap 4, meaning any nested tables will be styled in the
same manner as the parent.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" responsive>
<thead>
<tr>
<th className="bt-0">Project</th>
<th className="bt-0">Deadline</th>
<th className="bt-0">Leader</th>
<th className="bt-0">Budget</th>
<th className="bt-0">Status</th>
<th className="text-right bt-0">
Actions
</th>
</tr>
</thead>
<tbody>
<TrTableDefault />
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 1 */}
{ /* START Section 2 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Responsive
<span className="small ml-1 text-muted">
#2.01
</span>
</CardTitle>
<p className="mb-0">
Responsive tables allow tables to be scrolled horizontally with ease.
Make any table responsive across all viewports by wrapping a <code>Table</code> with <code>responsive</code>.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" responsive>
<thead>
<tr>
<th className="bt-0">#</th>
<th className="bt-0">Browser & OS</th>
<th className="bt-0">IP</th>
<th className="bt-0">Location</th>
<th className="bt-0">Signed In</th>
<th className="text-right bt-0">
Action
</th>
</tr>
</thead>
<tbody>
<TrTableResponsive />
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 2 */}
{ /* START Section 3 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Striped
<span className="small ml-1 text-muted">
#3.01
</span>
</CardTitle>
<p className="mb-0">
Use <code>striped</code> to add zebra-striping to any table row within the <code>&lt;tbody&gt;</code>.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" striped responsive>
<thead>
<tr>
<th className="bt-0">#</th>
<th className="bt-0">Product Name</th>
<th className="bt-0">Last Refresh</th>
<th className="text-right bt-0">
Last Month
</th>
</tr>
</thead>
<tbody>
<TrTableStriped />
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 3 */}
{ /* START Section 4 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Hoverable
<span className="small ml-1 text-muted">
#4.01
</span>
</CardTitle>
<p className="mb-0">
Use <code>hover</code> to add zebra-striping to any table row within the <code>&lt;tbody&gt;</code>.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" hover responsive>
<thead>
<tr>
<th className="bt-0">#</th>
<th className="bt-0">Name</th>
<th className="bt-0">Price</th>
<th className="text-right bt-0">
Date
</th>
</tr>
</thead>
<tbody>
<TrTableHoverable />
<TrTableHoverable />
<TrTableHoverable />
<TrTableHoverable />
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 4 */}
{ /* START Section 5 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Small
<span className="small ml-1 text-muted">
#5.01
</span>
</CardTitle>
<p className="mb-0">
Add <code>size="sm"</code> to make tables more compact by cutting cell padding in half.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" hover responsive>
<thead>
<tr>
<th className="bt-0">ID</th>
<th className="bt-0">Name</th>
<th className="bt-0">Amount</th>
<th className="text-right bt-0">
Payment
</th>
</tr>
</thead>
<tbody>
<TrTableSmall />
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 5 */}
{ /* START Section 6 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Borderless
<span className="small ml-1 text-muted">
#6.01
</span>
</CardTitle>
<p className="mb-0">
Add <code>borderless</code> for a table without borders.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" borderless responsive>
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th>Date</th>
<th>Amount</th>
<th>Description</th>
<th>Payment Method</th>
<th className="text-right">
Receipt
</th>
</tr>
</thead>
<tbody>
<TrTableBorderless />
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 6 */}
{ /* START Section 7 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Bordered
<span className="small ml-1 text-muted">
#7.01
</span>
</CardTitle>
<p>
Add <code>bordered</code> for borders on all sides of the table and cells.
</p>
{ /* START Table */}
<Table className="mb-0" bordered responsive>
<thead>
<tr>
<th>Ticket</th>
<th>Completion</th>
<th>Create</th>
<th>Deadline</th>
<th className="text-right">
Actions
</th>
</tr>
</thead>
<tbody>
<TrTableBordered />
</tbody>
</Table>
{ /* END Table */}
</CardBody>
</Card>
</Col>
</Row>
{ /* END Section 7 */}
{ /* START Section 8 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Heads
<span className="small ml-1 text-muted">
#8.01
</span>
</CardTitle>
<p className="mb-0">
Similar to tables and dark tables, use the modifier classes
<code>.thead-light</code> or <code>.thead-dark</code> to make
<code>&lt;thead&gt;</code>s appear light or dark gray.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" hover responsive>
<thead className="thead-light">
<tr>
<th className="bt-0">#</th>
<th className="bt-0">First Name</th>
<th className="bt-0">Last Name</th>
<th className="bt-0">Email</th>
<th className="bt-0">Nick</th>
<th className="text-right bt-0">
Role
</th>
</tr>
</thead>
<tbody>
<TrTableHeads />
</tbody>
<thead className="thead-dark">
<tr>
<th className="bt-0">#</th>
<th className="bt-0">First Name</th>
<th className="bt-0">Last Name</th>
<th className="bt-0">Email</th>
<th className="bt-0">Nick</th>
<th className="text-right bt-0">
Role
</th>
</tr>
</thead>
<tbody>
<TrTableHeads />
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 8 */}
{ /* START Section 9 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3">
<CardBody>
<CardTitle tag="h6">
Table Contextual
<span className="small ml-1 text-muted">
#9.01
</span>
</CardTitle>
<p className="mb-0">
Use contextual classes to color table rows or individual cells.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" hover responsive>
<thead>
<tr>
<th className="bt-0">Invoice</th>
<th className="bt-0">Name</th>
<th className="bt-0">Date</th>
<th className="bt-0">Price</th>
<th className="bt-0">Status</th>
<th className="text-right bt-0">
Country
</th>
</tr>
</thead>
<tbody>
<TrTableContextual />
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 9 */}
{ /* START Section 10 */}
<Row>
<Col lg={ 12 }>
<Card className="mb-3" type="background" color="dark">
<CardBody>
<CardTitle tag="h6">
Table Inverse
<span className="small ml-1 text-muted">
#1.10
</span>
</CardTitle>
<p className="mb-0">
You can also invert the colorswith light text on dark backgroundswith <code>dark</code>.
</p>
</CardBody>
{ /* START Table */}
<Table className="mb-0" dark responsive>
<thead>
<tr>
<th className="bt-0">Project</th>
<th className="bt-0">Deadline</th>
<th className="bt-0">Leader</th>
<th className="bt-0">Budget</th>
<th className="bt-0">Status</th>
<th className="text-right bt-0">
Actions
</th>
</tr>
</thead>
<tbody>
<TrTableDefault
projectColor="text-white"
leaderStatus="900"
dropdownColor="text-white"
/>
</tbody>
</Table>
{ /* END Table */}
</Card>
</Col>
</Row>
{ /* END Section 10 */}
</Container>
</React.Fragment>
);
export default Tables;

View File

@@ -0,0 +1,55 @@
import React from 'react';
import faker from 'faker/locale/en_US';
import _ from 'lodash';
import {
Progress,
ButtonGroup,
Button
} from './../../../../components';
/*eslint-disable */
const completion = [
"25",
"50",
"75",
"97"
];
/*eslint-enable */
const TrTableBordered = () => (
<React.Fragment>
{
_.times(5, (index) => (
<tr key={ index }>
<td className="align-middle">
<span className="text-inverse">
{ faker.company.catchPhrase() }
</span>
</td>
<td className="align-middle">
<Progress value={ completion[index%4] } style={{height: "5px"}} />
</td>
<td className="align-middle">
{ faker.date.weekday() }, 12 { faker.date.month() }, 2018
</td>
<td className="align-middle">
{ faker.date.weekday() }, 12 { faker.date.month() }, 2018
</td>
<td className="align-middle text-right">
<ButtonGroup>
<Button color="link" className="text-decoration-none">
<i className="fa fa-clone"></i>
</Button>
<Button color="link" className="text-decoration-none">
<i className="fa fa-close"></i>
</Button>
</ButtonGroup>
</td>
</tr>
))
}
</React.Fragment>
)
export { TrTableBordered };

View File

@@ -0,0 +1,92 @@
import React from 'react';
import faker from 'faker/locale/en_US';
import _ from 'lodash';
import {
Badge,
UncontrolledTooltip,
} from './../../../../components';
/*eslint-disable */
const payment = [
<Badge color="primary">
Premium
</Badge>,
<Badge color="info">
Basic
</Badge>,
<Badge color="warning">
Pro
</Badge>,
<Badge color="danger">
Advanced
</Badge>,
<Badge color="secondary">
Free
</Badge>
];
/*eslint-enable */
/*eslint-disable */
const receipt = [
<td className="align-middle text-right">
<a href="#" id="UncontrolledTooltipDownload">
<i className="fa fa-fw fa-download text-primary"></i>
</a>
<UncontrolledTooltip placement="left" target="UncontrolledTooltipDownload">
Download
</UncontrolledTooltip>
</td>,
<td className="align-middle text-right">
</td>
];
/*eslint-enable */
/*eslint-disable */
const paymentMethod = [
<td className="align-middle">
<i className="fa fa-fw fa-paypal text-primary mr-2"></i>
{ faker.internet.email() }
</td>,
<td className="align-middle">
<i className="fa fa-fw fa-credit-card-alt mr-2"></i>
Visa 4*** **** **** 9221
</td>
];
/*eslint-enable */
/*eslint-disable */
const status = [
<td className="align-middle">
<i className="fa fa-fw fa-check text-success"></i>
</td>,
<td className="align-middle">
<i className="fa fa-fw fa-close text-danger"></i>
</td>
];
/*eslint-enable */
const TrTableBorderless = () => (
<React.Fragment>
{
_.times(5, (index) => (
<tr key={ index }>
{ status[index%2] }
<td className="align-middle">
<samp>{ faker.random.number() }</samp>
</td>
<td className="align-middle">
{ faker.date.weekday() }, 12 { faker.date.month() }, 2018
</td>
<td className="align-middle text-inverse">
$ { faker.finance.amount() }
</td>
<td className="align-middle">
{ payment[index%5] }
</td>
{ paymentMethod[index%2] }
{ receipt[index%2] }
</tr>
))
}
</React.Fragment>
)
export { TrTableBorderless };

View File

@@ -0,0 +1,83 @@
import React from 'react';
import faker from 'faker/locale/en_US';
import _ from 'lodash';
import {
Badge
} from './../../../../components';
/*eslint-disable */
const trColor = [
"table-active",
"",
"table-success",
"",
"table-info",
"",
"table-warning",
"",
"table-danger",
"",
"table-primary",
""
];
/*eslint-enable */
/*eslint-disable */
const statusColor = [
"secondary",
"secondary",
"success",
"secondary",
"info",
"secondary",
"warning",
"secondary",
"danger",
"secondary",
"primary",
"secondary"
];
/*eslint-enable */
const TrTableContextual = () => (
<React.Fragment>
{
_.times(12, (index) => (
<tr className={ trColor[index%12] } key={ index }>
<td className="align-middle">
#{ faker.finance.mask() }
</td>
<td className="align-middle">
{ faker.name.firstName() } { faker.name.lastName() }
</td>
<td className="align-middle">
{ faker.date.weekday() }, 12 { faker.date.month() }, 2018
</td>
<td className="align-middle">
$ { faker.finance.amount() }
</td>
<td className="align-middle">
<Badge color={ statusColor[index%12] }>
{ faker.finance.accountName() }
</Badge>
</td>
<td className="align-middle text-right">
{ faker.address.country() }
</td>
</tr>
))
}
</React.Fragment>
)
export { TrTableContextual };

View File

@@ -0,0 +1,127 @@
import React from 'react';
import faker from 'faker/locale/en_US';
import _ from 'lodash';
import PropTypes from 'prop-types';
import {
UncontrolledButtonDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
Media,
Avatar,
AvatarAddOn
} from './../../../../components';
import { randomAvatar } from './../../../../utilities';
/*eslint-disable */
const colorStatus = [
"danger",
"success",
"warning",
"secondary"
];
/*eslint-enable */
const TrTableDefault = (props) => (
<React.Fragment>
{
_.times(4, (index) => (
<tr key={ index }>
<td className="align-middle">
<div className={ props.projectColor }>
{ faker.name.firstName() } { faker.name.lastName() }
</div>
<span>
{ faker.company.companyName() }
</span>
</td>
<td className="align-middle">
<div>
Thursday
</div>
<span className="text-danger">
Overdue
</span>
</td>
<td className="align-middle">
<Media>
<Media left middle className="mr-3">
<Avatar.Image
size="md"
src={ randomAvatar() }
addOns={[
<AvatarAddOn.Icon
className="fa fa-circle"
color={ props.leaderStatus }
key="avatar-icon-bg"
/>,
<AvatarAddOn.Icon
className="fa fa-circle"
color={ colorStatus[index%4] }
key="avatar-icon-fg"
/>
]}
/>
</Media>
<Media body>
<div className="mt-0 d-flex text-inverse">
{ faker.name.firstName() } { faker.name.lastName() }
</div>
<span>
{ faker.name.jobTitle() }
</span>
</Media>
</Media>
</td>
<td className="align-middle">
<div>
{ faker.finance.amount() }
</div>
<span>
Paid
</span>
</td>
<td className="align-middle">
<i className="fa fa-circle-o text-success mr-2"></i>
{ faker.finance.transactionType() }
</td>
<td className="align-middle text-right">
<UncontrolledButtonDropdown>
<DropdownToggle color="link" className={` text-decoration-none ${ props.dropdownColor } `}>
<i className="fa fa-gear"></i><i className="fa fa-angle-down ml-2"></i>
</DropdownToggle>
<DropdownMenu right>
<DropdownItem>
<i className="fa fa-fw fa-envelope mr-2"></i>
Send Email
</DropdownItem>
<DropdownItem>
<i className="fa fa-fw fa-phone mr-2"></i>
Call
</DropdownItem>
<DropdownItem>
<i className="fa fa-fw fa-user mr-2"></i>
Profile
</DropdownItem>
</DropdownMenu>
</UncontrolledButtonDropdown>
</td>
</tr>
))
}
</React.Fragment>
)
TrTableDefault.propTypes = {
projectColor: PropTypes.node,
leaderStatus: PropTypes.node,
dropdownColor: PropTypes.node
};
TrTableDefault.defaultProps = {
projectColor: "text-inverse",
leaderStatus: "white",
dropdownColor: ""
};
export { TrTableDefault };

View File

@@ -0,0 +1,49 @@
import React from 'react';
import faker from 'faker/locale/en_US';
import _ from 'lodash';
import {
Badge
} from './../../../../components';
/*eslint-disable */
const colorStatus = [
"danger",
"success",
"warning",
"secondary"
];
/*eslint-enable */
const TrTableHeads = () => (
<React.Fragment>
{
_.times(4, (index) => (
<tr key={ index }>
<td className="align-middle">
1
</td>
<td className="align-middle">
{ faker.name.firstName() }
</td>
<td className="align-middle">
{ faker.name.lastName() }
</td>
<td className="align-middle">
{ faker.internet.email() }
</td>
<td className="align-middle">
{ faker.internet.userName() }
</td>
<td className="align-middle text-right">
<Badge color={ colorStatus[index%4] }>
{ faker.name.jobType() }
</Badge>
</td>
</tr>
))
}
</React.Fragment>
)
export { TrTableHeads };

View File

@@ -0,0 +1,25 @@
import React from 'react';
import faker from 'faker/locale/en_US';
const TrTableHoverable = () => (
<React.Fragment>
<tr>
<td className="align-middle">
<a href="#">
Invoice #{ faker.finance.mask() }
</a>
</td>
<td className="align-middle">
{ faker.name.firstName() } { faker.name.lastName() }
</td>
<td className="align-middle">
$ { faker.finance.amount() }
</td>
<td className="align-middle text-right">
{ faker.date.weekday() }, 12 { faker.date.month() }, 2018
</td>
</tr>
</React.Fragment>
)
export { TrTableHoverable };

View File

@@ -0,0 +1,97 @@
import React from 'react';
import faker from 'faker/locale/en_US';
import _ from 'lodash';
import {
UncontrolledTooltip,
Media
} from './../../../../components';
/*eslint-disable */
const browserOs = [
"Safari",
"Firefox",
"Opera",
"Chrome"
];
/*eslint-enable */
/*eslint-disable */
const browserIcon = [
"desktop",
"laptop",
"mobile",
"tablet"
];
/*eslint-enable */
/*eslint-disable */
const colorStatus = [
"danger",
"success",
"warning",
"secondary"
];
/*eslint-enable */
const TrTableResponsive = () => (
<React.Fragment>
{
_.times(4, (index) => (
<tr key={ index }>
<td className="align-middle">
<i className={`fa fa -fw fa-circle text-${ colorStatus[index%4] }`}></i>
</td>
<td className="align-middle">
<Media>
<Media left className="align-self-center mr-3">
<i className={`fa fa-fw fa-${ browserIcon[index%4] } fa-lg`}></i>
</Media>
<Media body>
<div className="mt-0 d-flex">
<span className="text-inverse">
{ browserOs[index%4] }
</span> /
{ faker.system.semver() }
</div>
<span>
macOs { faker.system.semver() }
</span>
</Media>
</Media>
</td>
<td className="align-middle">
<div>
<samp>
{ faker.internet.ip() }
</samp>
</div>
<span>
-
</span>
</td>
<td className="align-middle">
<div>
{ faker.address.city() }
</div>
<span>
{ faker.address.state() }, { faker.address.country() }
</span>
</td>
<td className="align-middle">
{ faker.date.weekday() }, 12 { faker.date.month() }, 2018<br />
12:34 PM
</td>
<td className="align-middle text-right">
<a href="#" id="UncontrolledTooltipRevoke">
<i className="fa fa-fw fa-close text-danger"></i>
</a>
<UncontrolledTooltip placement="left" target="UncontrolledTooltipRevoke">
Revoke
</UncontrolledTooltip>
</td>
</tr>
))
}
</React.Fragment>
)
export { TrTableResponsive };

View File

@@ -0,0 +1,43 @@
import React from 'react';
import faker from 'faker/locale/en_US';
import _ from 'lodash';
import {
Badge
} from './../../../../components';
/*eslint-disable */
const payment = [
"success",
"danger",
"warning",
"secondary"
];
/*eslint-enable */
const TrTableSmall = () => (
<React.Fragment>
{
_.times(4, (index) => (
<tr key={ index }>
<td className="align-middle">
#{ faker.finance.mask() }
</td>
<td className="align-middle">
{ faker.name.firstName() } { faker.name.lastName() }
</td>
<td className="align-middle">
$ { faker.finance.amount() }
</td>
<td className="align-middle text-right">
<Badge pill color={ payment[index%4] }>
{ faker.finance.transactionType() }
</Badge>
</td>
</tr>
))
}
</React.Fragment>
)
export { TrTableSmall };

View File

@@ -0,0 +1,47 @@
import React from 'react';
import faker from 'faker/locale/en_US';
import _ from 'lodash';
/*eslint-disable */
const lastMonth = [
<td className="align-middle text-right text-danger">
<i className="fa fa-fw fa-caret-down mr-1"></i>92.02%
</td>,
<td className="align-middle text-right text-success">
<i className="fa fa-fw fa-caret-up mr-1"></i>23.02%
</td>
];
/*eslint-enable */
/*eslint-disable */
const no = [
"1",
"2",
"3",
"4"
];
/*eslint-enable */
const TrTableStriped = () => (
<React.Fragment>
{
_.times(4, (index) => (
<tr key={ index }>
<td className="align-middle">
{ no[index%4] }.
</td>
<td className="align-middle">
<span className="text-inverse">
{ faker.commerce.productName() }
</span>
</td>
<td className="align-middle">
{ faker.date.weekday() }, 12 { faker.date.month() }, 2018
</td>
{ lastMonth[index%2] }
</tr>
))
}
</React.Fragment>
)
export { TrTableStriped };

View File

@@ -0,0 +1,3 @@
import Tables from './Tables';
export default Tables;