svelte-gantt-event-custom
Version:
Interactive JavaScript Gantt chart/resource booking component
32 lines • 77.3 kB
Source Map (JSON)
{
"version": 3,
"file": "svelteGantt.css",
"sources": [
"..\\..\\Gantt.svelte",
"..\\..\\Column.svelte",
"..\\..\\ColumnHeaderRow.svelte",
"..\\..\\Columns.svelte",
"..\\..\\Milestone.svelte",
"..\\..\\Row.svelte",
"..\\..\\Task.svelte",
"..\\..\\TimeRange.svelte",
"..\\..\\TimeRangeHeader.svelte",
"..\\..\\Arrow.svelte",
"..\\..\\Dependency.svelte",
"..\\..\\GanttDependencies.svelte",
"..\\..\\Table.svelte",
"..\\..\\TableRow.svelte",
"..\\..\\TableTreeCell.svelte",
"..\\..\\ContextMenu.svelte",
"..\\..\\Resizer.svelte"
],
"sourcesContent": [
"<script>\r\n import { onMount, setContext, tick, onDestroy } from 'svelte';\r\n import { writable, derived } from 'svelte/store';\r\n import moment from 'moment';\r\n\r\n let ganttElement;\r\n let mainHeaderContainer;\r\n let mainContainer;\r\n let rowContainer;\r\n let scrollables = [];\r\n let mounted = false;\r\n\r\n import { rowStore, taskStore, timeRangeStore, allTasks, allRows, allTimeRanges, rowTaskCache } from './core/store';\r\n import { Task, Row, TimeRange, TimeRangeHeader, Milestone } from './entities';\r\n import { Columns, ColumnHeader } from './column';\r\n import { Resizer } from \"./ui\";\r\n\r\n import { GanttUtils, getPositionByDate } from \"./utils/utils\";\r\n import { getRelativePos, debounce, throttle } from \"./utils/domUtils\";\r\n import { SelectionManager } from \"./utils/selectionManager\";\r\n import { GanttApi } from \"./core/api\";\r\n import { TaskFactory, reflectTask } from \"./core/task\";\r\n import { RowFactory } from \"./core/row\";\r\n import { TimeRangeFactory } from \"./core/timeRange\";\r\n import { DragDropManager } from \"./core/drag\";\r\n import { findByPosition, findByDate } from './core/column';\r\n import { onEvent, onDelegatedEvent, offDelegatedEvent } from './core/events';\r\n\r\n export let rows;\r\n export let tasks = [];\r\n export let timeRanges = [];\r\n $: if(mounted) initRows(rows);\r\n $: if(mounted) initTasks(tasks);\r\n $: if(mounted) initTimeRanges(timeRanges);\r\n\r\n export let rowPadding = 6;\r\n export let rowHeight = 52;\r\n const _rowHeight = writable(rowHeight);\r\n const _rowPadding = writable(rowPadding);\r\n\r\n export let from;\r\n export let to;\r\n const _from = writable(from);\r\n const _to = writable(to);\r\n $: $_from = from;\r\n $: $_to = to;\r\n\r\n export let minWidth = 800;\r\n export let fitWidth = false;\r\n const _minWidth = writable(minWidth);\r\n const _fitWidth = writable(fitWidth);\r\n $: {\r\n $_minWidth = minWidth;\r\n $_fitWidth = fitWidth;\r\n } \r\n\r\n export let classes = [];\r\n export let headers = [{unit: 'day', format: 'MMMM Do'}, {unit: 'hour', format: 'H:mm'}];\r\n export let zoomLevels = [\r\n\t\t{\r\n\t\t\theaders: [\r\n\t\t\t\t{ unit: 'day', format: 'DD.MM.YYYY' },\r\n\t\t\t\t{ unit: 'hour', format: 'HH' }\r\n\t\t\t],\r\n\t\t\tminWidth: 800,\r\n\t\t\tfitWidth: true\r\n\t\t},\r\n\t\t{\r\n\t\t\theaders: [\r\n\t\t\t\t{ unit: 'hour', format: 'ddd D/M, H A' },\r\n\t\t\t\t{ unit: 'minute', format: 'mm', offset: 15 }\r\n\t\t\t],\r\n\t\t\tminWidth: 5000,\r\n\t\t\tfitWidth: false\r\n\t\t}\r\n\t];\r\n export let taskContent = null;\r\n export let tableWidth = 100;\r\n export let resizeHandleWidth = 10;\r\n export let onTaskButtonClick = null;\r\n\r\n export let magnetUnit = 'minute';\r\n export let magnetOffset = 15;\r\n export let columnUnit = 'minute';\r\n export let columnOffset = 15;\r\n\r\n // export until Svelte3 implements Svelte2's setup(component) hook\r\n export let ganttTableModules = [];\r\n export let ganttBodyModules = [];\r\n\r\n export let reflectOnParentRows = true;\r\n export let reflectOnChildRows = false;\r\n\r\n const visibleWidth = writable();\r\n const visibleHeight = writable();\r\n const headerHeight = writable();\r\n const _width = derived([visibleWidth, _minWidth, _fitWidth], ([visible, min, stretch]) => {\r\n return stretch && visible > min ? visible : min;\r\n });\r\n \r\n export const columnService = {\r\n getColumnByDate(date) {\r\n const pair = findByDate(columns, date);\r\n return !pair[0] ? pair[1] : pair[0];\r\n },\r\n getColumnByPosition(x) {\r\n const pair = findByPosition(columns, x);\r\n return !pair[0] ? pair[1] : pair[0];\r\n },\r\n getPositionByDate (date) {\r\n if(!date) return null;\r\n const column = this.getColumnByDate(date);\r\n \r\n let durationTo = date.diff(column.from, 'milliseconds');\r\n const position = durationTo / column.duration * column.width;\r\n\r\n //multiples - skip every nth col, use other duration\r\n return column.left + position;\r\n },\r\n getDateByPosition (x) {\r\n const column = this.getColumnByPosition(x);\r\n x = x - column.left;\r\n\r\n let positionDuration = column.duration / column.width * x;\r\n const date = moment(column.from).add(positionDuration, 'milliseconds');\r\n\r\n return date;\r\n },\r\n /**\r\n * \r\n * @param {Moment} date - Date\r\n * @returns {Moment} rounded date passed as parameter\r\n */\r\n roundTo(date) {\r\n let value = date.get(magnetUnit);\r\n value = Math.round(value / magnetOffset);\r\n date.set(magnetUnit, value * magnetOffset);\r\n\r\n //round all smaller units to 0\r\n const units = ['millisecond', 'second', 'minute', 'hour', 'date', 'month', 'year'];\r\n const indexOf = units.indexOf(magnetUnit);\r\n for (let i = 0; i < indexOf; i++) {\r\n date.set(units[i], 0)\r\n }\r\n return date\r\n }\r\n }\r\n\r\n const columnWidth = writable(getPositionByDate($_from.clone().add(columnOffset, columnUnit), $_from, $_to, $_width) | 0);\r\n $: $columnWidth = getPositionByDate($_from.clone().add(columnOffset, columnUnit), $_from, $_to, $_width) | 0;\r\n let columnCount = Math.ceil($_width / $columnWidth);\r\n $: columnCount = Math.ceil($_width / $columnWidth);\r\n let columns = getColumns($_from, columnCount, columnOffset, columnUnit, $columnWidth);\r\n $: columns = getColumns($_from, columnCount, columnOffset, columnUnit, $columnWidth);\r\n\r\n function getColumns(from, count, offset, unit, width) {\r\n let columns = [];\r\n let columnFrom = from.clone();\r\n let left = 0;\r\n for (let i = 0; i < count; i++) {\r\n const from = columnFrom.clone();\r\n const to = columnFrom.add(offset, unit);\r\n const duration = to.diff(from, 'milliseconds');\r\n\r\n columns.push({\r\n width: width,\r\n from,\r\n left,\r\n duration\r\n });\r\n left += width;\r\n columnFrom = to;\r\n }\r\n \r\n return columns;\r\n }\r\n\r\n const dimensionsChanged = derived([columnWidth, _from, _to], () => ({}));\r\n $: {\r\n if($dimensionsChanged) {\r\n refreshTasks();\r\n refreshTimeRanges();\r\n }\r\n }\r\n\r\n setContext('dimensions', {\r\n from: _from,\r\n to: _to,\r\n width: _width,\r\n visibleWidth,\r\n visibleHeight,\r\n headerHeight,\r\n dimensionsChanged\r\n });\r\n\r\n setContext('options', {\r\n taskContent,\r\n rowPadding: _rowPadding,\r\n rowHeight: _rowHeight,\r\n resizeHandleWidth: resizeHandleWidth,\r\n reflectOnParentRows,\r\n reflectOnChildRows,\r\n onTaskButtonClick\r\n });\r\n\r\n const hoveredRow = writable();\r\n const selectedRow = writable();\r\n\r\n const ganttContext = { \r\n scrollables, \r\n hoveredRow, \r\n selectedRow \r\n };\r\n setContext('gantt', ganttContext);\r\n\r\n onMount(() => {\r\n Object.assign(ganttContext, {\r\n rowContainer,\r\n mainContainer,\r\n mainHeaderContainer\r\n });\r\n\r\n api.registerEvent('tasks', 'move');\r\n api.registerEvent('tasks', 'select');\r\n api.registerEvent('tasks', 'switchRow');\r\n api.registerEvent('tasks', 'moveEnd');\r\n api.registerEvent('tasks', 'change');\r\n api.registerEvent('tasks', 'changed');\r\n api.registerEvent('gantt', 'viewChanged');\r\n\r\n mounted = true;\r\n });\r\n\r\n onDelegatedEvent('click', 'data-task-id', (event, data, target) => {\r\n const taskId = +data;\r\n if (event.ctrlKey) {\r\n selectionManager.toggleSelection(taskId);\r\n } else {\r\n selectionManager.selectSingle(taskId);\r\n }\r\n api.tasks.raise.select($taskStore.entities[taskId]);\r\n });\r\n\r\n onDelegatedEvent('mouseover', 'data-row-id', (event, data, target) => {\r\n $hoveredRow = +data;\r\n });\r\n\r\n onDelegatedEvent('click', 'data-row-id', (event, data, target) => {\r\n $selectedRow = +data;\r\n });\r\n \r\n onDestroy(() => {\r\n offDelegatedEvent('click', 'data-task-id');\r\n offDelegatedEvent('click', 'data-row-id');\r\n });\r\n\r\n let __scrollTop = 0;\r\n let __scrollLeft = 0;\r\n function scrollable(node) {\r\n const onscroll = event => {\r\n const { scrollTop, scrollLeft } = node;\r\n\r\n scrollables.forEach(scrollable => {\r\n if (scrollable.orientation === \"horizontal\") {\r\n scrollable.node.scrollLeft = scrollLeft;\r\n } else {\r\n scrollable.node.scrollTop = scrollTop;\r\n }\r\n });\r\n\r\n __scrollTop = scrollTop;\r\n __scrollLeft = scrollLeft;\r\n };\r\n\r\n node.addEventListener(\"scroll\", onscroll);\r\n return {\r\n destroy() {\r\n node.removeEventListener(\"scroll\", onscroll, false);\r\n }\r\n };\r\n }\r\n\r\n function horizontalScrollListener(node) {\r\n scrollables.push({ node, orientation: \"horizontal\" });\r\n }\r\n\r\n function onResize(event) {\r\n tableWidth = event.detail.left;\r\n }\r\n\r\n let zoomLevel = 0;\r\n let zooming = false;\r\n async function onwheel(event) {\r\n if (event.ctrlKey) {\r\n event.preventDefault();\r\n\r\n const prevZoomLevel = zoomLevel;\r\n if (event.deltaY > 0) {\r\n zoomLevel = Math.max(zoomLevel - 1, 0);\r\n } else {\r\n zoomLevel = Math.min(zoomLevel + 1, zoomLevels.length - 1);\r\n }\r\n\r\n if (prevZoomLevel != zoomLevel && zoomLevels[zoomLevel]) {\r\n const options = {\r\n columnUnit: columnUnit,\r\n columnOffset: columnOffset,\r\n minWidth: $_minWidth,\r\n ...zoomLevels[zoomLevel]\r\n };\r\n\r\n const scale = options.minWidth / $_width;\r\n const node = mainContainer;\r\n const mousepos = getRelativePos(node, event);\r\n const before = node.scrollLeft + mousepos.x;\r\n const after = before * scale;\r\n const scrollLeft = after - mousepos.x + node.clientWidth / 2;\r\n\r\n console.log('scrollLeft', scrollLeft);\r\n\r\n columnUnit = options.columnUnit;\r\n columnOffset = options.columnOffset;\r\n $_minWidth = options.minWidth;\r\n\r\n if(options.headers)\r\n headers = options.headers;\r\n\r\n if(options.fitWidth)\r\n $_fitWidth = options.fitWidth;\r\n\r\n api.gantt.raise.viewChanged();\r\n zooming = true;\r\n await tick();\r\n node.scrollLeft = scrollLeft;\r\n zooming = false;\r\n }\r\n }\r\n }\r\n\r\n function onDateSelected(event) {\r\n $_from = event.detail.from.clone();\r\n $_to = event.detail.to.clone();\r\n }\r\n\r\n function initRows(rowsData) {\r\n const rows = rowFactory.createRows(rowsData);\r\n rowStore.addAll(rows);\r\n }\r\n\r\n async function initTasks(taskData) {\r\n await tick();\r\n\r\n const tasks = [];\r\n const opts = { rowPadding: $_rowPadding };\r\n taskData.forEach(t => {\r\n const task = taskFactory.createTask(t);\r\n const row = $rowStore.entities[task.model.resourceId];\r\n task.reflections = [];\r\n\r\n if(reflectOnChildRows && row.allChildren) {\r\n row.allChildren.forEach(r => {\r\n const reflectedTask = reflectTask(task, r, opts);\r\n task.reflections.push(reflectedTask.model.id);\r\n tasks.push(reflectedTask);\r\n });\r\n }\r\n\r\n if(reflectOnParentRows && row.allParents.length > 0) {\r\n row.allParents.forEach(r => {\r\n const reflectedTask = reflectTask(task, r, opts);\r\n task.reflections.push(reflectedTask.model.id);\r\n tasks.push(reflectedTask);\r\n });\r\n }\r\n\r\n tasks.push(task);\r\n });\r\n taskStore.addAll(tasks);\r\n }\r\n\r\n function initTimeRanges(timeRangeData) {\r\n const timeRanges = timeRangeData.map(timeRange => {\r\n return timeRangeFactory.create(timeRange);\r\n });\r\n timeRangeStore.addAll(timeRanges);\r\n }\r\n \r\n function onModuleInit(module) {\r\n \r\n }\r\n\r\n async function tickWithoutCSSTransition() {\r\n disableTransition = false;\r\n await tick();\r\n ganttElement.offsetHeight; // force a reflow\r\n disableTransition = true;\r\n }\r\n\r\n export const api = new GanttApi();\r\n const selectionManager = new SelectionManager();\r\n\r\n export const taskFactory = new TaskFactory(columnService);\r\n $: {\r\n taskFactory.rowPadding = $_rowPadding;\r\n taskFactory.rowEntities = $rowStore.entities;\r\n }\r\n\r\n export const rowFactory = new RowFactory();\r\n $: rowFactory.rowHeight = rowHeight;\r\n\r\n export const dndManager = new DragDropManager(rowStore);\r\n export const timeRangeFactory = new TimeRangeFactory(columnService);\r\n\r\n export const utils = new GanttUtils();\r\n $: {\r\n utils.from = $_from;\r\n utils.to = $_to;\r\n utils.width = $_width;\r\n utils.magnetOffset = magnetOffset;\r\n utils.magnetUnit = magnetUnit;\r\n }\r\n\r\n setContext('services', {\r\n utils,\r\n api,\r\n dndManager,\r\n selectionManager,\r\n columnService\r\n });\r\n\r\n export function refreshTimeRanges() {\r\n timeRangeStore._update(({ids, entities}) => {\r\n ids.forEach(id => {\r\n const timeRange = entities[id];\r\n const newLeft = columnService.getPositionByDate(timeRange.model.from) | 0;\r\n const newRight = columnService.getPositionByDate(timeRange.model.to) | 0;\r\n\r\n timeRange.left = newLeft;\r\n timeRange.width = newRight - newLeft;\r\n });\r\n return { ids, entities };\r\n });\r\n }\r\n\r\n export function refreshTasks() {\r\n $allTasks.forEach(task => {\r\n const newLeft = columnService.getPositionByDate(task.model.from) | 0;\r\n const newRight = columnService.getPositionByDate(task.model.to) | 0;\r\n\r\n task.left = newLeft;\r\n task.width = newRight - newLeft;\r\n });\r\n\r\n taskStore.refresh();\r\n }\r\n\r\n export function getRowContainer() {\r\n return rowContainer;\r\n }\r\n\r\n export function selectTask(id) {\r\n const task = $taskStore.entities[id];\r\n if (task) {\r\n selectionManager.selectSingle(task);\r\n }\r\n }\r\n\r\n export function unselectTasks() {\r\n selectionManager.clearSelection();\r\n }\r\n\r\n export function scrollToRow(id, scrollBehavior = 'auto') {\r\n const { scrollTop, clientHeight } = mainContainer;\r\n \r\n const index = $allRows.findIndex(r => r.model.id == id);\r\n if(index === -1) return;\r\n const targetTop = index * rowHeight;\r\n\r\n if(targetTop < scrollTop) {\r\n mainContainer.scrollTo({\r\n top: targetTop,\r\n behavior: scrollBehavior\r\n });\r\n }\r\n\r\n if(targetTop > scrollTop + clientHeight) {\r\n mainContainer.scrollTo({\r\n top: targetTop + rowHeight - clientHeight,\r\n behavior: scrollBehavior\r\n });\r\n }\r\n }\r\n\r\n\texport function scrollToTask(id, scrollBehavior = 'auto') {\r\n const { scrollLeft, scrollTop, clientWidth, clientHeight } = mainContainer;\r\n \r\n const task = $taskStore.entities[id];\r\n if(!task) return;\r\n const targetLeft = task.left;\r\n const rowIndex = $allRows.findIndex(r => r.model.id == task.model.resourceId);\r\n const targetTop = rowIndex * rowHeight;\r\n \r\n const options = {\r\n top: undefined,\r\n left: undefined,\r\n behavior: scrollBehavior\r\n };\r\n\r\n if(targetLeft < scrollLeft) {\r\n options.left = targetLeft;\r\n }\r\n\r\n if(targetLeft > scrollLeft + clientWidth) {\r\n options.left = targetLeft + task.width - clientWidth;\r\n }\r\n\r\n if(targetTop < scrollTop) {\r\n options.top = targetTop;\r\n }\r\n\r\n if(targetTop > scrollTop + clientHeight) {\r\n options.top = targetTop + rowHeight - clientHeight;\r\n }\r\n \r\n mainContainer.scrollTo(options);\r\n }\r\n\r\n export function updateTask(model) {\r\n const task = taskFactory.createTask(model);\r\n taskStore.upsert(task);\r\n }\r\n\r\n export function updateTasks(taskModels) {\r\n const tasks = taskModels.map(model => taskFactory.createTask(model));\r\n taskStore.upsertAll(tasks);\r\n }\r\n\r\n export function updateRow(model) {\r\n const row = rowFactory.createRow(model);\r\n rowStore.upsert(row);\r\n }\r\n\r\n export function updateRows(rowModels) {\r\n const rows = rowModels.map(model => rowFactory.createRow(model));\r\n rowStore.upsertAll(rows);\r\n }\r\n\r\n export function getRow(resourceId) {\r\n return $rowStore.entities[resourceId];\r\n }\r\n\r\n export function getTask(id) {\r\n return $taskStore.entities[id];\r\n }\r\n\r\n export function getTasks(resourceId) {\r\n if ($rowTaskCache[resourceId]) {\r\n return $rowTaskCache[resourceId].map(id => $taskStore.entities[id]);\r\n }\r\n return null;\r\n }\r\n\r\n let filteredRows = [];\r\n $: filteredRows = $allRows.filter(row => !row.hidden);\r\n\r\n let rightScrollbarVisible;\r\n $: rightScrollbarVisible = rowContainerHeight > $visibleHeight;\r\n\r\n let rowContainerHeight;\r\n $: rowContainerHeight = filteredRows.length * rowHeight;\r\n\r\n let startIndex;\r\n $: startIndex = Math.floor(__scrollTop / rowHeight);\r\n\r\n let endIndex;\r\n $: endIndex = Math.min(startIndex + Math.ceil($visibleHeight / rowHeight), filteredRows.length - 1);\r\n\r\n let paddingTop = 0;\r\n $: paddingTop = startIndex * rowHeight;\r\n\r\n let paddingBottom = 0;\r\n $: paddingBottom = (filteredRows.length - endIndex - 1) * rowHeight;\r\n\r\n let visibleRows = [];\r\n $: visibleRows = filteredRows.slice(startIndex, endIndex + 1);\r\n\r\n let visibleTasks;\r\n $: {\r\n const tasks = [];\r\n visibleRows.forEach(row => {\r\n if ($rowTaskCache[row.model.id]) {\r\n $rowTaskCache[row.model.id].forEach(id => {\r\n tasks.push($taskStore.entities[id]);\r\n });\r\n }\r\n });\r\n visibleTasks = tasks;\r\n }\r\n\r\n let disableTransition = true;\r\n $: if($dimensionsChanged) tickWithoutCSSTransition();\r\n</script>\r\n\r\n<div class=\"sg-gantt {classes}\" class:sg-disable-transition={!disableTransition} bind:this={ganttElement} on:click={onEvent} on:mouseover={onEvent}>\r\n {#each ganttTableModules as module}\r\n <svelte:component this={module} {rowContainerHeight} {paddingTop} {paddingBottom} tableWidth={tableWidth} {...$$restProps} on:init=\"{onModuleInit}\" {visibleRows} />\r\n\r\n <Resizer x={tableWidth} on:resize=\"{onResize}\" container={ganttElement}></Resizer>\r\n {/each}\r\n\r\n <div class=\"sg-timeline sg-view\">\r\n <div class=\"sg-header\" bind:this={mainHeaderContainer} bind:clientHeight=\"{$headerHeight}\" class:right-scrollbar-visible=\"{rightScrollbarVisible}\">\r\n <div class=\"sg-header-scroller\" use:horizontalScrollListener>\r\n <div class=\"header-container\" style=\"width:{$_width}px\">\r\n <ColumnHeader {headers} {columnUnit} {columnOffset} on:dateSelected=\"{onDateSelected}\" />\r\n {#each $allTimeRanges as timeRange (timeRange.id)}\r\n <TimeRangeHeader {...timeRange} />\r\n {/each}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"sg-timeline-body\" bind:this={mainContainer} use:scrollable class:zooming=\"{zooming}\" on:wheel=\"{onwheel}\"\r\n bind:clientHeight=\"{$visibleHeight}\" bind:clientWidth=\"{$visibleWidth}\">\r\n <div class=\"content\" style=\"width:{$_width}px\">\r\n <Columns columns={columns}/>\r\n <div class=\"sg-rows\" bind:this={rowContainer} style=\"height:{rowContainerHeight}px;\">\r\n <div style=\"transform: translateY({paddingTop}px);\">\r\n {#each visibleRows as row (row.model.id)}\r\n <Row row={row} />\r\n {/each}\r\n </div>\r\n </div>\r\n <div class=\"sg-foreground\">\r\n {#each $allTimeRanges as timeRange (timeRange.id)}\r\n <TimeRange {...timeRange} />\r\n {/each}\r\n\r\n {#each visibleTasks as task (task.model.id)}\r\n <Task model={task.model} left={task.left}\r\n width={task.width} height={task.height} top={task.top} {...task} />\r\n {/each}\r\n </div>\r\n {#each ganttBodyModules as module}\r\n <svelte:component this={module} {paddingTop} {paddingBottom} {visibleRows} {...$$restProps} on:init=\"{onModuleInit}\" />\r\n {/each}\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<style>\r\n .sg-disable-transition :global(.sg-task),\r\n .sg-disable-transition :global(.sg-milestone) {\r\n transition: transform 0s, background-color 0.2s, width 0s !important;\r\n }\r\n\r\n :global(.sg-view:not(:first-child)) {\r\n margin-left: 5px;\r\n }\r\n \r\n /* This class should take into account varying widths of the scroll bar */\r\n .right-scrollbar-visible {\r\n padding-right: 17px;\r\n }\r\n\r\n .sg-timeline {\r\n flex: 1 1 0%;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-x: auto;\r\n }\r\n\r\n .sg-gantt {\r\n display: flex;\r\n\r\n width: 100%;\r\n height: 100%;\r\n position: relative;\r\n }\r\n\r\n .sg-foreground {\r\n box-sizing: border-box;\r\n overflow: hidden;\r\n top: 0;\r\n left: 0;\r\n position: absolute;\r\n width: 100%;\r\n height: 100%;\r\n z-index: 1;\r\n pointer-events: none;\r\n }\r\n\r\n .sg-rows {\r\n width: 100%;\r\n box-sizing: border-box;\r\n overflow: hidden;\r\n }\r\n\r\n .sg-timeline-body {\r\n overflow: auto;\r\n flex: 1 1 auto;\r\n }\r\n\r\n .sg-header {\r\n \r\n }\r\n\r\n .header-container {\r\n }\r\n\r\n .sg-header-scroller {\r\n border-right: 1px solid #efefef;\r\n overflow: hidden;\r\n position: relative;\r\n }\r\n\r\n .content {\r\n position: relative;\r\n }\r\n\r\n :global(*) {\r\n box-sizing: border-box;\r\n }\r\n</style>",
"<script>\r\n export let left;\r\n export let width;\r\n</script>\r\n\r\n<div class=\"column\" style=\"left:{left}px;width:{width}px\"></div>\r\n<style>\r\n .column {\r\n position: absolute;\r\n height: 100%;\r\n box-sizing: border-box;\r\n }\r\n</style>",
"<script>\r\n import { createEventDispatcher, getContext } from 'svelte';\r\n\r\n const dispatch = createEventDispatcher();\r\n\r\n import { duration as momentDuration } from 'moment';\r\n\r\n const { from, to, width } = getContext('dimensions');\r\n \r\n export let header;\r\n export let baseWidth;\r\n export let baseDuration;\r\n\r\n export let columnWidth;\r\n $: {\r\n const offset = header.offset || 1;\r\n const duration = momentDuration(offset, header.unit).asMilliseconds();\r\n const ratio = duration / baseDuration;\r\n columnWidth = baseWidth * ratio;\r\n }\r\n\r\n export let columnCount;\r\n $: {\r\n columnCount = Math.ceil($width / columnWidth);\r\n if(!isFinite(columnCount)){\r\n console.error('columnCount is not finite');\r\n columnCount = 0;\r\n }\r\n }\r\n\r\n let _headers = [];\r\n $: {\r\n const headers = [];\r\n let headerTime = $from.clone().startOf(header.unit);\r\n const offset = header.offset || 1;\r\n\r\n for(let i = 0; i < columnCount; i++){\r\n headers.push({\r\n width: Math.min(columnWidth, $width), \r\n label: headerTime.format(header.format),\r\n from: headerTime.clone(),\r\n to: headerTime.clone().add(offset, header.unit),\r\n unit: header.unit\r\n });\r\n headerTime.add(offset, header.unit);\r\n }\r\n _headers = headers;\r\n }\r\n</script>\r\n\r\n<div class=\"column-header-row\">\r\n {#each _headers as _header}\r\n <div class=\"column-header-cell\" class:sticky={header.sticky} style=\"width:{_header.width}px\" on:click=\"{() => dispatch('dateSelected', { from: _header.from, to: _header.to, unit: _header.unit })}\">\r\n <div class=\"column-header-cell-label\">{_header.label || 'N/A'}</div>\r\n </div>\r\n {/each}\r\n</div>\r\n<style>\r\n .column-header-row {\r\n box-sizing: border-box;\r\n white-space: nowrap;\r\n height: 32px;\r\n }\r\n\r\n .column-header-cell {\r\n display: inline-block;\r\n height: 100%;\r\n box-sizing: border-box;\r\n text-overflow: clip;\r\n /* vertical-align: top; */\r\n text-align: center;\r\n\r\n display: inline-flex;\r\n justify-content: center;\r\n align-items: center;\r\n font-size: 1em; \r\n font-size: 14px;\r\n font-weight: 300;\r\n transition: background 0.2s;\r\n\r\n cursor: pointer; \r\n user-select: none;\r\n }\r\n\r\n .column-header-cell.sticky > .column-header-cell-label {\r\n position: sticky;\r\n left: 1rem;\r\n }\r\n\r\n .column-header-cell:hover {\r\n background: #f9f9f9;\r\n }\r\n \r\n</style>",
"<script>\r\n import { getContext, onMount } from 'svelte';\r\n \r\n import Column from './Column.svelte';\r\n /**\r\n * Container component for columns rendered as gantt body background\r\n */\r\n export let columns = [];\r\n\r\n function lineAt(ctx, x) {\r\n ctx.beginPath();\r\n ctx.moveTo(x, 0);\r\n ctx.lineTo(x, 20);\r\n ctx.stroke();\r\n }\r\n\r\n function createBackground(columns) {\r\n const canvas = document.createElement('canvas');\r\n canvas.width = columns.length * columns[0].width;\r\n canvas.height = 20;\r\n\r\n const ctx = canvas.getContext('2d');\r\n ctx.shadowColor = \"rgba(128,128,128,0.5)\";\r\n ctx.shadowOffsetX = 0;\r\n ctx.shadowOffsetY = 0;\r\n ctx.shadowBlur = 0.5;\r\n ctx.lineWidth = 1;\r\n ctx.lineCap = \"square\";\r\n ctx.strokeStyle = '#efefef';\r\n ctx.translate(0.5, 0.5);\r\n\r\n columns.forEach(column => {\r\n lineAt(ctx, column.left);\r\n });\r\n\r\n const dataURL = canvas.toDataURL();\r\n return `url(\"${dataURL}\")`;\r\n }\r\n\r\n let backgroundImage;\r\n $: {\r\n backgroundImage = createBackground(columns.slice(0,4));\r\n }\r\n</script>\r\n\r\n<div class=\"sg-columns\" style=\"background-image:{backgroundImage};\">\r\n\t<!-- {#each columns as column}\r\n\t<Column left={column.left} width={column.width} />\r\n\t{/each} -->\r\n</div>\r\n<style>\r\n .sg-columns {\r\n position: absolute;\r\n height: 100%;\r\n width: 100%;\r\n overflow: hidden;\r\n\r\n background-repeat: repeat;\r\n background-position-x: -1px;\r\n }\r\n</style>",
"<script>\r\n import { beforeUpdate, onMount, getContext } from 'svelte';\r\n\r\n let milestoneElement;\r\n\r\n import { Draggable } from '../core/drag';\r\n import { rowStore, taskStore } from '../core/store';\r\n const { rowPadding } = getContext('options');\r\n const { selectionManager, api, rowContainer, dndManager, columnService} = getContext('services');\r\n\r\n export let left;\r\n export let top;\r\n export let model;\r\n export let height = 20;\r\n\r\n const selection = selectionManager.selection;\r\n\r\n let dragging = false;\r\n let x = null;\r\n let y = null;\r\n $: {\r\n if(!dragging){\r\n x = left, y = top;\r\n }\r\n }\r\n\r\n function drag(node) {\r\n const ondrop = ({ x, y, currWidth, event, dragging }) => {\r\n let rowChangeValid = true;\r\n //row switching\r\n if(dragging){\r\n const sourceRow = $rowStore.entities[model.resourceId];\r\n const targetRow = dndManager.getTarget('row', event);\r\n if(targetRow){\r\n model.resourceId = targetRow.model.id;\r\n api.tasks.raise.switchRow(this, targetRow, sourceRow);\r\n }\r\n else{\r\n rowChangeValid = false;\r\n }\r\n }\r\n \r\n dragging = false;\r\n const task = $taskStore.entities[model.id];\r\n if(rowChangeValid) {\r\n const newFrom = utils.roundTo(columnService.getDateByPosition(x)); \r\n const newLeft = columnService.getPositionByDate(newFrom);\r\n\r\n Object.assign(model, {\r\n from: newFrom\r\n });\r\n \r\n $taskStore.update({\r\n ...task,\r\n left: newLeft,\r\n top: rowPadding + $rowStore.entities[model.resourceId].y,\r\n model\r\n });\r\n }\r\n else {\r\n // reset position\r\n $taskStore.update({\r\n ...task\r\n });\r\n }\r\n }\r\n\r\n const draggable = new Draggable(node, {\r\n onDown: ({x, y}) => {\r\n //this.set({x, y});\r\n }, \r\n onDrag: (pos) => {\r\n x = pos.x, y = pos.y, dragging = true;\r\n },\r\n dragAllowed: () => {\r\n return row.model.enableDragging && model.enableDragging;\r\n },\r\n resizeAllowed: false,\r\n onDrop: ondrop, \r\n container: rowContainer, \r\n getX: () => x,\r\n getY: () => y\r\n });\r\n\r\n return {\r\n destroy() { draggable.destroy(); }\r\n }\r\n }\r\n\r\n onMount(() => {\r\n x = left = columnService.getPositionByDate(model.from); \r\n y = top = row.y + $rowPadding;; \r\n height = row.height - 2 * $rowPadding;\r\n });\r\n\r\n export function select(event) {\r\n if(event.ctrlKey){\r\n selectionManager.toggleSelection(model.id);\r\n }\r\n else{\r\n selectionManager.selectSingle(model.id);\r\n }\r\n \r\n if(selected){\r\n api.tasks.raise.select(model);\r\n }\r\n }\r\n\r\n let selected = false;\r\n $: selected = $selection.indexOf(model.id) !== -1;\r\n\r\n let row;\r\n $: row = $rowStore.entities[model.resourceId];\r\n</script>\r\n\r\n<div bind:this={milestoneElement}\r\n class=\"sg-milestone {model.classes}\" \r\n style=\"transform: translate({x}px, {y}px);height:{height}px;width:{height}px\"\r\n use:drag \r\n on:click=\"{select}\"\r\n class:selected=\"{selected}\"\r\n class:moving=\"{dragging}\">\r\n <div class=\"inside\"></div>\r\n <!-- <span class=\"debug\">x:{x|0} y:{y|0}, x:{left|0} y:{top|0}</span> -->\r\n</div>\r\n\r\n<style>\r\n .sg-milestone {\r\n\t\tposition: absolute; \r\n top: 0;\r\n bottom: 0;\r\n\r\n white-space: nowrap;\r\n /* overflow: hidden; */\r\n\r\n height: 20px;\r\n width: 20px;\r\n\r\n min-width: 40px;\r\n margin-left: -20px;\r\n display: flex;\r\n align-items: center;\r\n flex-direction: column;\r\n\r\n transition: background-color 0.2s, opacity 0.2s;\r\n }\r\n\r\n .sg-milestone .inside {\r\n position: relative;\r\n }\r\n\r\n .sg-milestone .inside:before {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n content: ' ';\r\n height: 28px;\r\n width: 28px;\r\n transform-origin: 0 0;\r\n transform: rotate(45deg); \r\n /* //after -45 */\r\n background-color: #feac31;\r\n border-color: #feac31;\r\n }\r\n\r\n .sg-milestone:not(.moving) {\r\n transition: transform 0.2s, background-color 0.2s, width 0.2s;\r\n }\r\n\r\n .sg-milestone.moving{\r\n z-index: 1;\r\n }\r\n\r\n .sg-milestone.selected {\r\n outline: 2px solid rgba(3, 169, 244, 0.5);\r\n outline-offset: 3px;\r\n z-index: 1;\r\n }\r\n</style>",
"<script>\r\n import { getContext } from 'svelte';\r\n export let row;\r\n let rowElement;\r\n\r\n const { rowHeight } = getContext('options');\r\n const { hoveredRow, selectedRow } = getContext('gantt');\r\n</script>\r\n\r\n<div class=\"sg-row {row.model.classes}\" data-row-id=\"{row.model.id}\" class:sg-hover={$hoveredRow == row.model.id} class:sg-selected={$selectedRow == row.model.id} bind:this={rowElement} style=\"height:{$rowHeight}px\">\r\n {#if row.model.contentHtml}\r\n {@html row.model.contentHtml}\r\n {/if}\r\n</div> \r\n<style>\r\n .sg-row {\r\n position: relative;\r\n width: 100%;\r\n box-sizing: border-box;\r\n }\r\n</style>",
"<script>\r\n import { beforeUpdate, afterUpdate, getContext, onMount, onDestroy, tick } from \"svelte\";\r\n\r\n import { setCursor } from \"src/utils/domUtils\";\r\n import { taskStore, rowStore } from '../core/store';\r\n import { Draggable } from \"../core/drag\";\r\n import { reflectTask } from \"src/core/task\";\r\n\r\n export let model;\r\n export let height;\r\n export let left;\r\n export let top;\r\n export let width;\r\n export let reflected = false;\r\n\r\n let animating = true;\r\n\r\n let _dragging = false;\r\n let _resizing = false;\r\n\r\n let _position = {\r\n x: left,\r\n y: top,\r\n width: width,\r\n }\r\n\r\n $: updatePosition(left, top, width);\r\n function updatePosition(x, y, width) {\r\n if(!_dragging && !_resizing) {\r\n _position.x = x;\r\n _position.y = y;//row.y + 6;\r\n _position.width = width;\r\n // should NOT animate on resize/update of columns\r\n }\r\n }\r\n\r\n const { dimensionsChanged } = getContext('dimensions');\r\n const { rowContainer } = getContext('gantt');\r\n const { taskContent, resizeHandleWidth, rowPadding, onTaskButtonClick, reflectOnParentRows, reflectOnChildRows } = getContext('options');\r\n const { dndManager, api, utils, selectionManager, columnService } = getContext('services');\r\n\r\n function drag(node) {\r\n const ondrop = (event) => {\r\n let rowChangeValid = true;\r\n //row switching\r\n const sourceRow = $rowStore.entities[model.resourceId];\r\n if (event.dragging) {\r\n const targetRow = dndManager.getTarget(\"row\", event.mouseEvent);\r\n if (targetRow) {\r\n model.resourceId = targetRow.model.id;\r\n api.tasks.raise.switchRow(this, targetRow, sourceRow);\r\n } else {\r\n rowChangeValid = false;\r\n }\r\n }\r\n\r\n _dragging = _resizing = false;\r\n\r\n const task = $taskStore.entities[model.id];\r\n\r\n if (rowChangeValid) {\r\n const prevFrom = model.from;\r\n const prevTo = model.to;\r\n const newFrom = model.from = utils.roundTo(columnService.getDateByPosition(event.x));\r\n const newTo = model.to = utils.roundTo(columnService.getDateByPosition(event.x + event.width));\r\n const newLeft = columnService.getPositionByDate(newFrom) | 0;\r\n const newRight = columnService.getPositionByDate(newTo) | 0;\r\n\r\n const targetRow = $rowStore.entities[model.resourceId];\r\n const left = newLeft;\r\n const width = newRight - newLeft;\r\n const top = $rowPadding + targetRow.y;\r\n \r\n updatePosition(left, top, width);\r\n\r\n const newTask = {\r\n ...task,\r\n left: left,\r\n width: width,\r\n top: top,\r\n model\r\n }\r\n\r\n const changed = !prevFrom.isSame(newFrom) || !prevTo.isSame(newTo) || (sourceRow && sourceRow.model.id !== targetRow.model.id);\r\n if(changed) {\r\n api.tasks.raise.change({ task: newTask, sourceRow, targetRow });\r\n }\r\n\r\n taskStore.update(newTask);\r\n\r\n if(changed) {\r\n api.tasks.raise.changed({ task: newTask, sourceRow, targetRow });\r\n }\r\n\r\n // update shadow tasks\r\n if(newTask.reflections) {\r\n taskStore.deleteAll(newTask.reflections);\r\n }\r\n\r\n const reflectedTasks = [];\r\n if(reflectOnChildRows && targetRow.allChildren) {\r\n if(!newTask.reflections)\r\n newTask.reflections = [];\r\n\r\n const opts = { rowPadding: $rowPadding };\r\n targetRow.allChildren.forEach(r => {\r\n const reflectedTask = reflectTask(newTask, r, opts);\r\n newTask.reflections.push(reflectedTask.model.id);\r\n reflectedTasks.push(reflectedTask);\r\n });\r\n }\r\n\r\n if(reflectOnParentRows && targetRow.allParents.length > 0) {\r\n if(!newTask.reflections)\r\n newTask.reflections = [];\r\n\r\n const opts = { rowPadding: $rowPadding };\r\n targetRow.allParents.forEach(r => {\r\n const reflectedTask = reflectTask(newTask, r, opts);\r\n newTask.reflections.push(reflectedTask.model.id);\r\n reflectedTasks.push(reflectedTask);\r\n });\r\n }\r\n\r\n if(reflectedTasks.length > 0) {\r\n taskStore.upsertAll(reflectedTasks);\r\n }\r\n\r\n if(!(targetRow.allParents.length > 0) && !targetRow.allChildren) {\r\n newTask.reflections = null;\r\n }\r\n }\r\n else {\r\n // reset position\r\n (_position.x = task.left), (_position.width = task.width), (_position.y = task.top);\r\n }\r\n };\r\n\r\n const draggable = new Draggable(node, {\r\n onDown: (event) => {\r\n if (event.dragging) {\r\n setCursor(\"move\");\r\n }\r\n if (event.resizing) {\r\n setCursor(\"e-resize\");\r\n }\r\n },\r\n onMouseUp: () => {\r\n setCursor(\"default\");\r\n },\r\n onResize: (event) => {\r\n (_position.x = event.x), (_position.width = event.width), (_resizing = true);\r\n },\r\n onDrag: (event) => {\r\n (_position.x = event.x), (_position.y = event.y), (_dragging = true);\r\n },\r\n dragAllowed: () => {\r\n return row.model.enableDragging && model.enableDragging;\r\n },\r\n resizeAllowed: () => {\r\n return row.model.enableDragging && model.enableDragging;\r\n },\r\n onDrop: ondrop,\r\n container: rowContainer,\r\n resizeHandleWidth, \r\n getX: () => _position.x,\r\n getY: () => _position.y,\r\n getWidth: () => _position.width\r\n });\r\n return {\r\n destroy: () => draggable.destroy()\r\n };\r\n }\r\n\r\n export function onclick(event) {\r\n if (onTaskButtonClick) {\r\n onTaskButtonClick(task);\r\n }\r\n }\r\n\r\n let selection = selectionManager.selection;\r\n let selected = false;\r\n $: selected = $selection.indexOf(model.id) !== -1;\r\n\r\n let row;\r\n $: row = $rowStore.entities[model.resourceId];\r\n</script>\r\n\r\n<style>\r\n .sg-label-bottom {\r\n position: absolute;\r\n top: calc(100% + 10px);\r\n color: #888;\r\n }\r\n\r\n .debug {\r\n position: absolute;\r\n top: -10px;\r\n right: 0;\r\n font-size: 8px;\r\n color: black;\r\n }\r\n\r\n .sg-task {\r\n position: absolute;\r\n\r\n white-space: nowrap;\r\n /* overflow: hidden; */\r\n\r\n transition: background-color 0.2s, opacity 0.2s;\r\n pointer-events: all;\r\n }\r\n\r\n .sg-task-background {\r\n position: absolute;\r\n height: 100%;\r\n top: 0;\r\n }\r\n\r\n .sg-task-content {\r\n position: absolute;\r\n height: 100%;\r\n top: 0;\r\n\r\n padding-left: 14px;\r\n font-size: 14px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: flex-start;\r\n }\r\n\r\n .sg-task:not(.moving) {\r\n transition: transform 0.2s, background-color 0.2s, width 0.2s;\r\n }\r\n\r\n .sg-task.moving {\r\n z-index: 1;\r\n }\r\n\r\n .sg-task:hover::before {\r\n content: \"\";\r\n width: 4px;\r\n height: 50%;\r\n top: 25%;\r\n position: absolute;\r\n cursor: ew-resize;\r\n border-style: solid;\r\n border-color: rgba(255, 255, 255, 0.5);\r\n\r\n margin-left: 3px;\r\n left: 0;\r\n border-width: 0 1px;\r\n z-index: 1;\r\n }\r\n\r\n .sg-task:hover::after {\r\n content: \"\";\r\n width: 4px;\r\n height: 50%;\r\n top: 25%;\r\n position: absolute;\r\n cursor: ew-resize;\r\n border-style: solid;\r\n border-color: rgba(255, 255, 255, 0.5);\r\n\r\n margin-right: 3px;\r\n right: 0;\r\n border-width: 0 1px;\r\n z-index: 1;\r\n }\r\n\r\n .sg-task.selected {\r\n outline: 2px solid rgba(3, 169, 244, 0.5);\r\n outline-offset: 3px;\r\n z-index: 1;\r\n }\r\n\r\n .sg-task-reflected {\r\n opacity: 0.5;\r\n }\r\n</style>\r\n\r\n<div\r\n data-task-id=\"{model.id}\"\r\n use:drag\r\n class=\"sg-task {model.classes}\"\r\n style=\"width:{_position.width}px; height:{height}px; transform: translate({_position.x}px, {_position.y}px);\"\r\n class:moving={_dragging || _resizing}\r\n class:selected\r\n class:animating\r\n class:sg-task-reflected={reflected}>\r\n {#if model.amountDone}\r\n <div class=\"sg-task-background\" style=\"width:{model.amountDone}%\" />\r\n {/if}\r\n <div class=\"sg-task-content\">\r\n {#if model.html}\r\n {@html model.html}\r\n {:else if taskContent}\r\n {@html taskContent(model)}\r\n {:else}{model.label}{/if}\r\n <!-- <span class=\"debug\">x:{_position.x} y:{_position.y}, x:{left} y:{top}</span> -->\r\n {#if model.showButton}\r\n <span class=\"sg-task-button {model.buttonClasses}\" on:click={onclick}>\r\n {@html model.buttonHtml}\r\n </span>\r\n {/if}\r\n </div>\r\n\r\n {#if model.labelBottom}\r\n <label class=\"sg-label-bottom\">{model.labelBottom}</label>\r\n {/if}\r\n</div>\r\n",
"<script>\r\n export let model;\r\n export let left;\r\n export let width;\r\n export let resizing = false;\r\n\r\n const _position = {\r\n width,\r\n x: left\r\n }\r\n $: {\r\n _position.x = left, _position.width = width;\r\n };\r\n</script>\r\n\r\n<div class=\"sg-time-range\" class:moving=\"{resizing}\" style=\"width:{_position.width}px;left:{_position.x}px\">\r\n <div class=\"sg-time-range-label\">{model.label}</div>\r\n</div>\r\n<style>\r\n .sg-time-range {\r\n height: 100%;\r\n position: absolute;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n\r\n background-image: linear-gradient(-45deg, rgba(0, 0, 0, 0) 46%, #e03218 49%, #e03218 51%, rgba(0, 0, 0, 0) 55%);\r\n background-size: 6px 6px !important;\r\n color: red;\r\n font-weight: 400;\r\n }\r\n\r\n .sg-time-range-label {\r\n margin-top: 10px;\r\n background: #fff;\r\n white-space: nowrap;\r\n padding: 4px;\r\n font-weight: 400;\r\n font-size: 10px;\r\n }\r\n</style>",
"<script>\r\n import { beforeUpdate, getContext } from 'svelte';\r\n\r\n import { Draggable } from '../core/drag';\r\n import { timeRangeStore } from '../core/store';\r\n\r\n const { rowContainer } = getContext('gantt');\r\n const { utils, columnService } = getContext('services');\r\n const { resizeHandleWidth } = getContext('options');\r\n const {\r\n from,\r\n to,\r\n width: ganttWidth,\r\n visibleWidth\r\n } = getContext('dimensions');\r\n\r\n export let model;\r\n export let width;\r\n export let left;\r\n \r\n const _position = {\r\n width,\r\n x: left\r\n }\r\n $: {\r\n _position.x = left, _position.width = width;\r\n };\r\n\r\n function drag(node) {\r\n const ondrop = (event) => {\r\n const newFrom = utils.roundTo(columnService.getDateByPosition(event.x)); \r\n const newTo = utils.roundTo(columnService.getDateByPosition(event.x + event.width));\r\n const newLeft = columnService.getPositionByDate(newFrom);\r\n const newRight = columnService.getPositionByDate(newTo);\r\n \r\n Object.assign(model, {\r\n from: newFrom,\r\n to: newTo\r\n });\r\n\r\n update({\r\n left: newLeft,\r\n width: newRight - newLeft,\r\n model,\r\n resizing: false\r\n });\r\n\r\n window.removeEventListener('mousemove', onmousemove, false);\r\n };\r\n\r\n function update(state) {\r\n timeRangeStore.update(state);\r\n _position.x = state.left;\r\n _position.width = state.width;\r\n }\r\n\r\n return new Draggable(node, {\r\n onDown: (event) => {\r\n update({\r\n left: event.x,\r\n width: event.width,\r\n model,\r\n resizing: true\r\n });\r\n }, \r\n onResize: (event) => {\r\n update({\r\n left: event.x,\r\n width: event.width,\r\n model,\r\n resizing: true\r\n });\r\n },\r\n dragAllowed: false,\r\n resizeAllowed: true,\r\n onDrop: ondrop, \r\n container: rowContainer, \r\n resizeHandleWidth,\r\n