Created
October 12, 2015 07:13
-
-
Save edtoken/2d5803e9f06efb2660e0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var monthNames = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']; | |
/** | |
* количество дней в месяце | |
* @param date | |
* @returns {number} | |
*/ | |
var getMonthCountDays = function (date) { | |
var here = new Date(date.getTime()); | |
here.setDate(32); | |
return 32 - here.getDate(); | |
}; | |
/** | |
* узнать количество дней между датами | |
* @param beginingDate | |
* @param endingDate | |
* @returns {number} | |
*/ | |
var getDaysDiff = function (beginingDate, endingDate) { | |
return Math.floor((endingDate.getTime() - beginingDate.getTime()) / 24 / 60 / 60 / 1000); | |
}; | |
/** | |
* добавит количество дней к дате и вернет новую дату | |
* @param date | |
* @param days | |
* @returns {Date} | |
*/ | |
var addDaysToDate = function (date, days) { | |
var out = new Date(date.getTime()); | |
out.setDate(out.getDate() + days); | |
return out; | |
}; | |
/** | |
* Одна группа с данными | |
*/ | |
class DataGroup { | |
constructor(data) { | |
this.id = _.uniqueId('group-'); | |
this.attributes = { | |
start: false, // дата начала | |
end: false, // дата конца | |
data: [] // данные массив объектов при сравнении сравнивается первый уровень объекта | |
}; | |
_.extend(this.attributes, _.clone(data)); | |
this.attributes.id = this.id; | |
} | |
/** | |
* объединит текущую группу с переданной | |
* @param objectDataGroup | |
*/ | |
join(objectDataGroup){ | |
var joinGroupJSON = objectDataGroup.toJSON(); | |
//пересчитываю дату начала и конца | |
var newDateStart = (this.attributes.start.getDateTime().getTime() > joinGroupJSON.start.getDateTime().getTime()) | |
? joinGroupJSON.start | |
: this.attributes.start; | |
var newDateEnd = (this.attributes.end.getDateTime().getTime() > joinGroupJSON.end.getDateTime().getTime()) | |
? this.attributes.end | |
: joinGroupJSON.end; | |
//тут можно добавить проверку данных, или их объединение | |
this.attributes.start = newDateStart; | |
this.attributes.end = newDateEnd | |
} | |
toJSON() { | |
return _.clone(this.attributes); | |
} | |
getCloneData() { | |
return this.attributes.data.slice(0); | |
} | |
} | |
/** | |
* Отрисовка одной группы | |
*/ | |
class SlideTableGroup extends Component { | |
constructor(props) { | |
super(props); | |
this.state = {editDates: false, data:props.data.toJSON()}; | |
} | |
componentWillReciveProps(nextProps) { | |
this.props = nextProps; | |
this.setState({editDates: false, data:this.props.data.toJSON()}); | |
} | |
clickChangeDate(e, data) { | |
e.preventDefault(); | |
var value = data.value; | |
var data = this.state.data; | |
// если это первый элемент у него нельзя смещать первую дату | |
// если это последние элемент у него нельзя смещать последнюю дату | |
data.start = (this.props.notFirstItem)? new ObjectDate(value[0]) : this.state.data.start; | |
data.end = (this.props.notLastItem)? new ObjectDate(value[1]) : this.state.data.end; | |
console.log('clickChangeDate', data.start, data.end); | |
this.setState({data:data}); | |
} | |
/** | |
* Клик разделить группу | |
* @param e | |
*/ | |
clickSplitGroup(e) { | |
e.preventDefault(); | |
this.props.smartSplit(this.state.data.id, 2); | |
} | |
/** | |
* клик изменить группу | |
* @param e | |
*/ | |
clickEnableEditDates(e) { | |
e.preventDefault(); | |
this.props.onChangeEditDates(this.state.data.id, true); | |
} | |
/** | |
* клик сохранить даты | |
* @param e | |
*/ | |
clickSaveEditDates(e) { | |
e.preventDefault(); | |
this.props.onChangeEditDates(this.state.data.id, false); | |
var newStart = this.state.data.start; | |
var newEnd = this.state.data.end; | |
this.props.updateGroupDatesById(this.state.data.id, newStart, newEnd); | |
} | |
/** | |
* Клик отменить изменения дат | |
* @param e | |
*/ | |
clickCancelEditDates(e){ | |
e.preventDefault(); | |
var data = this.state.data; | |
var olddata = this.props.data.toJSON(); | |
data.start = olddata.start; | |
data.end = olddata.end; | |
this.setState({data:data}); | |
this.props.onChangeEditDates(this.state.data.id, false); | |
} | |
/** | |
* клик объединить с правым | |
* @param e | |
*/ | |
clickJoinRight(e) { | |
e.preventDefault(); | |
this.props.joinRightById(this.state.data.id); | |
} | |
/** | |
* клик объединить с левым | |
* @param e | |
*/ | |
clickJoinLeft(e) { | |
e.preventDefault(); | |
this.props.joinLeftById(this.state.data.id); | |
} | |
render() { | |
var groupMonthStringName; | |
var data = this.state.data; | |
var dateStart = data.start.getDateTime(); | |
var dateEnd = data.end.getDateTime(); | |
var enableEditDates = this.props.enableEditDates; | |
var enableChangeDates = this.props.enableChangeDates; | |
var enableJoinRight = this.props.notLastItem; | |
var enableJoinLeft = this.props.notFirstItem; | |
var enableSplit = (dateStart.getTime() !== dateEnd.getTime()); | |
if (dateStart.getMonth() === dateEnd.getMonth()) { | |
groupMonthStringName = monthNames[dateStart.getMonth()]; | |
groupMonthStringName += '(' + dateStart.getDate() + ' - ' + dateEnd.getDate() + ')'; | |
} else { | |
groupMonthStringName = monthNames[dateStart.getMonth()] | |
+ '(' + dateStart.getDate() + ')' | |
+ ' - ' + monthNames[dateEnd.getMonth()] | |
+ '(' + dateEnd.getDate() + ')'; | |
} | |
var dateComponentProps = { | |
config: { | |
title: 'Выберите период' | |
}, | |
value:[data.start.formatServerDateTime(), data.end.formatServerDateTime()], | |
enabled:this.props.editablePeriod(data.id), // массивы периудов или дат | |
onChange:this.clickChangeDate.bind(this) | |
}; | |
var styles = { | |
display:'table-cell', | |
//'minWidth':'150px' | |
}; | |
if(enableEditDates){ | |
styles.border = '1px solid #ddd'; | |
styles.background = '#eee'; | |
styles.padding = '15px'; | |
} | |
var stylesHeader = { | |
'borderBottom':'1px dashed #ddd', | |
borderRight:'1px solid #fff', | |
'paddingBottom':'7px', | |
fontSize:'10px', | |
fontWeight:'bold', | |
padding:'5px', | |
textAlign:'center', | |
color:'#333335', | |
background:'#cce5f4', | |
}; | |
var stylesHeaderTitle = { | |
maxWidth:'120px', | |
margin:'0 auto', | |
height:'37px', | |
lineHeight:'12px', | |
display:'block', | |
}; | |
var stylesHeaderActions = { | |
'paddingLeft':'7px', | |
'paddingRight':'7px', | |
margin:'0 0 7px', | |
}; | |
return ( | |
<div style={styles}> | |
<div style={stylesHeader}> | |
<span style={stylesHeaderTitle}>{groupMonthStringName}</span> | |
<div style={stylesHeaderActions}> | |
{!enableEditDates && <div> | |
{enableChangeDates && enableJoinLeft && | |
<button onClick={this.clickJoinLeft.bind(this)} className="btn btn-xs btn-link" title="Объединить с левым">←</button> | |
} | |
{enableSplit && <button onClick={this.clickSplitGroup.bind(this)} className="btn btn-xs btn-link">разбить</button>} | |
{enableChangeDates && <button onClick={this.clickEnableEditDates.bind(this)} className="btn btn-xs btn-link"> изменить</button>} | |
{enableChangeDates && enableJoinRight && | |
<button onClick={this.clickJoinRight.bind(this)} className="btn btn-xs btn-link" title="Объединить с правым">→</button> | |
} | |
</div>} | |
{enableEditDates && <div> | |
<FieldDate {...dateComponentProps} /> | |
<button onClick={this.clickSaveEditDates.bind(this)} className="btn btn-xs btn-success">Сохранить</button> | |
<button onClick={this.clickCancelEditDates.bind(this)} className="btn btn-xs btn-danger">Отмена</button> | |
</div>} | |
</div> | |
</div> | |
</div>); | |
} | |
} | |
/** | |
* Основная таблица | |
* рисует внутри себя группы | |
*/ | |
class SlideTable extends Component { | |
constructor(props) { | |
super(props); | |
// базовая группа | |
var baseGroup = new DataGroup({ | |
start: new ObjectDate(new Date(2015, 0, 1)), | |
end: new ObjectDate(new Date(2015, 11, getMonthCountDays(new Date(2015, 11, 1)))), | |
//data:[{},{},{}] | |
}); | |
this.state = { | |
groups: [baseGroup], | |
editGroupId:'' | |
} | |
} | |
/** | |
* Найти группу по ID | |
* @param id | |
* @returns {*} | |
*/ | |
getGroupById(id) { | |
return _.find(this.state.groups, group => (group.id === id)); | |
} | |
/** | |
* получить группу по индексу | |
* @param index | |
*/ | |
getGroupByIndex(index){ | |
return (this.state.groups[index])? this.state.groups[index] : false; | |
} | |
/** | |
* Вернет все группы которые цепляют промежуток между датами | |
* @param from | |
* @param to | |
*/ | |
getGroupsByDateRange(from, to){ | |
var f = from.getDateTime().getTime(); | |
var t = to.getDateTime().getTime(); | |
return _.filter(this.state.groups, function(group){ | |
var s = group.attributes.start.getDateTime().getTime(); | |
var e = group.attributes.end.getDateTime().getTime(); | |
// начало или конец группы находятся в промежутке | |
if(s >= f && s <= t) return true; | |
if(e >= f && e <= t) return true; | |
return false; | |
}); | |
} | |
/** | |
* получить следующую группу от группы по переданному ID | |
* @param id | |
*/ | |
getNextGroupById(id){ | |
var index = this.getGroupIndexById(id); | |
index++; | |
return this.getGroupByIndex(index); | |
} | |
/** | |
* получить предыдущую группу от группы по переданному id | |
* @param id | |
*/ | |
getPrevGroupById(id){ | |
var index = this.getGroupIndexById(id); | |
index--; | |
return this.getGroupByIndex(index); | |
} | |
/** | |
* Получить index (положение в массиве) группы по id | |
* @param id | |
* @returns {*} | |
*/ | |
getGroupIndexById(id) { | |
for (var i = 0; i < this.state.groups.length; i++) { | |
if (this.state.groups[i].id !== id) continue; | |
return i; | |
} | |
return undefined; | |
} | |
/** | |
* Вернет массив периодов для редактирования (периоды доступных дат) | |
* @param id | |
* @returns {Array} | |
*/ | |
getEditPeriodByGroupId(id){ | |
var prev = this.getPrevGroupById(id); | |
var next = this.getNextGroupById(id); | |
var out = []; | |
var start = (prev)? prev.toJSON().start : this.getGroupById(id).toJSON().start; | |
var end = (next)? next.toJSON().end : this.getGroupById(id).toJSON().end; | |
out.push([start.formatServerDateTime(), end.formatServerDateTime()]); | |
return out; | |
} | |
/** | |
* Проверить идентичность данных двух групп | |
* @param group1 | |
* @param group2 | |
*/ | |
isEqualGroups(group1, group2) { | |
return _.isEqual(group1, group2); | |
} | |
/** | |
* удалить группу по ID | |
* @param id | |
*/ | |
removeGroupById(id) { | |
var index = this.getGroupIndexById(id); | |
if (index !== undefined) { | |
this.state.groups.splice(index, 1); | |
this.sort(); | |
} | |
} | |
/** | |
* сортирует переданный массив групп по датам | |
* @param groups | |
* @returns {Array} | |
* @private | |
*/ | |
_sort(groups){ | |
return _.sortBy(groups, group => (group.attributes.start.getDateTime().getTime())); | |
} | |
/** | |
* пересортировать группы | |
* сортировка происходит по дате start | |
*/ | |
sort() { | |
var groups = this._sort(this.state.groups); | |
this.setState({groups: groups, editGroupId:''}); | |
} | |
/** | |
* вставить группу | |
* @param group | |
*/ | |
pushGroup(group) { | |
var groups = this.state.groups; | |
groups.push(group); | |
this.setState({groups: groups, editGroupId:''}, function () { | |
this.sort(); | |
}); | |
} | |
/** | |
* вставить группы | |
* @param arr | |
*/ | |
pushGroups(arr) { | |
var groups = this.state.groups; | |
groups = groups.concat(arr); | |
this.setState({groups: groups, editGroupId:''}, function () { | |
this.sort(); | |
}); | |
} | |
/** | |
* Разделить группу на количество кусков | |
* удалить старую группу вставить сформированные | |
* @param groupId | |
*/ | |
splitGroup(groupID, count) { | |
var group = this.getGroupById(groupID); | |
var startDate = group.attributes.start.getDateTime(); | |
var endDate = group.attributes.end.getDateTime(); | |
var days = getDaysDiff(startDate, endDate); | |
var stepValue = days / count; | |
var groupData = group.getCloneData(); | |
var createGroups = []; | |
var steps = count; | |
while (steps) { | |
var nextEndDate = addDaysToDate(startDate, stepValue); | |
if (nextEndDate > endDate) { | |
nextEndDate = endDate; | |
} | |
createGroups.push(new DataGroup({start: new ObjectDate(startDate), end: new ObjectDate(nextEndDate), data: groupData})); | |
startDate = addDaysToDate(nextEndDate, 1); | |
steps--; | |
} | |
this.removeGroupById(groupID); | |
this.pushGroups(createGroups); | |
} | |
/** | |
* Разделит группу на количество кусков с дополнительной логикой | |
* @param groupId | |
* @param count | |
*/ | |
smartSplit(groupId, count){ | |
if(this.state.groups.length === 1) count = 6; | |
this.splitGroup(groupId, count); | |
} | |
/** | |
* Объединит группу 1 и группу 2 | |
* @param id1 | |
* @param id2 | |
*/ | |
joinGroupsByIds(id1, id2){ | |
var groups = this.state.groups; | |
this.getGroupById(id1).join(this.getGroupById(id2)); | |
this.removeGroupById(id2); | |
this.setState({groups:groups, editGroupId:''}, function(){ | |
this.sort(); | |
}); | |
} | |
/** | |
* Объединить группу по ID с правой от неё группой | |
* если данные идентичны | |
*/ | |
joinRightById(groupID) { | |
var nextGroup = this.getNextGroupById(groupID); | |
this.joinGroupsByIds(nextGroup.id, groupID); | |
} | |
/** | |
* Объединить группу по ID с лувой от неё группой | |
* если данные идентичны | |
*/ | |
joinLeftById(groupID) { | |
var prevGroup = this.getPrevGroupById(groupID); | |
this.joinGroupsByIds(prevGroup.id, groupID); | |
} | |
/** | |
* Обновит значение дат группы по ID | |
* при этом если данная группа "заехала" на какие-либо другие, то другие будут сжаты/расширены | |
* @param groupId | |
* @param start | |
* @param end | |
*/ | |
updateGroupDatesById(groupId, start, end){ | |
console.log('updateGroupDatesById', groupId, start, end); | |
var group = this.getGroupById(groupId); | |
var json = group.toJSON(); | |
var s = start.getDateTime(); | |
var st = s.getTime(); | |
var e = end.getDateTime(); | |
var et = e.getTime(); | |
// все группы которые попадают к этот временной промежуток | |
// отсортированные по времени | |
var modifyGroups = this._sort(this.getGroupsByDateRange(start, end)); | |
console.log('modifyGroups', modifyGroups); | |
for(var i=0; i<modifyGroups.length;i++){ | |
var g = modifyGroups[i]; // группа | |
var main = (g.id == json.id); // это основная изменяемая группа | |
var gs = g.attributes.start.getDateTime(); // её время начала | |
var gsTime = gs.getTime(); // её время начала | |
var ge = g.attributes.end.getDateTime();// её время конца | |
var geTime = ge.getTime();// её время конца | |
// самая первая затронутая группа | |
// добавить json данных групп | |
//var moveToLeft = (gs > s) | |
// если это main группа | |
// то ставим ей требуемые значения | |
//if(main){ | |
// g.attributes.start = start; | |
// g.attributes.end = end; | |
//} | |
console.log('i', i, g.attributes.start.getDateTime(), g.attributes.end.getDateTime(), '|', start.getDateTime(), end.getDateTime()); | |
} | |
/** | |
* обновляем значения групп | |
*/ | |
var groups = this.state.groups; | |
this.setState({groups:groups}); | |
// группа переехала левее | |
//var moveToLeft = start.getDateTime().getTime() < json.start.getDateTime().getTime(); | |
// | |
//// левая граница осталась на месте | |
//var stopLeft = start.getDateTime().getTime() === json.start.getDateTime().getTime(); | |
// | |
//// группа переехала правее | |
//var moveToRight = end.getDateTime().getTime() > json.end.getDateTime().getTime(); | |
// | |
//// правая граница осталась на месте | |
//var stopRight = end.getDateTime().getTime() === json.end.getDateTime().getTime(); | |
///** | |
// * Есть 2 точки на линии времени | |
// * возможны 7 видов модицикации | |
// */ | |
//// если группа никуда не перехала - ниче не делаем | |
//if(stopLeft && stopRight){ | |
// return; | |
//} | |
// | |
//// группа была целиком расширена | |
//// нужно изменить группы которые стоят до и после | |
//if(moveToRight && moveToLeft){ | |
// | |
// return; | |
//} | |
// | |
//// группа была целиком сжата | |
//// нужно изменить группы которые стоят до и после | |
//if(!moveToRight && !moveToLeft && !stopRight && !stopLeft){ | |
// | |
// return; | |
//} | |
// | |
//// правая граница группы переехала вперед | |
//// нужно изменить группы которые стоят после | |
//if(stopLeft && moveToRight){ | |
// | |
// return; | |
//} | |
// | |
//// правая граница переехала назад | |
//// нужно изменить группы которые стоят после | |
//if(stopLeft && !moveToRight && !stopRight){ | |
// | |
// return; | |
//} | |
// | |
//// левая граница перехала назад | |
//// нужно изменить группы которые стоят до | |
//if(stopRight && moveToLeft){ | |
// | |
// return; | |
//} | |
// | |
//// левая граница перехала вперед | |
//// нужно изменить группы которые стоят до | |
//if(stopRight && !moveToLeft && !stopLeft){ | |
// | |
// return; | |
//} | |
} | |
/** | |
* открыть группу на редактирование дат | |
* @param id | |
*/ | |
onChangeEditGroupDatesById(id, value){ | |
this.setState({editGroupId:(value)? id : false}); | |
} | |
render() { | |
var groups = this.state.groups; | |
var enableChangeDates = (groups.length > 1); | |
var editGroupId = this.state.editGroupId; | |
var renderedGroups = []; | |
for (var i=0; i<groups.length;i++) { | |
var notFirstItem = (i > 0); | |
var enableEdit = (editGroupId == groups[i].id); | |
var notLastItem = (i < groups.length - 1); | |
var key = i + groups[i].id + groups[i].attributes.start.getDateTime().getTime() + groups[i].attributes.end.getDateTime().getTime(); | |
renderedGroups.push( | |
<SlideTableGroup | |
key={key} | |
data={groups[i]} | |
enableEditDates={enableEdit} | |
editablePeriod={this.getEditPeriodByGroupId.bind(this)} | |
enableChangeDates={enableChangeDates} | |
onChangeEditDates={this.onChangeEditGroupDatesById.bind(this)} | |
updateGroupDatesById={this.updateGroupDatesById.bind(this)} | |
notFirstItem={notFirstItem} | |
notLastItem={notLastItem} | |
joinRightById={this.joinRightById.bind(this)} | |
joinLeftById={this.joinLeftById.bind(this)} | |
smartSplit={this.smartSplit.bind(this)}/> | |
); | |
} | |
var styles = { | |
border:'1px solid #ddd', | |
padding:'15px', | |
'display':'flex', | |
'flexWrap': 'wrap', | |
'justifyContent':'left' | |
}; | |
return (<div style={styles}> | |
<div className="clearfix"> | |
{renderedGroups} | |
</div> | |
</div>); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment