WIP: added print mods to the range mode

This commit is contained in:
Ivymaster
2026-10-20 11:57:27 +02:00
parent 7718c4aea6
commit 0cb4e855c6
5 changed files with 291 additions and 97 deletions
+8 -1
View File
@@ -92,9 +92,16 @@ function generatePrintCSS($path)
$ci =& get_instance();
$cachetoken = '?'.$ci->config->item('fhcomplete_build_version');
if (isset($path)) {
if (is_array($path)) {
foreach ($path as $p) {
echo sprintf($cssLink, base_url($p).$cachetoken) . PHP_EOL;
}
} else if (isset($path)) {
echo sprintf($cssLink, base_url($path).$cachetoken) . PHP_EOL;
}
}
/**
+4 -1
View File
@@ -31,7 +31,10 @@
'customJSModules' => [
'public/js/apps/Tempus.js'
],
'printCssPath' => 'public/css/Tempus_print.css'
'printCssPath' => [
'public/css/Tempus_print.css',
'public/css/Tempus_print_range.css'
]
);
$this->load->view('templates/FHC-Header', $includesArray);
+173
View File
@@ -0,0 +1,173 @@
@page {
size: landscape;
}
* {
background-color: white !important;
color: black !important;
font-size: x-small !important;
overflow: visible !important;
}
html,
body,
#main,
.tempus {
height: auto !important;
min-height: 0 !important;
max-height: none !important;
}
.fhc-calendar-base-slider > div {
position: relative !important;
}
header,
#skiplink,
#cis-lvplan-personal-heading,
#sidebarMenu,
.cis-lvplan-personal hr,
.tempus .horizontalsplit-container > .horizontalsplitted:first-child,
.tempus .horizontalsplitter,
.fhc-calendar-base-header,
.fhc-calendar-mode-range .grid-allday,
.fhc-calendar-mode-range #rangePresetSelector,
.fhc-calendar-base-grid-dropzone,
.fhc-calendar-base-grid .main-header .form-check,
.fhc-calendar-base-grid-line-background,
.fhc-calendar-base-grid-line-background > div,
.p-tooltip {
display: none !important;
}
.tempus .container-fluid,
.tempus .container-fluid > .row,
.tempus .horizontalsplit-container {
display: block !important;
width: 100% !important;
max-width: none !important;
height: auto !important;
max-height: none !important;
}
.tempus .horizontalsplit-container > .horizontalsplitted:last-child {
display: block !important;
width: 100% !important;
max-width: none !important;
height: auto !important;
max-height: none !important;
flex: 0 0 100% !important;
}
.fhc-calendar-mode-range,
.fhc-calendar-mode-range > .flex-grow-1,
.fhc-calendar-mode-range .fhc-calendar-base-slider {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
}
.fhc-calendar-mode-range .fhc-calendar-mode-range-view,
.fhc-calendar-mode-range .fhc-calendar-mode-range-view > div {
width: 100% !important;
height: 100vh !important;
min-width: 0 !important;
overflow: visible !important;
}
.fhc-calendar-mode-range .fhc-calendar-mode-range-view > div {
width: var(--fhc-calendar-print-layout-width, 100%) !important;
transform: scale(
var(--fhc-calendar-print-scale-x, 1),
var(--fhc-calendar-print-scale-y, 1)
) !important;
transform-origin: top left !important;
}
.fhc-calendar-print-measure {
position: fixed !important;
visibility: hidden !important;
width: 100vw !important;
height: 0 !important;
pointer-events: none !important;
}
.fhc-calendar-mode-range .fhc-calendar-mode-range-view > div {
break-after: page !important;
page-break-after: always !important;
break-inside: avoid-page !important;
page-break-inside: avoid !important;
}
.fhc-calendar-mode-range .fhc-calendar-base-grid {
width: 100% !important;
grid-template-rows: auto minmax(0, 1fr) !important;
gap: 1px !important;
background-color: #b8b8b8 !important;
}
.fhc-calendar-mode-range .fhc-calendar-base-grid .grid-header,
.fhc-calendar-mode-range .fhc-calendar-base-grid .grid-main,
.fhc-calendar-mode-range .fhc-calendar-base-grid .grid-body {
gap: 1px !important;
background-color: #b8b8b8 !important;
}
.fhc-calendar-mode-range .fhc-calendar-base-grid > #grid-main-scrollable {
border-top: solid 1px #b8b8b8 !important;
grid-row: 2 !important;
height: 100% !important;
min-height: 0 !important;
}
.fhc-calendar-mode-range .fhc-calendar-base-grid .grid-main,
.fhc-calendar-mode-range .fhc-calendar-base-grid .grid-body {
height: 100% !important;
min-height: 0 !important;
align-self: stretch !important;
}
.fhc-calendar-mode-range .fhc-calendar-base-grid .grid-header:before,
.fhc-calendar-mode-range .fhc-calendar-base-grid .main-header,
.fhc-calendar-mode-range .fhc-calendar-base-grid .part-header,
.fhc-calendar-mode-range .fhc-calendar-base-grid .part-body {
background-color: white !important;
border: 0 !important;
}
.fhc-calendar-mode-range .fhc-calendar-base-grid .grid-header:before {
grid-column: 1 !important;
grid-row: 1 !important;
}
.fhc-calendar-mode-range .main-header:not(.main-header-empty) {
padding-top: 0.25rem !important;
}
.fhc-calendar-mode-range .fhc-calendar-base-grid-line {
background-color: transparent !important;
border: 0 !important;
overflow: visible !important;
}
.fhc-calendar-mode-range .fhc-calendar-base-grid-line-event {
break-inside: avoid !important;
margin: 1px !important;
page-break-inside: avoid !important;
z-index: auto !important;
}
.fhc-calendar-mode-range
.fhc-calendar-base-grid-line-event
> .rangePageContainer {
background-color: var(--event-bg, var(--event-default-bg)) !important;
break-inside: avoid !important;
page-break-inside: avoid !important;
}
.fhc-calendar-mode-range
.fhc-calendar-base-grid-line-event
> .rangePageContainer
* {
background-color: transparent !important;
}
+100 -18
View File
@@ -1,9 +1,9 @@
import BaseSlider from "../Base/Slider.js";
import RangeView from "./Range/View.js";
import FormInput from "../../Form/Input.js";
import BaseSlider from '../Base/Slider.js';
import RangeView from './Range/View.js';
import FormInput from '../../Form/Input.js';
export default {
name: "ModeRange",
name: 'ModeRange',
components: {
BaseSlider,
RangeView,
@@ -26,7 +26,7 @@ export default {
required: true,
},
},
emits: ["update:currentDate", "update:range", "click", "requestModalOpen"],
emits: ['update:currentDate', 'update:range', 'click', 'requestModalOpen'],
data() {
return {
selectedRangePreset: null,
@@ -34,18 +34,21 @@ export default {
},
computed: {
range() {
let first = this.$props.currentDate;
let last = first.plus({ days: this.rangeLength });
const rangeLength = Math.max(parseInt(this.rangeLength) || 1, 1);
const first = this.currentDate.startOf('week', { useLocaleWeeks: true });
const last = this.currentDate
.plus({ days: rangeLength - 1 })
.endOf('week', { useLocaleWeeks: true });
return luxon.Interval.fromDateTimes(first, last);
},
},
watch: {
currentDate() {
this.$emit("update:range", this.range);
this.$emit('update:range', this.range);
},
rangeLength() {
this.$emit("update:range", this.range);
this.$emit('update:range', this.range);
},
selectedRangePreset() {
if (!this.selectedRangePreset) return;
@@ -55,10 +58,9 @@ export default {
);
if (!preset) return;
this.$emit("update:date", {
this.$emit('update:date', {
date: preset.startDate,
rangeLength:
preset.endDate.diff(preset.startDate, "days").days + 1,
rangeLength: preset.endDate.diff(preset.startDate, 'days').days + 1,
});
this.selectedRangePreset = null;
@@ -66,33 +68,113 @@ export default {
},
methods: {
viewAttrs() {
const day = this.$props.currentDate.startOf("day");
const day = this.$props.currentDate.startOf('day');
const rangeLength = parseInt(this.rangeLength);
return { ...this.$attrs, day, rangeLength };
},
handleClickDefaults(evt) {
switch (evt.detail.source) {
case "day":
case 'day':
// default: Set current-date
this.$emit("update:currentDate", {
this.$emit('update:currentDate', {
date: evt.detail.value,
});
break;
case "event":
case 'event':
// default: Request Modal
this.$emit("requestModalOpen", { event: evt.detail.value });
this.$emit('requestModalOpen', { event: evt.detail.value });
break;
}
},
preparePrintLayout() {
const view = this.$el?.querySelector('.fhc-calendar-mode-range-view');
if (!view) return;
const weeks = Array.from(view.children);
weeks.forEach((week) => {
week.style.setProperty('--fhc-calendar-print-scale-x', '1');
week.style.setProperty('--fhc-calendar-print-scale-y', '1');
week.style.setProperty(
'--fhc-calendar-print-layout-width',
'max-content',
);
});
const viewWidth = view.getBoundingClientRect().width;
const printViewportWidth = document
.querySelector('.fhc-calendar-base-slider')
.getBoundingClientRect().width;
const availableWidth = printViewportWidth
? Math.min(viewWidth, printViewportWidth)
: viewWidth;
if (!availableWidth) return;
weeks.forEach((week) => {
const grid = week.querySelector('.fhc-calendar-base-grid');
if (!grid) return;
const gridRect = grid.getBoundingClientRect();
const contentRight = Array.from(grid.querySelectorAll('*')).reduce(
(right, element) =>
Math.max(right, element.getBoundingClientRect().right),
gridRect.right,
);
const gridWidth = Math.ceil(
Math.max(
week.scrollWidth,
grid.scrollWidth,
gridRect.width,
contentRight - gridRect.left,
),
);
if (!gridWidth) return;
const scale = Math.min(1, availableWidth / gridWidth);
const verticalScale = 1 - (1 - scale) * 0.9;
week.style.setProperty(
'--fhc-calendar-print-scale-x',
scale.toFixed(4),
);
week.style.setProperty(
'--fhc-calendar-print-scale-y',
verticalScale.toFixed(4),
);
week.style.setProperty(
'--fhc-calendar-print-layout-width',
gridWidth + 'px',
);
});
},
resetPrintLayout() {
const view = this.$el?.querySelector('.fhc-calendar-mode-week-view');
if (!view) return;
Array.from(view.children).forEach((week) => {
week.style.removeProperty('--fhc-calendar-print-scale-x');
week.style.removeProperty('--fhc-calendar-print-scale-y');
week.style.removeProperty('--fhc-calendar-print-layout-width');
});
},
},
mounted() {
this.$emit("update:range", this.range);
this.$emit('update:range', this.range);
this.beforePrintHandler = () => this.preparePrintLayout();
this.afterPrintHandler = () => this.resetPrintLayout();
window.addEventListener('beforeprint', this.beforePrintHandler);
window.addEventListener('afterprint', this.afterPrintHandler);
},
beforeUnmount() {
window.removeEventListener('beforeprint', this.beforePrintHandler);
window.removeEventListener('afterprint', this.afterPrintHandler);
},
template: /*html*/ `
<div
class="fhc-calendar-mode-range flex-grow-1 position-relative d-flex flex-column"
@cal-click-default.capture="handleClickDefaults"
>
<div ref="printMeasure" class="fhc-calendar-print-measure" aria-hidden="true"></div>
<div
v-if="rangeViewPresetsConfig?.presets?.length"
id="rangePresetSelector"
@@ -38,7 +38,7 @@ export default {
},
data() {
return {
visibleDays: JSON.parse(
dayVisibilityStates: JSON.parse(
localStorage.getItem('fhc-calender-visible-days'),
) ?? [true, true, true, true, true, true, true],
gridWidthEqualizingData: {
@@ -48,11 +48,6 @@ export default {
syncing: false,
isActive: false,
},
gridHeightEqualizingData: {
resizeObserver: null,
gridHeights: [],
observedGrids: Vue.markRaw(new Set()),
},
gridColumnWidthEqualizingData: {
sharedDayWidths: null,
largestDayWidths: null,
@@ -128,21 +123,16 @@ export default {
},
);
},
dayVisibility() {
return this.daysGroupedByWeek[this.weekStartDays[0]].map(
(date) => this.visibleDays[date.weekday - 1],
);
},
},
methods: {
isToday(date) {
return date.hasSame(luxon.DateTime.now().setZone(this.timezone), 'day');
},
toggleDayVisibility(day) {
this.visibleDays.splice(day, 1, !this.visibleDays[day]);
this.dayVisibilityStates.splice(day, 1, !this.dayVisibilityStates[day]);
localStorage.setItem(
'fhc-calender-visible-days',
JSON.stringify(this.visibleDays),
JSON.stringify(this.dayVisibilityStates),
);
this.$nextTick(() => this.scheduleGridWidthSync());
},
@@ -281,55 +271,6 @@ export default {
grid.style.removeProperty('min-width'),
);
},
resetGridHeightsForEventReload() {
this.gridHeightEqualizingData.gridHeights = [];
this.$nextTick(() => this.initializeGridHeights());
},
initializeGridHeights() {
const grids = this.getGridElements();
const firstGrid = grids[0];
if (!firstGrid) return;
const initialGridHeight = Math.ceil(
firstGrid.getBoundingClientRect().height,
);
if (!Number.isFinite(initialGridHeight) || initialGridHeight <= 0) return;
this.gridHeightEqualizingData.gridHeights = grids.map(
() => initialGridHeight,
);
this.$nextTick(() => this.syncGridResizeObservation());
},
syncGridResizeObservation() {
const grids = this.getGridElements();
const currentGrids = new Set(grids);
this.gridHeightEqualizingData.observedGrids.forEach((grid) => {
if (!currentGrids.has(grid)) {
this.gridHeightEqualizingData?.resizeObserver.unobserve(grid);
this.gridHeightEqualizingData.observedGrids.delete(grid);
}
});
if (this.gridHeightEqualizingData.gridHeights.length > grids.length)
this.gridHeightEqualizingData.gridHeights.splice(grids.length);
grids.forEach((grid, index) => {
if (!this.gridHeightEqualizingData.observedGrids.has(grid)) {
this.gridHeightEqualizingData.resizeObserver.observe(grid);
this.gridHeightEqualizingData.observedGrids.add(grid);
this.setGridHeight(
index,
Math.ceil(grid.getBoundingClientRect().height),
);
}
});
},
measureAndRecordGridHeight(grid) {
const index = this.getGridElements().indexOf(grid);
if (index === -1) return;
this.setGridHeight(index, Math.ceil(grid.getBoundingClientRect().height));
},
setGridHeight(index, height) {
if (!Number.isFinite(height) || height <= 0) return;
if (this.gridHeightEqualizingData.gridHeights[index] !== height)
this.gridHeightEqualizingData.gridHeights.splice(index, 1, height);
},
resetColumnWidthAdjustment() {
this.gridColumnWidthEqualizingData.sharedDayWidths = null;
this.gridColumnWidthEqualizingData.largestDayWidths = null;
@@ -360,7 +301,7 @@ export default {
flushFreshColumnWidthSync() {
if (
!this.gridColumnWidthEqualizingData.syncRequested ||
this.gridWidthEqualizingData.syncing
this.gridColumnWidthEqualizingData.syncing
)
return;
this.gridColumnWidthEqualizingData.syncRequested = false;
@@ -400,7 +341,6 @@ export default {
);
if (!relevantMutations.length) return;
this.$nextTick(() => {
this.syncGridResizeObservation();
if (
!this.gridColumnWidthEqualizingData.initialEventsSeen &&
this.hasColumnWidthEvents()
@@ -418,15 +358,6 @@ export default {
subtree: true,
});
this.gridHeightEqualizingData.resizeObserver = Vue.markRaw(
new ResizeObserver((entries) => {
entries.forEach((entry) =>
this.measureAndRecordGridHeight(entry.target),
);
}),
);
this.initializeGridHeights();
if (this.hasColumnWidthEvents()) {
this.gridColumnWidthEqualizingData.initialEventsSeen = true;
this.startFreshColumnWidthSync();
@@ -445,8 +376,6 @@ export default {
cancelAnimationFrame(
this.gridColumnWidthEqualizingData.initialSyncAnimationFrame,
);
this.gridHeightEqualizingData?.resizeObserver?.disconnect();
},
template: /* html */ `
<div
@@ -468,7 +397,7 @@ export default {
weekStartDay in eventsGroupedByWeek ? eventsGroupedByWeek[weekStartDay] : []
"
:shouldMatchParentHeight="false"
:day-visibility="dayVisibility"
:day-visibility="dayVisibilityStates"
:sticky-header="isHeaderSticky"
:scrollable="false"
:main-column-widths="gridColumnWidthEqualizingData.sharedDayWidths"
@@ -485,7 +414,7 @@ export default {
/>
<form-input
type="checkbox"
:checked="visibleDays[date.weekday - 1]"
:checked="dayVisibilityStates[date.weekday - 1]"
@change="toggleDayVisibility(date.weekday - 1)"
@click.stop
>