Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ class Scheduler extends SchedulerOptionsBaseWidget {
break;
case 'dateSerializationFormat':
break;
case 'autoHeight':
case 'maxAppointmentsPerCell':
this.repaint();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const DEFAULT_SCHEDULER_OPTIONS: Properties = {
indicatorUpdateInterval: 300000,
recurrenceEditMode: 'dialog',
cellDuration: 30,
autoHeight: false,
maxAppointmentsPerCell: 'auto',
selectedCellData: [],
groupByDate: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { filterAppointments } from './filtration/filter_appointments';
import { getOccurrences } from './filtration/get_occurrences';
import { generateAgendaViewModel } from './generate_view_model/generate_agenda_view_model';
import { generateGridViewModel, sortAppointments } from './generate_view_model/generate_grid_view_model';
import { getDefaultAppointmentSize } from './generate_view_model/options/get_min_appointment_size';
import { OptionManager } from './generate_view_model/options/option_manager';
import type { RealSize } from './generate_view_model/steps/add_geometry/types';
import { getAgendaAppointmentInfo, getAppointmentInfo } from './get_appointment_info';
Expand All @@ -20,6 +21,57 @@ import type {
UTCDatesBeforeSplit,
} from './types';

const computeAutoPerRowHeights = (
sortedItems: SortedEntity[],
panelName: 'regularPanel' | 'allDayPanel',
minHeight: number,
baseCellHeight: number,
isMonthView: boolean,
): number[] => {
const maxLevelPerRow: Record<number, number> = {};

for (const item of sortedItems) {
const inPanel = panelName === 'allDayPanel' ? item.isAllDayPanelOccupied : !item.isAllDayPanelOccupied;
if (inPanel) {
const rowKey = isMonthView ? item.rowIndex : item.groupIndex;
const prev = maxLevelPerRow[rowKey] ?? 0;
if (item.maxLevel > prev) {
maxLevelPerRow[rowKey] = item.maxLevel;
}
}
}

const keys = Object.keys(maxLevelPerRow);
if (keys.length === 0) {
return [];
}

const maxKey = Math.max(...keys.map(Number));
const heights: number[] = Array<number>(maxKey + 1).fill(baseCellHeight);
for (const [key, maxLevel] of Object.entries(maxLevelPerRow)) {
heights[Number(key)] = Math.max(baseCellHeight, maxLevel * minHeight);
}
return heights;
};

const computeAllDayAutoHeight = (
sortedItems: SortedEntity[],
minHeight: number,
baseCellHeight: number,
): number => {
let maxLevel = 0;

for (const item of sortedItems) {
if (item.isAllDayPanelOccupied) {
maxLevel = Math.max(maxLevel, item.maxLevel);
}
}

return maxLevel > 0
? Math.max(baseCellHeight, maxLevel * minHeight)
: baseCellHeight;
};

class AppointmentLayoutManager {
private preparedItems: MinimalAppointmentEntity[] = [];

Expand Down Expand Up @@ -77,6 +129,62 @@ class AppointmentLayoutManager {

this._sortedItems = sortAppointments(optionManager, this._filteredItems);

const {
autoHeight,
viewOrientation,
isTimelineView,
isMonthView,
isAdaptivityEnabled,
hasAllDayPanel,
} = optionManager.options;

const isAutoHeightApplicable = autoHeight && (isTimelineView || isMonthView);

if (isAutoHeightApplicable) {
const workspace = this.schedulerStore.getWorkSpace();
const baseCellHeight = Math.max(workspace.getCellHeight(), 1);
const minAppointmentHeight = getDefaultAppointmentSize({
isTimelineView,
isAdaptivityEnabled,
viewOrientation,
}).height;

const autoRowHeights = computeAutoPerRowHeights(
this._sortedItems,
'regularPanel',
minAppointmentHeight,
baseCellHeight,
isMonthView,
);

if (autoRowHeights.length) {
workspace.setAutoRowHeights(autoRowHeights);
optionManager.setAutoRowHeights(autoRowHeights);
}

if (hasAllDayPanel) {
const allDayBaseCellHeight = Math.max(workspace.getAllDayHeight(), 1);
const allDayMinAppointmentHeight = getDefaultAppointmentSize({
isTimelineView: false,
isAdaptivityEnabled,
viewOrientation: 'horizontal',
}).height;
const requiredAllDayHeight = computeAllDayAutoHeight(
this._sortedItems,
allDayMinAppointmentHeight,
allDayBaseCellHeight,
);

workspace.setAutoAllDayRowHeight(requiredAllDayHeight);
}

optionManager.clearCache();
} else {
const workspace = this.schedulerStore.getWorkSpace();
workspace.clearAutoRowHeight();
optionManager.resetAutoRowHeights();
}

const viewModel = generateGridViewModel(
this.schedulerStore,
optionManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const MIN_LEVEL_VERTICAL_VIEW = 1;
export const getPanelCollectorOptions = (schedulerStore: Scheduler, {
alwaysReserveSpaceForCollector,
isTimelineView,
isMonthView,
viewOrientation,
isAdaptivityEnabled,
collectorCSS,
Expand All @@ -26,6 +27,7 @@ export const getPanelCollectorOptions = (schedulerStore: Scheduler, {
DOMMetaData: DOMMetaData;
alwaysReserveSpaceForCollector: boolean;
isTimelineView: boolean;
isMonthView: boolean;
viewOrientation: Orientation;
isAdaptivityEnabled: boolean;
collectorCSS: CollectorCSS;
Expand All @@ -52,7 +54,13 @@ export const getPanelCollectorOptions = (schedulerStore: Scheduler, {
width: cellDOM.width ?? 0,
height: cellDOM.height ?? 0,
};
const maxAppointmentsPerCell = schedulerStore.getViewOption('maxAppointmentsPerCell');
const isAutoHeight = Boolean(
schedulerStore.getViewOption('autoHeight') ?? schedulerStore.option('autoHeight'),
);
const isAutoHeightApplicable = isAutoHeight && (isTimelineView || isMonthView);
const maxAppointmentsPerCell = isAutoHeightApplicable
? 'unlimited'
: schedulerStore.getViewOption('maxAppointmentsPerCell');
const collectorSizes = maxAppointmentsPerCell === 'unlimited' && !alwaysReserveSpaceForCollector
? UNLIMITED_COLLECTOR_SIZES
: getCollectorSize(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface ViewModelOptions {
isAdaptivityEnabled: boolean;
cellDurationMinutes: number;
isVirtualScrolling: boolean;
autoHeight: boolean;
}

export const getViewModelOptions = (schedulerStore: Scheduler): ViewModelOptions => {
Expand All @@ -76,6 +77,7 @@ export const getViewModelOptions = (schedulerStore: Scheduler): ViewModelOptions
} = configByView[type];
const isRTLEnabled = Boolean(schedulerStore.option('rtlEnabled'));
const isAdaptivityEnabled = Boolean(schedulerStore.option('adaptivityEnabled'));
const autoHeight = Boolean(schedulerStore.getViewOption('autoHeight') ?? schedulerStore.option('autoHeight'));
const cellDurationMinutes = schedulerStore.getViewOption('cellDuration');
const allDayPanelMode = schedulerStore.getViewOption('allDayPanelMode');
const snapToCellsMode = schedulerStore.getViewOption('snapToCellsMode');
Expand All @@ -95,6 +97,7 @@ export const getViewModelOptions = (schedulerStore: Scheduler): ViewModelOptions
viewOrientation,
isRTLEnabled,
isAdaptivityEnabled,
autoHeight,
cellDurationMinutes,
hasAllDayPanel: showAllDayPanel && allDayPanelMode !== 'hidden' && viewOrientation === 'vertical',
isVirtualScrolling,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,20 @@ export class OptionManager {

public readonly options: ViewModelOptions;

private autoRowHeightsData: number[] | undefined;

constructor(protected schedulerStore: Scheduler) {
this.options = getViewModelOptions(schedulerStore);
}

setAutoRowHeights(heights: number[]): void {
this.autoRowHeightsData = heights.length ? heights : undefined;
}

resetAutoRowHeights(): void {
this.autoRowHeightsData = undefined;
}

protected getPanelOptions(panelName: PanelName): {
splitIntervals: DateInterval[];
cells: CellInterval[];
Expand Down Expand Up @@ -89,6 +99,7 @@ export class OptionManager {
} = getPanelCollectorOptions(this.schedulerStore, {
alwaysReserveSpaceForCollector: type === 'month',
isTimelineView,
isMonthView,
viewOrientation,
isAdaptivityEnabled,
collectorCSS,
Expand Down Expand Up @@ -120,6 +131,7 @@ export class OptionManager {
groupOrientation,
isGroupByDate,
isTimelineView,
isMonthView,
isRTLEnabled,
isAdaptivityEnabled,
allDayPanelCellSize,
Expand All @@ -137,6 +149,7 @@ export class OptionManager {
isAllDayPanel: panelName === 'allDayPanel',
}),
panelSize: panelDOMSize,
autoRowHeights: this.autoRowHeightsData,
};
const collectorOptions: CollectorOptions = {
cells,
Expand All @@ -155,6 +168,10 @@ export class OptionManager {
});
}

clearCache(): void {
this.cache.clear();
}

getSplitIntervals(panelName: PanelName): DateInterval[] {
return this.getPanelOptions(panelName).splitIntervals;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AppointmentCollectorWithGeometry } from '../../../types';
import { addAdaptivityGeometryInsideInterval } from './add_adaptivity_geometry_inside_interval';
import { addGeometryInsideInterval } from './add_geometry_inside_interval';
import { addGroupingOffset } from './add_grouping_offset';
import { addGroupingOffset, getCumulativeRowOffset } from './add_grouping_offset';
import type {
Geometry,
GeometryMinimalEntity,
Expand All @@ -20,11 +20,13 @@ const RTLSwap = (

const addPanelOffset = <T extends GeometryMinimalEntity & Geometry>(
entity: T,
{ cellSize, viewOrientation, isTimelineView }: GeometryOptions,
{
cellSize, viewOrientation, isTimelineView, autoRowHeights,
}: GeometryOptions,
): void => {
switch (true) {
case viewOrientation === 'horizontal' && !isTimelineView: // month
entity.top += entity.rowIndex * cellSize.height;
entity.top += getCumulativeRowOffset(autoRowHeights, entity.rowIndex, cellSize.height);
break;
case viewOrientation === 'horizontal' && isTimelineView: // timelineX
case viewOrientation === 'vertical': // day, week, workWeek
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import type {
GeometryOptions,
} from './types';

export const getCumulativeRowOffset = (
heights: number[] | undefined,
index: number,
uniformSize: number,
): number => (heights?.length
? heights.slice(0, index).reduce((sum, height) => sum + height, 0)
: index * uniformSize);

export const addGroupingOffset = (
entity: GeometryMinimalEntity & Geometry,
{
Expand All @@ -15,6 +23,7 @@ export const addGroupingOffset = (
allDayPanelCellSize,
cellSize,
groupSize,
autoRowHeights,
}: GeometryOptions,
): void => {
if (groupCount) {
Expand All @@ -29,10 +38,17 @@ export const addGroupingOffset = (
case groupOrientation === 'horizontal':
entity.left += entity.groupIndex * groupSize.width; // intervals before
break;
default:
entity.top += entity.groupIndex * groupSize.height
+ (entity.groupIndex + Number(!entity.isAllDayPanelOccupied))
default: {
const groupTopOffset = getCumulativeRowOffset(
autoRowHeights,
entity.groupIndex,
groupSize.height,
);

entity.top += groupTopOffset
+ (entity.groupIndex + Number(!entity.isAllDayPanelOccupied))
* Number(hasAllDayPanel) * allDayPanelCellSize.height;
}
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@ export const getAppointmentGeometry = (
cellSize,
collectorWithMarginsSize,
viewOrientation,
isMonthView,
cells,
autoRowHeights,
}: GeometryOptions,
): Geometry => {
const cellAbstractSize = getAbstractSizeByViewOrientation(cellSize, viewOrientation);
const rowKey = isMonthView ? entity.rowIndex : entity.groupIndex;
const effectiveCellSize = autoRowHeights?.length
? { ...cellSize, height: autoRowHeights[rowKey] ?? cellSize.height }
: cellSize;

const cellAbstractSize = getAbstractSizeByViewOrientation(effectiveCellSize, viewOrientation);
const collectorFullAbstractSize = getAbstractSizeByViewOrientation(
collectorWithMarginsSize,
viewOrientation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface GeometryOptions {
isGroupByDate: boolean;
hasAllDayPanel: boolean;
isTimelineView: boolean;
isMonthView: boolean;
isRTLEnabled: boolean;
isAdaptivityEnabled: boolean;
collectorPosition: 'start' | 'end';
Expand All @@ -69,6 +70,7 @@ export interface GeometryOptions {
collectorWithMarginsSize: RealSize;
groupSize: RealSize;
panelSize: RealSize;
autoRowHeights?: number[];
}

export interface VirtualCropOptions {
Expand Down
Loading
Loading