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,300 @@
import React from 'react';
import PropTypes from 'prop-types';
import uid from 'uuid/v4';
import _ from 'lodash';
import faker from 'faker/locale/en_US';
import {
DragDropContext,
Droppable,
Draggable
} from 'react-beautiful-dnd';
import classNames from 'classnames';
import {
Table,
Badge,
Avatar,
AvatarAddOn,
Progress,
Card,
CardHeader,
CardTitle
} from './../../../../components';
import { randomAvatar, randomArray } from './../../../../utilities';
import { reorder } from './utilities';
import classes from './common.scss';
const allSkills = ['JavaScript', 'Photoshop', 'Management', 'Bootstrap',
'PHP', 'Sketch', 'MySQL', 'Mongo', 'Node.js', 'TypeScript'];
const generateUser = () => ({
id: uid(),
name: `${faker.name.firstName()} ${faker.name.lastName()}`,
title: faker.name.jobType(),
avatarUrl: randomAvatar(),
status: randomArray(['success', 'warning', 'danger']),
skills: _.uniq(_.times(_.random(2, 5), () => randomArray(allSkills))),
interviewProgress: _.random(40, 90),
portfolio: (Math.round(Math.random())) ? {
url: 'http://webkom.co',
title: 'www.webkom.co'
} : null
});
const getTableClass = (isDraggedOver) =>
classNames(classes['table'], {
[classes['table--drag-over']]: isDraggedOver
});
const getRowClass = (isDragging) =>
classNames(classes['row'], {
[classes['row--dragging']]: isDragging
});
// Custom Table Cell - keeps cell width when the row
// is detached from the table
// ========================================================
class TableCell extends React.Component {
static propTypes = {
children: PropTypes.node,
isDragOccurring: PropTypes.bool
};
ref = React.createRef();
getSnapshotBeforeUpdate(prevProps) {
if (!this.ref) {
return null;
}
const ref = this.ref.current;
const isDragStarting =
this.props.isDragOccurring && !prevProps.isDragOccurring;
if (!isDragStarting) {
return null;
}
const { width, height } = ref.getBoundingClientRect();
const snapshot = { width, height };
return snapshot;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (!this.ref) {
return;
}
const ref = this.ref.current;
if (snapshot) {
if (ref.style.width === snapshot.width) {
return;
}
ref.style.width = `${snapshot.width}px`;
ref.style.height = `${snapshot.height}px`;
return;
}
if (this.props.isDragOccurring) {
return;
}
// inline styles not applied
if (ref.style.width == null) {
return;
}
// no snapshot and drag is finished - clear the inline styles
ref.style.removeProperty('height');
ref.style.removeProperty('width');
}
render() {
// eslint-disable-next-line no-unused-vars
const { children, isDragOccurring, ...otherProps } = this.props;
return <td ref={this.ref} {...otherProps}>{children}</td>;
}
}
// Draggable Table Row
// ========================================================
const DraggableRow = (props) => (
<Draggable
draggableId={props.id}
index={props.index}
>
{(provided, snapshot) => (
<tr
ref={ provided.innerRef }
{ ...provided.draggableProps }
className={getRowClass(snapshot.isDragging)}
>
<TableCell
className="align-middle"
isDragOccurring={snapshot.isDragging}
{ ...provided.dragHandleProps }
>
<i className="fa fa-fw fa-arrows-v fa-lg d-block mx-auto text-muted" />
</TableCell>
<TableCell
className="align-middle"
isDragOccurring={snapshot.isDragging}
>
<Avatar.Image
size="md"
className="d-block"
src={ props.avatarUrl }
addOns={[
<AvatarAddOn.Icon
className="fa fa-circle"
color="white"
key="avatar-icon-bg"
/>,
<AvatarAddOn.Icon
className="fa fa-circle"
color={ props.status }
key="avatar-icon-fg"
/>
]}
/>
</TableCell>
<TableCell
className="align-middle"
isDragOccurring={snapshot.isDragging}
>
<span className="mt-0 h6 mb-1">
{ props.name }
</span>
<p className="mb-0 text-muted text-truncate">
{ props.title }
</p>
</TableCell>
<TableCell
className="align-middle"
isDragOccurring={snapshot.isDragging}
>
{_.map(props.skills, (skill, index) => (
<Badge
key={ index }
className={`px-2 ${index > 0 && 'ml-1'}`}
>
{ skill }
</Badge>
))}
</TableCell>
<TableCell
className="align-middle"
isDragOccurring={snapshot.isDragging}
>
<Progress value={props.interviewProgress} slim />
<span className="fw-500">{ props.interviewProgress }%</span>
</TableCell>
<TableCell
className="text-right align-middle"
isDragOccurring={snapshot.isDragging}
>
{
!_.isEmpty(props.portfolio) ? (
<a href={props.portfolio.url}>
{ props.portfolio.title }
</a>
) : (
<span>-</span>
)
}
</TableCell>
</tr>
)}
</Draggable>
);
DraggableRow.propTypes = {
avatarUrl: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
skills: PropTypes.array.isRequired,
interviewProgress: PropTypes.number.isRequired,
portfolio: PropTypes.object,
index: PropTypes.number.isRequired
}
// Demo Component
// ========================================================
const initialState = _.times(5, generateUser);
export class DraggableTable extends React.Component {
static propTypes = {
className: PropTypes.string,
}
state = {
users: initialState
}
constructor(props) {
super(props);
this.onDragEnd = this.onDragEnd.bind(this);
}
onDragEnd({ source, destination }) {
if (!destination) {
return;
}
const users = reorder(this.state.users,
source.index, destination.index);
this.setState({ users });
}
recoverInitialState() {
this.setState({
users: initialState
});
}
render() {
return (
<Card className={ this.props.className }>
<CardHeader className="bg-none bb-0">
<CardTitle className="h6">
Queue of Candidates
</CardTitle>
</CardHeader>
<DragDropContext onDragEnd={this.onDragEnd}>
<Table className="mb-0">
<thead>
<tr>
<th className="bt-0"></th>
<th className="bt-0">Photo</th>
<th className="bt-0">Name</th>
<th className="bt-0">Skills</th>
<th className="bt-0">Interview Passed in</th>
<th className="bt-0 text-right">Portfolio</th>
</tr>
</thead>
<Droppable droppableId="table">
{(provided, snapshot) => (
<tbody
ref={provided.innerRef}
{...provided.droppableProps}
className={getTableClass(snapshot.isDraggingOver)}
>
{_.map(this.state.users, (user, index) => (
<DraggableRow
key={user.id}
index={index}
{ ...user }
/>
))}
</tbody>
)}
</Droppable>
</Table>
</DragDropContext>
</Card>
);
}
}

View File

@@ -0,0 +1,178 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import classNames from 'classnames';
import {
DragDropContext,
Droppable,
Draggable
} from 'react-beautiful-dnd';
import uid from 'uuid/v4';
import {
Card,
CardHeader,
CardTitle,
Avatar,
AvatarAddOn
} from './../../../../components';
import { randomAvatar, randomArray } from './../../../../utilities';
import { reorder, move } from './utilities';
import classes from './common.scss';
const generateItem = () => ({
id: uid(),
avatarUrl: randomAvatar(),
status: randomArray(['success', 'warning', 'danger'])
});
const getListClass = (isDraggedOver) =>
classNames(classes['list'], {
[classes['list--drag-over']]: isDraggedOver
});
const RowList = (props) => (
<Card className={props.className}>
<CardHeader className="bg-none">
<CardTitle className="h6 mb-0">
{ props.title }
</CardTitle>
</CardHeader>
<Droppable
droppableId={ props.listId }
direction="horizontal"
>
{(provided, snapshot) => (
<div
className={`card-body d-flex ${getListClass(snapshot.isDraggingOver)}`}
style={{ overflowX: 'auto' }}
ref={provided.innerRef}
{...provided.droppableProps}
>
{_.map(props.items, (item, index) => (
<Draggable
key={item.id}
draggableId={item.id}
index={index}
>
{(provided) => (
<div
className="px-3"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Avatar.Image
key={`avatar-${item.id}`}
size="lg"
className="d-block"
src={ item.avatarUrl }
addOns={[
<AvatarAddOn.Icon
className="fa fa-circle"
color="white"
key="avatar-icon-bg"
/>,
<AvatarAddOn.Icon
className="fa fa-circle"
color={ item.status }
key="avatar-icon-fg"
/>
]}
/>
</div>
)}
</Draggable>
))}
{ provided.placeholder }
</div>
)}
</Droppable>
</Card>
)
RowList.propTypes = {
listId: PropTypes.string.isRequired,
items: PropTypes.array.isRequired,
title: PropTypes.string.isRequired,
className: PropTypes.stirng
}
const initialState = {
listAItems: _.times(_.random(3, 8), generateItem),
listBItems: _.times(_.random(3, 8), generateItem),
listCItems: _.times(_.random(3, 8), generateItem)
};
export class HorizontalLists extends React.Component {
static propTypes = {
className: PropTypes.string
}
state = _.clone(initialState);
constructor (props) {
super(props);
this.onDragEnd = this.onDragEnd.bind(this);
}
onDragEnd(result) {
const { source, destination } = result;
// dropped outside the list
if (!destination) {
return;
}
// Handle List Items
if (source.droppableId === destination.droppableId) {
const items = reorder(
this.state[`${source.droppableId}Items`],
source.index,
destination.index
);
this.setState({
[`${source.droppableId}Items`]: items
});
} else {
const result = move(
this.state[`${source.droppableId}Items`],
this.state[`${destination.droppableId}Items`],
source,
destination
);
this.setState(_.mapKeys(result, (val, key) => `${key}Items`));
}
}
recoverInitialState() {
this.setState(initialState);
}
render() {
const { className } = this.props;
return (
<div className={ className }>
<DragDropContext onDragEnd={this.onDragEnd}>
<RowList
listId="listA"
items={ this.state.listAItems }
title="All Candidates"
/>
<RowList
listId="listB"
items={ this.state.listBItems }
title="Candidates Interview"
className="mt-4"
/>
<RowList
listId="listC"
items={ this.state.listCItems }
title="Candidates Testing"
className="mt-4"
/>
</DragDropContext>
</div>
)
}
}

View File

@@ -0,0 +1,267 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import {
DragDropContext,
Droppable,
Draggable
} from 'react-beautiful-dnd';
import uid from 'uuid/v4';
import faker from 'faker/locale/en_US';
import classNames from 'classnames';
import {
Card,
CardHeader,
CardTitle,
Media,
Avatar,
AvatarAddOn
} from './../../../../components';
import { randomAvatar, randomArray } from './../../../../utilities';
import { reorder, move } from './utilities';
import classes from './common.scss';
// Utility Functions
//=========================================================
const generateItem = () => ({
id: uid(),
type: 'single',
name: `${faker.name.firstName()} ${faker.name.lastName()}`,
title: faker.name.jobType(),
avatarUrl: randomAvatar(),
status: randomArray(['success', 'warning', 'danger'])
});
const getListClass = (isDraggedOver) =>
classNames(classes['list'], {
[classes['list--drag-over']]: isDraggedOver
});
const getItemClass = (isDragging) =>
classNames(classes['list-group-item'], {
[classes['list-group-item--dragging']]: isDragging
});
// Draggable List Component
//=========================================================
const VerticalList = React.memo((props) => {
return (
<Droppable droppableId={ props.listId }>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
className={`list-group list-group-flush flex-grow-1 ${getListClass(snapshot.isDraggingOver)}`}
>
{props.items.map((item, index) => (
<Draggable
key={item.id}
draggableId={item.id}
index={index}>
{(provided, draggableSnapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
className={`list-group-item ${getItemClass(draggableSnapshot.isDragging)}`}
>
<Media>
<Media left className="align-self-center pr-3">
<i className="fa fa-ellipsis-v text-muted" />
</Media>
<Media left middle className="mr-4 align-self-center">
<Avatar.Image
size="md"
className="d-block"
src={ item.avatarUrl }
addOns={[
<AvatarAddOn.Icon
className="fa fa-circle"
color="white"
key="avatar-icon-bg"
/>,
<AvatarAddOn.Icon
className="fa fa-circle"
color={ item.status }
key="avatar-icon-fg"
/>
]}
/>
</Media>
<Media body>
<span className="mt-0 h6 mb-1">
{ item.name }
</span>
<p className="mb-0 text-muted text-truncate">
{ item.title }
</p>
</Media>
</Media>
</div>
)}
</Draggable>
))}
</div>
)}
</Droppable>
);
});
VerticalList.propTypes = {
items: PropTypes.array,
listId: PropTypes.string,
title: PropTypes.string
}
// Draggable Column Component
//=========================================================
class Column extends React.Component {
static propTypes = {
children: PropTypes.node,
id: PropTypes.string,
index: PropTypes.number,
title: PropTypes.string
}
render() {
const { children, id, index, title } = this.props;
return (
<Draggable
draggableId={id}
index={index}
>
{(provided) => (
<div
className="col-md-4"
ref={provided.innerRef}
{...provided.draggableProps}
>
<Card className="h-100">
<CardHeader {...provided.dragHandleProps} className="b-0 bg-none">
<CardTitle className="h6 mb-0">
<i className="fa fa-ellipsis-v mr-3 text-muted" />
{ title }
</CardTitle>
</CardHeader>
{ children }
</Card>
</div>
)}
</Draggable>
)
}
}
// Demo Component
//=========================================================
const initialState = {
listAItems: _.times(_.random(2, 4), generateItem),
listBItems: _.times(_.random(3, 8), generateItem),
listCItems: _.times(_.random(3, 8), generateItem),
lists: [
{ id: 'listA', title: 'All Candidates' },
{ id: 'listB', title: 'Candidates Interview' },
{ id: 'listC', title: 'Candidates Testing' }
]
};
export class MultipleVerticalLists extends React.Component {
static propTypes = {
className: PropTypes.string
}
state = _.clone(initialState);
constructor (props) {
super(props);
this.onDragEnd = this.onDragEnd.bind(this);
}
onDragEnd(result) {
const { source, destination } = result;
// Swap column positions
if (source.droppableId === 'board') {
if (destination.droppableId !== 'board') {
return;
}
const lists = reorder(
this.state.lists,
source.index,
destination.index
);
this.setState({ lists });
return;
}
// dropped outside the list
if (!destination) {
return;
}
// Handle List Items
if (source.droppableId === destination.droppableId) {
const items = reorder(
this.state[`${source.droppableId}Items`],
source.index,
destination.index
);
this.setState({
[`${source.droppableId}Items`]: items
});
} else {
const result = move(
this.state[`${source.droppableId}Items`],
this.state[`${destination.droppableId}Items`],
source,
destination
);
this.setState(_.mapKeys(result, (val, key) => `${key}Items`));
}
}
recoverInitialState() {
this.setState(initialState);
}
render() {
const { className } = this.props;
const { lists } = this.state;
return (
<div className={ className }>
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable
droppableId="board"
type="COLUMN"
direction="horizontal"
>
{(provided) => (
<div
className="row"
ref={provided.innerRef}
{...provided.droppableProps}
>
{_.map(lists, (list, index) => (
<Column
id={list.id}
index={ index }
title={list.title}
key={ list.id }
>
<VerticalList
listId={list.id}
items={this.state[`${list.id}Items`]}
/>
</Column>
))}
</div>
)}
</Droppable>
</DragDropContext>
</div>
)
}
}

View File

@@ -0,0 +1,43 @@
@import "./../../../../styles/variables.scss";
.list {
transition: background-color 200ms cubic-bezier(0.645, 0.045, 0.355, 1.000);
&--drag-over {
background-color: lighten($primary, 45%);
}
}
.list-group-item {
border-left: 1px solid transparent !important;
border-right: 1px solid transparent !important;
transition: border-color 200ms cubic-bezier(0.645, 0.045, 0.355, 1.000);
&:first-child {
border-top: 1px solid transparent !important;
}
&--dragging {
&:first-child {
border-top-color: $primary !important;
}
border-color: $primary !important;
}
}
.table {
transition: background-color 200ms cubic-bezier(0.645, 0.045, 0.355, 1.000);
&--drag-over {
background-color: lighten($primary, 45%);
}
}
.row {
transition: border-color 200ms cubic-bezier(0.645, 0.045, 0.355, 1.000);
background: $white;
&--dragging {
border: 1px solid $primary !important;
}
}

View File

@@ -0,0 +1,3 @@
export * from './MultipleVerticalLists';
export * from './DraggableTable';
export * from './HorizontalLists';

View File

@@ -0,0 +1,21 @@
export const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
export const move = (source, destination, droppableSource, droppableDestination) => {
const sourceClone = Array.from(source);
const destClone = Array.from(destination);
const [removed] = sourceClone.splice(droppableSource.index, 1);
destClone.splice(droppableDestination.index, 0, removed);
const result = {};
result[droppableSource.droppableId] = sourceClone;
result[droppableDestination.droppableId] = destClone;
return result;
};