diff --git a/examples/js/default.js b/examples/js/default.js index 80408b73c..8ee0d8c7b 100644 --- a/examples/js/default.js +++ b/examples/js/default.js @@ -12,7 +12,7 @@ var datePicker, selectedCalendar; cal = new Calendar('#calendar', { - defaultView: 'week', + defaultView: 'month', useCreationPopup: useCreationPopup, useDetailPopup: useDetailPopup, calendars: CalendarList, @@ -26,23 +26,6 @@ time: function(schedule) { return getTimeTemplate(schedule, false); } - }, - timezones: [{ - timezoneOffset: 600, - // displayLabel: 'GMT+09:00', - tooltip: 'Seoul' - }, { - timezoneOffset: -720, - // displayLabel: 'GMT-08:00', - tooltip: 'Los Angeles' - }, { - timezoneOffset: -120, - // displayLabel: 'GMT-08:00', - tooltip: 'Los Angeles' - }], - week: { - showTimezoneCollapseButton: true, - timezonesCollapsed: false } }); diff --git a/src/js/controller/viewMixin/week.js b/src/js/controller/viewMixin/week.js index 1a2c93c6b..80589e869 100644 --- a/src/js/controller/viewMixin/week.js +++ b/src/js/controller/viewMixin/week.js @@ -10,6 +10,7 @@ var util = require('tui-code-snippet'); var Collection = require('../../common/collection'); var array = require('../../common/array'); var datetime = require('../../common/datetime'); +var TZDate = require('../../common/timezone').Date; var MILLISECONDS_SCHEDULE_MIN_DURATION = datetime.MILLISECONDS_SCHEDULE_MIN_DURATION; @@ -146,17 +147,20 @@ var Week = { * @param {Date} start - start date. * @param {Date} end - end date. * @param {Collection} time - view model collection. + * @param {number} hourStart - start hour to be shown + * @param {number} hourEnd - end hour to be shown * @returns {object} view model for time part. */ - getViewModelForTimeView: function(start, end, time) { + getViewModelForTimeView: function(start, end, time, hourStart, hourEnd) { var self = this, ymdSplitted = this.splitScheduleByDateRange(start, end, time), result = {}; + var _getViewModel = Week._makeGetViewModelFuncForTimeView(hourStart, hourEnd); + util.forEach(ymdSplitted, function(collection, ymd) { - var viewModels = collection.sort(array.compare.schedule.asc), - collisionGroups, - matrices; + var viewModels = _getViewModel(collection); + var collisionGroups, matrices; collisionGroups = self.Core.getCollisionGroup(viewModels); matrices = self.Core.getMatrices(collection, collisionGroups); @@ -168,6 +172,51 @@ var Week = { return result; }, + /** + * make view model function depending on start and end hour + * if time view option has start or end hour condition + * it add filter + * @param {number} hourStart - start hour to be shown + * @param {number} hourEnd - end hour to be shown + * @returns {function} function + */ + _makeGetViewModelFuncForTimeView: function(hourStart, hourEnd) { + if (hourStart === 0 && hourEnd === 24) { + return function(collection) { + return collection.sort(array.compare.schedule.asc); + }; + } + + return function(collection) { + return collection.find(Week._makeHourRangeFilter(hourStart, hourEnd)) + .sort(array.compare.schedule.asc); + }; + }, + + /** + * make a filter function that is not included range of start, end hour + * @param {number} hStart - hour start + * @param {number} hEnd - hour end + * @returns {function} - filtering function + */ + _makeHourRangeFilter: function(hStart, hEnd) { + return function(schedule) { + var ownHourStart = schedule.model.start; + var ownHourEnd = schedule.model.end; + var yyyy = ownHourStart.getFullYear(); + var mm = ownHourStart.getMonth(); + var dd = ownHourStart.getDate(); + + var hourStart = new TZDate(yyyy, mm, dd).setHours(hStart); + var hourEnd = new TZDate(yyyy, mm, dd).setHours(hEnd); + + return (ownHourStart >= hourStart && ownHourStart < hourEnd) || + (ownHourEnd > hourStart && ownHourEnd <= hourEnd) || + (ownHourStart < hourStart && ownHourEnd > hourStart) || + (ownHourEnd > hourEnd && ownHourStart < hourEnd); + }; + }, + /********** * ALLDAY VIEW **********/ @@ -228,13 +277,16 @@ var Week = { * @param {Date} end end date. * @param {Array.} panels - schedule panels like 'milestone', 'task', 'allday', 'time' * @param {function[]} [andFilters] - optional filters to applying search query + * @param {Object} options - week view options * @returns {object} schedules grouped by dates. */ - findByDateRange: function(start, end, panels, andFilters) { + findByDateRange: function(start, end, panels, andFilters, options) { var ctrlCore = this.Core, ctrlWeek = this.Week, filter = ctrlCore.getScheduleInDateRangeFilter(start, end), scheduleTypes = util.pluck(panels, 'name'), + hourStart = util.pick(options, 'hourStart'), + hourEnd = util.pick(options, 'hourEnd'), modelColl, group; @@ -250,7 +302,7 @@ var Week = { if (panel.type === 'daygrid') { group[name] = ctrlWeek.getViewModelForAlldayView(start, end, group[name]); } else if (panel.type === 'timegrid') { - group[name] = ctrlWeek.getViewModelForTimeView(start, end, group[name]); + group[name] = ctrlWeek.getViewModelForTimeView(start, end, group[name], hourStart, hourEnd); } }); diff --git a/src/js/factory/calendar.js b/src/js/factory/calendar.js index 038c13062..bd92a0650 100644 --- a/src/js/factory/calendar.js +++ b/src/js/factory/calendar.js @@ -78,6 +78,8 @@ var mmin = Math.min; * @property {boolean} [workweek=false] - show only 5 days except for weekend * @property {boolean} [showTimezoneCollapseButton=false] - show a collapse button to close multiple timezones * @property {boolean} [timezonesCollapsed=false] - An initial multiple timezones collapsed state + * @property {number} [hourStart=0] - Can limit of render hour start. + * @property {number} [hourEnd=24] - Can limit of render hour end. */ /** diff --git a/src/js/view/template/week/time.hbs b/src/js/view/template/week/time.hbs index 0398751a3..26b57d0cf 100644 --- a/src/js/view/template/week/time.hbs +++ b/src/js/view/template/week/time.hbs @@ -10,7 +10,15 @@ {{/fi}} ">
{{{time-tmpl model}}}
- {{#unless cropped}}
 
{{/unless}} + {{#unless croppedEnd}}
 
{{/unless}} {{/if ~}} {{/each}} diff --git a/src/js/view/template/week/timeMoveGuide.hbs b/src/js/view/template/week/timeMoveGuide.hbs index 6e4d3bd48..97e64977b 100644 --- a/src/js/view/template/week/timeMoveGuide.hbs +++ b/src/js/view/template/week/timeMoveGuide.hbs @@ -1,5 +1,5 @@
{{{time-tmpl model}}}
- {{#unless cropped}}
 
{{/unless}} + {{#unless croppedEnd}}
 
{{/unless}}
diff --git a/src/js/view/week/time.js b/src/js/view/week/time.js index 9065f0421..8b5350042 100644 --- a/src/js/view/week/time.js +++ b/src/js/view/week/time.js @@ -76,51 +76,85 @@ Time.prototype._parseDateGroup = function(str) { }; /** + * calculate left and width * @param {ScheduleViewModel} viewModel - view model instance to calculate bound. * @param {object} options - options for calculating schedule element's bound. - * @param {Date} options.todayStart - date object represent schedule date's start (00:00:00) - * @param {number} options.baseMS - the number of milliseconds to render schedule blocks. - * @param {number} options.baseHeight - pixel value related with baseMS options. - * @param {number[]} options.baseLeft - left position percents for each columns. - * @param {number} options.baseWidth - the unit of schedule blocks width percent. - * @param {number} options.columnIndex - the number index of schedule blocks. - * it represent rendering index from left sides in view. - * @returns {object} bound object for supplied view model. + * @returns {object} - left and width */ -Time.prototype.getScheduleViewBound = function(viewModel, options) { - var baseMS = options.baseMS; - var baseHeight = options.baseHeight; - var cropped = false; - var offsetStart, width, height, top; - var isReadOnly = util.pick(viewModel, 'model', 'isReadOnly') || false; - - offsetStart = viewModel.valueOf().start - options.todayStart; - - // containerHeight : milliseconds in day = x : schedule's milliseconds - top = (baseHeight * offsetStart) / baseMS; - height = (baseHeight * viewModel.duration()) / baseMS; - width = options.baseWidth * (viewModel.extraSpace + 1); +Time.prototype._getScheduleViewBoundX = function(viewModel, options) { + var width = options.baseWidth * (viewModel.extraSpace + 1); // set width auto when has no collisions. if (!viewModel.hasCollide) { width = null; } - if (height + top > baseHeight) { - height = baseHeight - top; - cropped = true; + return { + left: options.baseLeft[options.columnIndex], + width: width + }; +}; + +/** + * calculate top, height, croppedStart and croppedEnd + * @param {ScheduleViewModel} viewModel - view model instance to calculate bound. + * @param {object} options - options for calculating schedule element's bound. + * @returns {object} - left and width + */ +Time.prototype._getScheduleViewBoundY = function(viewModel, options) { + var baseMS = options.baseMS; + var baseHeight = options.baseHeight; + var croppedStart = false; + var croppedEnd = false; + var offsetStart = viewModel.valueOf().start - options.todayStart; + // containerHeight : milliseconds in day = x : schedule's milliseconds + var top = (baseHeight * offsetStart) / baseMS; + var height = (baseHeight * viewModel.duration()) / baseMS; + + if (offsetStart < 0) { + top = 0; + height += ((baseHeight * offsetStart) / baseMS); + croppedStart = true; } - if (isReadOnly || options.isReadOnly) { - cropped = true; + if (height + top > baseHeight) { + height = baseHeight - top; + croppedEnd = true; } return { top: top, - left: options.baseLeft[options.columnIndex], - width: width, height: Math.max(height, this.options.minHeight) - this.options.defaultMarginBottom, - cropped: cropped + croppedStart: croppedStart, + croppedEnd: croppedEnd + }; +}; + +/** + * @param {ScheduleViewModel} viewModel - view model instance to calculate bound. + * @param {object} options - options for calculating schedule element's bound. + * @param {Date} options.todayStart - date object represent schedule date's start (00:00:00) + * @param {number} options.baseMS - the number of milliseconds to render schedule blocks. + * @param {number} options.baseHeight - pixel value related with baseMS options. + * @param {number[]} options.baseLeft - left position percents for each columns. + * @param {number} options.baseWidth - the unit of schedule blocks width percent. + * @param {number} options.columnIndex - the number index of schedule blocks. + * it represent rendering index from left sides in view. + * @returns {object} bound object for supplied view model. + */ +Time.prototype.getScheduleViewBound = function(viewModel, options) { + var boundX = this._getScheduleViewBoundX(viewModel, options); + var boundY = this._getScheduleViewBoundY(viewModel, options); + var isReadOnly = util.pick(viewModel, 'model', 'isReadOnly') || false; + + return { + top: boundY.top, + left: boundX.left, + width: boundX.width, + height: boundY.height, + croppedEnd: boundY.croppedEnd, + croppedStart: boundY.croppedStart, + isReadOnly: isReadOnly }; }; diff --git a/src/js/view/week/week.js b/src/js/view/week/week.js index 49eef224a..7f900de3d 100644 --- a/src/js/view/week/week.js +++ b/src/js/view/week/week.js @@ -53,16 +53,18 @@ function Week(controller, options, container, panels) { * @type {object} Options for view. */ this.options = util.extend({ - scheduleFilter: function(schedule) { + scheduleFilter: [function(schedule) { return Boolean(schedule.isVisible); - }, + }], renderStartDate: datetime.format(range.start, 'YYYY-MM-DD'), renderEndDate: datetime.format(range.end, 'YYYY-MM-DD'), narrowWeekend: false, startDayOfWeek: 0, workweek: false, showTimezoneCollapseButton: false, - timezonesCollapsed: false + timezonesCollapsed: false, + hourStart: 0, + hourEnd: 24 }, options); /** @@ -130,7 +132,8 @@ Week.prototype.render = function() { datetime.start(renderStartDate), datetime.end(renderEndDate), this.panels, - scheduleFilter + scheduleFilter, + this.options ); grids = datetime.getGridLeftAndWidth( diff --git a/test/app/controller/viewMixin/week.spec.js b/test/app/controller/viewMixin/week.spec.js index b0cc2748c..a12bfd7e9 100644 --- a/test/app/controller/viewMixin/week.spec.js +++ b/test/app/controller/viewMixin/week.spec.js @@ -6,6 +6,7 @@ var ControllerFactory = require('factory/controller'); var Schedule = require('model/schedule'); var ScheduleViewModel = require('model/viewModel/scheduleViewModel'); var datetime = require('common/datetime'); +var TZDate = require('common/timezone').Date; describe('Base.Week', function() { var stamp = util.stamp; @@ -137,7 +138,7 @@ describe('Base.Week', function() { var start = new Date('2015/04/30'), end = new Date('2015/05/02'); - var result = ctrl.findByDateRange(start, end, panels); + var result = ctrl.findByDateRange(start, end, panels, [], {hourStart: 0, hourEnd: 24}); // There are 5 collision blocks on 5/1. expect(result.time['20150501'].length).toBe(5); @@ -148,12 +149,69 @@ describe('Base.Week', function() { end = new Date('2015/05/02'); // Since there is only one event with title J - var result = ctrl.findByDateRange(start, end, panels, function(model) {return model.title === 'J';}); + var result = ctrl.findByDateRange(start, end, panels, function(model) {return model.title === 'J';}, {hourStart: 0, hourEnd: 24}); // One collision block in the timeline group expect(result.time['20150501'].length).toBe(1); }); }); + describe('_getHourRangeFilter()', function() { + var hourRangeFilter; + var schedule = {}; + + beforeEach(function() { + // 8:00 ~ 20:00 + hourRangeFilter = ctrl._makeHourRangeFilter(10, 12); + schedule.model = {}; + }); + + it('filter schedule by start, end date visible', function() { + schedule.model.start = new TZDate('2018-05-02T09:30:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T13:30:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); + + schedule.model.start = new TZDate('2018-05-02T00:00:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T10:30:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); + + schedule.model.start = new TZDate('2018-05-02T10:30:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T11:30:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); + + schedule.model.start = new TZDate('2018-05-02T11:30:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T15:00:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); + + schedule.model.start = new TZDate('2018-05-02T00:00:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T10:00:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(false); + + schedule.model.start = new TZDate('2018-05-02T10:00:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T12:00:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); + + schedule.model.start = new TZDate('2018-05-02T12:00:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T15:00:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(false); + + schedule.model.start = new TZDate('2018-05-02T09:00:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T15:00:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); + + schedule.model.start = new TZDate('2018-05-02T09:00:00+09:00'); + schedule.model.end = new TZDate('2018-05-02T15:00:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); + + schedule.model.start = new TZDate('2018-05-02T09:00:00+09:00'); + schedule.model.end = new TZDate('2018-05-03T09:00:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); // true, false?? + + schedule.model.start = new TZDate('2018-05-02T11:00:00+15:00'); + schedule.model.end = new TZDate('2018-05-03T09:00:00+09:00'); + expect(hourRangeFilter(schedule)).toBe(true); + }); + }); + }); diff --git a/test/app/view/time.spec.js b/test/app/view/time.spec.js index 42f50b1f1..44f56a98a 100644 --- a/test/app/view/time.spec.js +++ b/test/app/view/time.spec.js @@ -14,7 +14,7 @@ describe('View/Time', function() { expect(actual).toEqual(expected); }); - it('getScheduleViewBound()', function() { + it('_getScheduleViewBoundX()', function() { var schedule = Schedule.create({ start: '2015-05-01T09:00:00+09:00', end: '2015-05-01T10:00:00+09:00' @@ -27,7 +27,7 @@ describe('View/Time', function() { }; var viewModel = ScheduleViewModel.create(schedule); - var result = Time.prototype.getScheduleViewBound.call(mock, viewModel, { + var result = Time.prototype._getScheduleViewBoundX.call(mock, viewModel, { todayStart: new Date('2015-05-01T00:00:00+09:00'), baseMS: datetime.millisecondsFrom('hour', 24), baseHeight: 230, @@ -36,9 +36,33 @@ describe('View/Time', function() { columnIndex: 1 }); - expect(result.top).toBeCloseTo(86, 0); expect(result.left).toBe(50); expect(result.width).toBe(null); + }); + + it('_getScheduleViewBoundY()', function() { + var schedule = Schedule.create({ + start: '2015-05-01T09:00:00+09:00', + end: '2015-05-01T10:00:00+09:00' + }); + var mock = { + options: { + minHeight: 0, + defaultMarginBottom: 0 + } + }; + var viewModel = ScheduleViewModel.create(schedule); + + var result = Time.prototype._getScheduleViewBoundY.call(mock, viewModel, { + todayStart: new Date('2015-05-01T00:00:00+09:00'), + baseMS: datetime.millisecondsFrom('hour', 24), + baseHeight: 230, + baseLeft: [0, 50], + baseWidth: 50, + columnIndex: 1 + }); + + expect(result.top).toBeCloseTo(86, 0); expect(result.height).toBeCloseTo(10, 0); }); });