From 0428c27bef05f8e1ad8fd1da1aa40d085994e949 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Wed, 7 Oct 2026 19:10:43 +0200 Subject: [PATCH] =?UTF-8?q?fix=20delete=20dialog=20handling;=20new=20multi?= =?UTF-8?q?select=20mode=20is=20now=20hidden=20behind=20a=20"Gesamtansicht?= =?UTF-8?q?"=20Button=20Switch;=20in=20Gesamtansicht=20remove=20Kontrollda?= =?UTF-8?q?tum=20select=20&=20Alle=20anzeigen=20and=20default=20to=20all?= =?UTF-8?q?=20LV-Teile=20selected;=20fix=20multiselect=20darkmode=20theme;?= =?UTF-8?q?=20align=20all=20=3F=20tooltips=20to=20be=20the=20same=20compon?= =?UTF-8?q?ent=20and=20enhanced=20that=20component=20with=20delayed=20stic?= =?UTF-8?q?ky=20animation,=20then=20moved=20that=20to=20the=20core=20so=20?= =?UTF-8?q?we=20can=20ALL=20have=20fancy=20sticky=20tooltips;=20checked=20?= =?UTF-8?q?over=20existing=20phrasen=20for=20spelling=20mistakes;=20bunch?= =?UTF-8?q?=20more=20css=20adjustments;=20fixed=20a=20possible=20bug=20in?= =?UTF-8?q?=20getAnwForUid=20where=20only=20uid=20was=20verified=20and=20n?= =?UTF-8?q?ot=20person=5Fid;=20renamed=20timeline=20tab=20and=20enabled=20?= =?UTF-8?q?it=20for=20students=20aswell=20->=20configurable;=20?= =?UTF-8?q?=F0=9F=A4=93;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/components/inViewTooltip.css | 81 +++ public/css/theme/default.css | 45 ++ public/js/components/InViewHelp.js | 34 ++ public/js/directives/inViewTooltip.js | 352 ++++++++++++ system/phrasesupdate.php | 736 ++++++++++++++++++++++++ 5 files changed, 1248 insertions(+) create mode 100644 public/css/components/inViewTooltip.css create mode 100644 public/js/components/InViewHelp.js create mode 100644 public/js/directives/inViewTooltip.js diff --git a/public/css/components/inViewTooltip.css b/public/css/components/inViewTooltip.css new file mode 100644 index 000000000..600d59302 --- /dev/null +++ b/public/css/components/inViewTooltip.css @@ -0,0 +1,81 @@ +/* the panel of js/directives/inViewTooltip.js. place() sets its place, max width and max height + inside the viewport, so neither a scrolling container nor a modal body clips it */ +.fhc-in-view-tooltip { + --fhc-in-view-tooltip-progress: var(--fhc-blue-primary, #0d6efd); + position: fixed; + z-index: 1080; + display: flex; + flex-direction: column; + width: max-content; + border: 1px solid var(--bs-border-color, #dee2e6); + border-radius: 0.375rem; + background-color: var(--bs-body-bg, #fff); + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + color: var(--bs-body-color, #212529); + font-size: 0.875rem; + font-weight: normal; + line-height: 1.5; + text-align: left; +} + +/* the dark primary reaches 2:1 on the dark panel, the highlight 3.9:1 */ +html.dark .fhc-in-view-tooltip { + --fhc-in-view-tooltip-progress: var(--fhc-blue-primary-highlight, #6ea8fe); +} + +/* the body scrolls, so the progress border stays on the panel edge */ +.fhc-in-view-tooltip-body { + min-height: 0; + overflow-y: auto; + padding: 0.75rem 1rem; + white-space: pre-line; /* the text keeps its line breaks */ +} + +/* the hover timer of a sticky panel: after a pause the border fills up clockwise, then the panel sticks. + The 2 px stroke covers the 1 px border and 1 px inside it */ +.fhc-in-view-tooltip-progress { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + overflow: visible; + pointer-events: none; +} + +.fhc-in-view-tooltip-progress rect { + fill: none; + stroke: var(--fhc-in-view-tooltip-progress); + stroke-width: 2; + stroke-dasharray: var(--fhc-in-view-tooltip-perimeter); + stroke-dashoffset: var(--fhc-in-view-tooltip-perimeter); +} + +.fhc-in-view-tooltip--holding .fhc-in-view-tooltip-progress rect { + animation: fhc-in-view-tooltip-hold var(--fhc-in-view-tooltip-hold-fill) ease-in var(--fhc-in-view-tooltip-hold-delay) forwards; +} + +/* a click during the hover timer fills the rest of the border quickly */ +.fhc-in-view-tooltip--pinned .fhc-in-view-tooltip-progress rect { + stroke-dashoffset: 0; + transition: stroke-dashoffset 0.15s linear; +} + +@keyframes fhc-in-view-tooltip-hold { + to { + stroke-dashoffset: 0; + } +} + +/* the ? icon of components/InViewHelp.js takes the color of its context, e.g. white in the blue + modal header. A class like text-muted overrides it */ +.fhc-in-view-help-btn, +.fhc-in-view-help-btn:hover, +.fhc-in-view-help-btn:focus-visible, +.btn.fhc-in-view-help-btn:active { + color: inherit; +} + +.fhc-in-view-help-btn { + line-height: 1; + text-decoration: none; +} diff --git a/public/css/theme/default.css b/public/css/theme/default.css index c2a584c12..cb5530c87 100644 --- a/public/css/theme/default.css +++ b/public/css/theme/default.css @@ -333,6 +333,25 @@ html.dark .btn-danger:hover { filter: brightness(90%); } +/* the dark primevue theme draws p-button-danger (e.g. FhcAlert.confirmDelete) light pink with black text, + * use the dark red of .btn-danger */ +html.dark .p-button.p-button-danger { + color: white; + background: var(--fhc-red-60); + border-color: var(--fhc-red-60); +} + +html.dark .p-button.p-button-danger:enabled:hover, +html.dark .p-button.p-button-danger:enabled:active { + color: white; + background: var(--fhc-red-70); + border-color: var(--fhc-red-70); +} + +html.dark .p-button.p-button-danger:enabled:focus { + box-shadow: 0 0 0 0.2rem rgba(225, 110, 120, 0.5); +} + html.dark .text-danger { color: var(--fhc-danger) !important; } @@ -364,6 +383,32 @@ html.dark .btn-outline-secondary { --bs-btn-active-border-color: var(--fhc-white-90); } +/* the primevue multiselect renders the check icon also in unchecked boxes and hides it by color: + * white on white in the light theme, but #151515 on #20262e in the dark theme */ +html.dark .p-checkbox .p-checkbox-box:not(.p-highlight) .p-checkbox-icon { + color: transparent; +} + +/* FhcAlert.confirm passes bootstrap classes to the primevue buttons. The primevue theme loads after + * bootstrap, its .p-button colors replace the .btn colors: every button blue */ +.p-button.btn { + color: var(--bs-btn-color); + background: var(--bs-btn-bg); + border-color: var(--bs-btn-border-color); +} + +.p-button.btn:enabled:hover { + color: var(--bs-btn-hover-color); + background: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); +} + +.p-button.btn:enabled:active { + color: var(--bs-btn-active-color); + background: var(--bs-btn-active-bg); + border-color: var(--bs-btn-active-border-color); +} + html.dark .tabulator, html.dark .tabulator .tabulator-header, diff --git a/public/js/components/InViewHelp.js b/public/js/components/InViewHelp.js new file mode 100644 index 000000000..2f2586828 --- /dev/null +++ b/public/js/components/InViewHelp.js @@ -0,0 +1,34 @@ +import InViewTooltip from '../directives/inViewTooltip.js'; + +/** + * ? icon with a help text in the in-view tooltip. The panel stays inside the viewport, also at the + * screen edge, in a modal and on a phone: a plain tooltip ran out of the screen there. + * + * Help texts are long, so the panel is sticky by default: it sticks after a hover of 1.4 s or a click, + * a tap toggles it on touch. sticky: false makes it a plain hover tooltip. + * + * The view needs public/css/components/inViewTooltip.css. + * + * Usage: + */ +export default { + name: 'InViewHelp', + directives: { + tooltip: InViewTooltip + }, + props: { + text: { type: String, default: '' }, + sticky: { type: Boolean, default: true }, + buttonClass: { type: [String, Array, Object], default: '' } + }, + template: /*html*/ ` + ` +}; diff --git a/public/js/directives/inViewTooltip.js b/public/js/directives/inViewTooltip.js new file mode 100644 index 000000000..023d15755 --- /dev/null +++ b/public/js/directives/inViewTooltip.js @@ -0,0 +1,352 @@ +/** + * v-tooltip with a panel that stays in view, also at the screen edge, in a modal and on a phone. + * + * - The panel is fixed and stays inside the viewport. It opens below the anchor, above it when + * it fits only there or that side has more space. It grows away from the nearer viewport side. + * - On an anchor wider than the panel, e.g. a long table cell, it opens at the mouse, not at the anchor edge. + * - One panel at a time, appended to the body. A modal or a scrolling container cannot clip it. + * - A mouse hover or a keyboard focus opens it. Escape, a click outside or a scroll closes it. + * + * Sticky, for several sentences or a list: a short label is read before the hover pause ends, a long text is not. + * - After HOLD_DELAY_MS + HOLD_FILL_MS of hovering the panel sticks, the border fills up in the end. + * A click on the anchor makes it stick at once. Touch has no hover: a tap toggles the sticky panel. + * - A sticky panel stays until a click outside, Escape, a scroll or an opening bootstrap modal. + * + * The view needs public/css/components/inViewTooltip.css. + * + * Usage examples: + * + * 1) Directive, registered for the app or the component: + * app.directive('tooltip', InViewTooltip) or directives: { tooltip: InViewTooltip } + *