routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+2
@@ -0,0 +1,2 @@
|
||||
import type { CalendarEventOverlapMode } from '../types.js';
|
||||
export declare const column: CalendarEventOverlapMode;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Types
|
||||
import { getOverlapGroupHandler } from "./common.js";
|
||||
const FULL_WIDTH = 100;
|
||||
export const column = (events, firstWeekday, overlapThreshold) => {
|
||||
const handler = getOverlapGroupHandler(firstWeekday);
|
||||
return (day, dayEvents, timed, reset) => {
|
||||
const visuals = handler.getVisuals(day, dayEvents, timed, reset);
|
||||
if (timed) {
|
||||
visuals.forEach(visual => {
|
||||
visual.left = visual.column * FULL_WIDTH / visual.columnCount;
|
||||
visual.width = FULL_WIDTH / visual.columnCount;
|
||||
});
|
||||
}
|
||||
return visuals;
|
||||
};
|
||||
};
|
||||
//# sourceMappingURL=column.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"column.js","names":["getOverlapGroupHandler","FULL_WIDTH","column","events","firstWeekday","overlapThreshold","handler","day","dayEvents","timed","reset","visuals","getVisuals","forEach","visual","left","columnCount","width"],"sources":["../../../../src/components/VCalendar/modes/column.ts"],"sourcesContent":["// Types\nimport { getOverlapGroupHandler } from './common'\nimport type { CalendarEventOverlapMode } from '../types'\n\nconst FULL_WIDTH = 100\n\nexport const column: CalendarEventOverlapMode = (events, firstWeekday, overlapThreshold) => {\n const handler = getOverlapGroupHandler(firstWeekday)\n\n return (day, dayEvents, timed, reset) => {\n const visuals = handler.getVisuals(day, dayEvents, timed, reset)\n\n if (timed) {\n visuals.forEach(visual => {\n visual.left = visual.column * FULL_WIDTH / visual.columnCount\n visual.width = FULL_WIDTH / visual.columnCount\n })\n }\n\n return visuals\n }\n}\n"],"mappings":"AAAA;AAAA,SACSA,sBAAsB;AAG/B,MAAMC,UAAU,GAAG,GAAG;AAEtB,OAAO,MAAMC,MAAgC,GAAGA,CAACC,MAAM,EAAEC,YAAY,EAAEC,gBAAgB,KAAK;EAC1F,MAAMC,OAAO,GAAGN,sBAAsB,CAACI,YAAY,CAAC;EAEpD,OAAO,CAACG,GAAG,EAAEC,SAAS,EAAEC,KAAK,EAAEC,KAAK,KAAK;IACvC,MAAMC,OAAO,GAAGL,OAAO,CAACM,UAAU,CAACL,GAAG,EAAEC,SAAS,EAAEC,KAAK,EAAEC,KAAK,CAAC;IAEhE,IAAID,KAAK,EAAE;MACTE,OAAO,CAACE,OAAO,CAACC,MAAM,IAAI;QACxBA,MAAM,CAACC,IAAI,GAAGD,MAAM,CAACZ,MAAM,GAAGD,UAAU,GAAGa,MAAM,CAACE,WAAW;QAC7DF,MAAM,CAACG,KAAK,GAAGhB,UAAU,GAAGa,MAAM,CAACE,WAAW;MAChD,CAAC,CAAC;IACJ;IAEA,OAAOL,OAAO;EAChB,CAAC;AACH,CAAC","ignoreList":[]}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import type { CalendarEventParsed, CalendarEventVisual, CalendarTimestamp } from '../types.js';
|
||||
export type GetRange = (event: CalendarEventParsed) => [number, number];
|
||||
export declare function getVisuals(events: CalendarEventParsed[], minStart?: number): CalendarEventVisual[];
|
||||
export interface ColumnGroup {
|
||||
start: number;
|
||||
end: number;
|
||||
visuals: CalendarEventVisual[];
|
||||
}
|
||||
export declare function hasOverlap(s0: number, e0: number, s1: number, e1: number, exclude?: boolean): boolean;
|
||||
export declare function setColumnCount(groups: ColumnGroup[]): void;
|
||||
export declare function getRange(event: CalendarEventParsed): [number, number];
|
||||
export declare function getDayRange(event: CalendarEventParsed): [number, number];
|
||||
export declare function getNormalizedRange(event: CalendarEventParsed, dayStart: number): [number, number];
|
||||
export declare function getOpenGroup(groups: ColumnGroup[], start: number, end: number, timed: boolean): number;
|
||||
export declare function getOverlapGroupHandler(firstWeekday: number): {
|
||||
groups: ColumnGroup[];
|
||||
min: number;
|
||||
max: number;
|
||||
reset: () => void;
|
||||
getVisuals: (day: CalendarTimestamp, dayEvents: CalendarEventParsed[], timed: boolean, reset?: boolean) => CalendarEventVisual[];
|
||||
};
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
// Types
|
||||
import { getTimestampIdentifier } from "../util/timestamp.js";
|
||||
const MILLIS_IN_DAY = 86400000;
|
||||
export function getVisuals(events, minStart = 0) {
|
||||
const visuals = events.map(event => ({
|
||||
event,
|
||||
columnCount: 0,
|
||||
column: 0,
|
||||
left: 0,
|
||||
width: 100
|
||||
}));
|
||||
visuals.sort((a, b) => {
|
||||
return Math.max(minStart, a.event.startTimestampIdentifier) - Math.max(minStart, b.event.startTimestampIdentifier) || b.event.endTimestampIdentifier - a.event.endTimestampIdentifier;
|
||||
});
|
||||
return visuals;
|
||||
}
|
||||
export function hasOverlap(s0, e0, s1, e1, exclude = true) {
|
||||
return exclude ? !(s0 >= e1 || e0 <= s1) : !(s0 > e1 || e0 < s1);
|
||||
}
|
||||
export function setColumnCount(groups) {
|
||||
groups.forEach(group => {
|
||||
group.visuals.forEach(groupVisual => {
|
||||
groupVisual.columnCount = groups.length;
|
||||
});
|
||||
});
|
||||
}
|
||||
export function getRange(event) {
|
||||
return [event.startTimestampIdentifier, event.endTimestampIdentifier];
|
||||
}
|
||||
export function getDayRange(event) {
|
||||
return [event.startIdentifier, event.endIdentifier];
|
||||
}
|
||||
export function getNormalizedRange(event, dayStart) {
|
||||
return [Math.max(dayStart, event.startTimestampIdentifier), Math.min(dayStart + MILLIS_IN_DAY, event.endTimestampIdentifier)];
|
||||
}
|
||||
export function getOpenGroup(groups, start, end, timed) {
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
const group = groups[i];
|
||||
let intersected = false;
|
||||
if (hasOverlap(start, end, group.start, group.end, timed)) {
|
||||
for (let k = 0; k < group.visuals.length; k++) {
|
||||
const groupVisual = group.visuals[k];
|
||||
const [groupStart, groupEnd] = timed ? getRange(groupVisual.event) : getDayRange(groupVisual.event);
|
||||
if (hasOverlap(start, end, groupStart, groupEnd, timed)) {
|
||||
intersected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!intersected) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
export function getOverlapGroupHandler(firstWeekday) {
|
||||
const handler = {
|
||||
groups: [],
|
||||
min: -1,
|
||||
max: -1,
|
||||
reset: () => {
|
||||
handler.groups = [];
|
||||
handler.min = handler.max = -1;
|
||||
},
|
||||
getVisuals: (day, dayEvents, timed, reset = false) => {
|
||||
if (day.weekday === firstWeekday || reset) {
|
||||
handler.reset();
|
||||
}
|
||||
const dayStart = getTimestampIdentifier(day);
|
||||
const visuals = getVisuals(dayEvents, dayStart);
|
||||
visuals.forEach(visual => {
|
||||
const [start, end] = timed ? getRange(visual.event) : getDayRange(visual.event);
|
||||
if (handler.groups.length > 0 && !hasOverlap(start, end, handler.min, handler.max, timed)) {
|
||||
setColumnCount(handler.groups);
|
||||
handler.reset();
|
||||
}
|
||||
let targetGroup = getOpenGroup(handler.groups, start, end, timed);
|
||||
if (targetGroup === -1) {
|
||||
targetGroup = handler.groups.length;
|
||||
handler.groups.push({
|
||||
start,
|
||||
end,
|
||||
visuals: []
|
||||
});
|
||||
}
|
||||
const target = handler.groups[targetGroup];
|
||||
target.visuals.push(visual);
|
||||
target.start = Math.min(target.start, start);
|
||||
target.end = Math.max(target.end, end);
|
||||
visual.column = targetGroup;
|
||||
if (handler.min === -1) {
|
||||
handler.min = start;
|
||||
handler.max = end;
|
||||
} else {
|
||||
handler.min = Math.min(handler.min, start);
|
||||
handler.max = Math.max(handler.max, end);
|
||||
}
|
||||
});
|
||||
setColumnCount(handler.groups);
|
||||
if (timed) {
|
||||
handler.reset();
|
||||
}
|
||||
return visuals;
|
||||
}
|
||||
};
|
||||
return handler;
|
||||
}
|
||||
//# sourceMappingURL=common.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+2
@@ -0,0 +1,2 @@
|
||||
import type { CalendarEventOverlapMode } from '../types.js';
|
||||
export declare const CalendarEventOverlapModes: Record<string, CalendarEventOverlapMode>;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Types
|
||||
import { column } from "./column.js";
|
||||
import { stack } from "./stack.js";
|
||||
export const CalendarEventOverlapModes = {
|
||||
stack,
|
||||
column
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","names":["column","stack","CalendarEventOverlapModes"],"sources":["../../../../src/components/VCalendar/modes/index.ts"],"sourcesContent":["// Types\nimport { column } from './column'\nimport { stack } from './stack'\nimport type { CalendarEventOverlapMode } from '../types'\n\nexport const CalendarEventOverlapModes: Record<string, CalendarEventOverlapMode> = {\n stack,\n column,\n}\n"],"mappings":"AAAA;AAAA,SACSA,MAAM;AAAA,SACNC,KAAK;AAGd,OAAO,MAAMC,yBAAmE,GAAG;EACjFD,KAAK;EACLD;AACF,CAAC","ignoreList":[]}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import type { CalendarEventOverlapMode } from '../types.js';
|
||||
/**
|
||||
* Variation of column mode where events can be stacked. The priority of this
|
||||
* mode is to stack events together taking up the least amount of space while
|
||||
* trying to ensure the content of the event is always visible as well as its
|
||||
* start and end. A sibling column has intersecting event content and must be
|
||||
* placed beside each other. Non-sibling columns are offset by 5% from the
|
||||
* previous column. The width is scaled by 1.7 so the events overlap and
|
||||
* whitespace is reduced. If there is a hole in columns the event width is
|
||||
* scaled up so it intersects with the next column. The columns have equal
|
||||
* width in the space they are given. If the event doesn't have any to the
|
||||
* right of it that intersect with it's content it's right side is extended
|
||||
* to the right side.
|
||||
*/
|
||||
export declare const stack: CalendarEventOverlapMode;
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
// Types
|
||||
import { getNormalizedRange, getOverlapGroupHandler, getVisuals, hasOverlap } from "./common.js";
|
||||
import { getTimestampIdentifier } from "../util/timestamp.js";
|
||||
const FULL_WIDTH = 100;
|
||||
const DEFAULT_OFFSET = 5;
|
||||
const WIDTH_MULTIPLIER = 1.7;
|
||||
|
||||
/**
|
||||
* Variation of column mode where events can be stacked. The priority of this
|
||||
* mode is to stack events together taking up the least amount of space while
|
||||
* trying to ensure the content of the event is always visible as well as its
|
||||
* start and end. A sibling column has intersecting event content and must be
|
||||
* placed beside each other. Non-sibling columns are offset by 5% from the
|
||||
* previous column. The width is scaled by 1.7 so the events overlap and
|
||||
* whitespace is reduced. If there is a hole in columns the event width is
|
||||
* scaled up so it intersects with the next column. The columns have equal
|
||||
* width in the space they are given. If the event doesn't have any to the
|
||||
* right of it that intersect with it's content it's right side is extended
|
||||
* to the right side.
|
||||
*/
|
||||
|
||||
export const stack = (events, firstWeekday, overlapThreshold) => {
|
||||
const handler = getOverlapGroupHandler(firstWeekday);
|
||||
|
||||
// eslint-disable-next-line max-statements
|
||||
return (day, dayEvents, timed, reset) => {
|
||||
if (!timed) {
|
||||
return handler.getVisuals(day, dayEvents, timed, reset);
|
||||
}
|
||||
const dayStart = getTimestampIdentifier(day);
|
||||
const visuals = getVisuals(dayEvents, dayStart);
|
||||
const groups = getGroups(visuals, dayStart);
|
||||
for (const group of groups) {
|
||||
const nodes = [];
|
||||
for (const visual of group.visuals) {
|
||||
const child = getNode(visual, dayStart);
|
||||
const index = getNextIndex(child, nodes);
|
||||
if (index === false) {
|
||||
const parent = getParent(child, nodes);
|
||||
if (parent) {
|
||||
child.parent = parent;
|
||||
child.sibling = hasOverlap(child.start, child.end, parent.start, addTime(parent.start, overlapThreshold));
|
||||
child.index = parent.index + 1;
|
||||
parent.children.push(child);
|
||||
}
|
||||
} else {
|
||||
const [parent] = getOverlappingRange(child, nodes, index - 1, index - 1);
|
||||
const children = getOverlappingRange(child, nodes, index + 1, index + nodes.length, true);
|
||||
child.children = children;
|
||||
child.index = index;
|
||||
if (parent) {
|
||||
child.parent = parent;
|
||||
child.sibling = hasOverlap(child.start, child.end, parent.start, addTime(parent.start, overlapThreshold));
|
||||
parent.children.push(child);
|
||||
}
|
||||
for (const grand of children) {
|
||||
if (grand.parent === parent) {
|
||||
grand.parent = child;
|
||||
}
|
||||
const grandNext = grand.index - child.index <= 1;
|
||||
if (grandNext && child.sibling && hasOverlap(child.start, addTime(child.start, overlapThreshold), grand.start, grand.end)) {
|
||||
grand.sibling = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
nodes.push(child);
|
||||
}
|
||||
calculateBounds(nodes, overlapThreshold);
|
||||
}
|
||||
visuals.sort((a, b) => a.left - b.left || a.event.startTimestampIdentifier - b.event.startTimestampIdentifier);
|
||||
return visuals;
|
||||
};
|
||||
};
|
||||
function calculateBounds(nodes, overlapThreshold) {
|
||||
for (const node of nodes) {
|
||||
const {
|
||||
visual,
|
||||
parent
|
||||
} = node;
|
||||
const columns = getMaxChildIndex(node) + 1;
|
||||
const spaceLeft = parent ? parent.visual.left : 0;
|
||||
const spaceWidth = FULL_WIDTH - spaceLeft;
|
||||
const offset = Math.min(DEFAULT_OFFSET, FULL_WIDTH / columns);
|
||||
const columnWidthMultiplier = getColumnWidthMultiplier(node, nodes);
|
||||
const columnOffset = spaceWidth / (columns - node.index + 1);
|
||||
const columnWidth = spaceWidth / (columns - node.index + (node.sibling ? 1 : 0)) * columnWidthMultiplier;
|
||||
if (parent) {
|
||||
visual.left = node.sibling ? spaceLeft + columnOffset : spaceLeft + offset;
|
||||
}
|
||||
visual.width = hasFullWidth(node, nodes, overlapThreshold) ? FULL_WIDTH - visual.left : Math.min(FULL_WIDTH - visual.left, columnWidth * WIDTH_MULTIPLIER);
|
||||
}
|
||||
}
|
||||
function getColumnWidthMultiplier(node, nodes) {
|
||||
if (!node.children.length) {
|
||||
return 1;
|
||||
}
|
||||
const maxColumn = node.index + nodes.length;
|
||||
const minColumn = node.children.reduce((min, c) => Math.min(min, c.index), maxColumn);
|
||||
return minColumn - node.index;
|
||||
}
|
||||
function getOverlappingIndices(node, nodes) {
|
||||
const indices = [];
|
||||
for (const other of nodes) {
|
||||
if (hasOverlap(node.start, node.end, other.start, other.end)) {
|
||||
indices.push(other.index);
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
function getNextIndex(node, nodes) {
|
||||
const indices = getOverlappingIndices(node, nodes);
|
||||
indices.sort();
|
||||
for (let i = 0; i < indices.length; i++) {
|
||||
if (i < indices[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function getOverlappingRange(node, nodes, indexMin, indexMax, returnFirstColumn = false) {
|
||||
const overlapping = [];
|
||||
for (const other of nodes) {
|
||||
if (other.index >= indexMin && other.index <= indexMax && hasOverlap(node.start, node.end, other.start, other.end)) {
|
||||
overlapping.push(other);
|
||||
}
|
||||
}
|
||||
if (returnFirstColumn && overlapping.length > 0) {
|
||||
const first = overlapping.reduce((min, n) => Math.min(min, n.index), overlapping[0].index);
|
||||
return overlapping.filter(n => n.index === first);
|
||||
}
|
||||
return overlapping;
|
||||
}
|
||||
function getParent(node, nodes) {
|
||||
let parent = null;
|
||||
for (const other of nodes) {
|
||||
if (hasOverlap(node.start, node.end, other.start, other.end) && (parent === null || other.index > parent.index)) {
|
||||
parent = other;
|
||||
}
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
function hasFullWidth(node, nodes, overlapThreshold) {
|
||||
for (const other of nodes) {
|
||||
if (other !== node && other.index > node.index && hasOverlap(node.start, addTime(node.start, overlapThreshold), other.start, other.end)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function getGroups(visuals, dayStart) {
|
||||
const groups = [];
|
||||
for (const visual of visuals) {
|
||||
const [start, end] = getNormalizedRange(visual.event, dayStart);
|
||||
let added = false;
|
||||
for (const group of groups) {
|
||||
if (hasOverlap(start, end, group.start, group.end)) {
|
||||
group.visuals.push(visual);
|
||||
group.end = Math.max(group.end, end);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
groups.push({
|
||||
start,
|
||||
end,
|
||||
visuals: [visual]
|
||||
});
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
function getNode(visual, dayStart) {
|
||||
const [start, end] = getNormalizedRange(visual.event, dayStart);
|
||||
return {
|
||||
parent: null,
|
||||
sibling: true,
|
||||
index: 0,
|
||||
visual,
|
||||
start,
|
||||
end,
|
||||
children: []
|
||||
};
|
||||
}
|
||||
function getMaxChildIndex(node) {
|
||||
let max = node.index;
|
||||
for (const child of node.children) {
|
||||
const childMax = getMaxChildIndex(child);
|
||||
if (childMax > max) {
|
||||
max = childMax;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
function addTime(identifier, minutes) {
|
||||
const removeMinutes = identifier % 100;
|
||||
const totalMinutes = removeMinutes + minutes;
|
||||
const addHours = Math.floor(totalMinutes / 60);
|
||||
const addMinutes = totalMinutes % 60;
|
||||
return identifier - removeMinutes + addHours * 100 + addMinutes;
|
||||
}
|
||||
//# sourceMappingURL=stack.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user