From 0cb4e855c67541ff907cb3d6dc6d3e994a6a842a Mon Sep 17 00:00:00 2001 From: Ivymaster Date: Tue, 20 Oct 2026 11:57:27 +0200 Subject: [PATCH] WIP: added print mods to the range mode --- application/helpers/hlp_header_helper.php | 9 +- application/views/Tempus.php | 5 +- public/css/Tempus_print_range.css | 173 ++++++++++++++++++ public/js/components/Calendar/Mode/Range.js | 118 ++++++++++-- .../js/components/Calendar/Mode/Range/View.js | 83 +-------- 5 files changed, 291 insertions(+), 97 deletions(-) create mode 100644 public/css/Tempus_print_range.css diff --git a/application/helpers/hlp_header_helper.php b/application/helpers/hlp_header_helper.php index 41f361bf1..5e2ebaf3f 100644 --- a/application/helpers/hlp_header_helper.php +++ b/application/helpers/hlp_header_helper.php @@ -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; } + } /** diff --git a/application/views/Tempus.php b/application/views/Tempus.php index 30cfd1e98..5ad48cb2d 100644 --- a/application/views/Tempus.php +++ b/application/views/Tempus.php @@ -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); diff --git a/public/css/Tempus_print_range.css b/public/css/Tempus_print_range.css new file mode 100644 index 000000000..ab2cb5071 --- /dev/null +++ b/public/css/Tempus_print_range.css @@ -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; +} diff --git a/public/js/components/Calendar/Mode/Range.js b/public/js/components/Calendar/Mode/Range.js index 153c5737f..0545fe0a0 100644 --- a/public/js/components/Calendar/Mode/Range.js +++ b/public/js/components/Calendar/Mode/Range.js @@ -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*/ `
+
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 */ `