entwurf kennzeichnen

This commit is contained in:
Simon Martens
2025-06-03 11:20:46 +02:00
parent 780a365301
commit d9727d10ef
9 changed files with 279 additions and 241 deletions

View File

@@ -0,0 +1,5 @@
package functions
func Minus(a, b int) int {
return a - b
}

View File

@@ -124,6 +124,9 @@ func (e *Engine) funcs() error {
e.AddFunc("Today", functions.Today) e.AddFunc("Today", functions.Today)
e.AddFunc("GetMonth", functions.GetMonth) e.AddFunc("GetMonth", functions.GetMonth)
// Math
e.AddFunc("Minus", functions.Minus)
return nil return nil
} }

View File

@@ -15370,76 +15370,6 @@ class Previewer {
} }
} }
EventEmitter(Previewer.prototype); EventEmitter(Previewer.prototype);
const ATTR_XSLT_ONLOAD = "script[xslt-onload]", ATTR_XSLT_TEMPLATE = "xslt-template", ATTR_XSLT_STATE = "xslt-transformed", SCROLL_BUTTON_ELEMENT = "scroll-button", TOOLTIP_ELEMENT = "tool-tip";
var ji, oa, ro;
class XSLTParseProcess {
constructor() {
Da(this, oa);
Da(this, ji);
eo(this, ji, /* @__PURE__ */ new Map());
}
setup() {
let ze = htmx.findAll(ATTR_XSLT_ONLOAD);
for (let Wr of ze)
to(this, oa, ro).call(this, Wr);
}
hookupHTMX() {
htmx.on("htmx:load", (ze) => {
this.setup();
});
}
}
ji = new WeakMap(), oa = new WeakSet(), ro = function(ze) {
if (ze.getAttribute(ATTR_XSLT_STATE) === "true" || !ze.hasAttribute(ATTR_XSLT_TEMPLATE))
return;
let Wr = "#" + ze.getAttribute(ATTR_XSLT_TEMPLATE), Yr = Na(this, ji).get(Wr);
if (!Yr) {
let en = htmx.find(Wr);
if (en) {
let tn = en.innerHTML ? new DOMParser().parseFromString(en.innerHTML, "application/xml") : en.contentDocument;
Yr = new XSLTProcessor(), Yr.importStylesheet(tn), Na(this, ji).set(Wr, Yr);
} else
throw new Error("Unknown XSLT template: " + Wr);
}
let Qr = new DOMParser().parseFromString(ze.innerHTML, "application/xml"), Kr = Yr.transformToFragment(Qr, document), Zr = new XMLSerializer().serializeToString(Kr);
ze.outerHTML = Zr;
};
class ScrollButton extends HTMLElement {
constructor() {
super(), this.handleScroll = this.handleScroll.bind(this), this.scrollToTop = this.scrollToTop.bind(this);
}
connectedCallback() {
this.innerHTML = `
<button
class="
scroll-to-top
fixed bottom-5 right-5
hidden
bg-gray-800 text-white
p-2
rounded-md
cursor-pointer
text-2xl
hover:opacity-80
transition-opacity
border-0
"
aria-label="Scroll to top"
>
<i class="ri-arrow-up-double-line"></i>
</button>
`, this._button = this.querySelector(".scroll-to-top"), window.addEventListener("scroll", this.handleScroll), this._button.addEventListener("click", this.scrollToTop);
}
disconnectedCallback() {
window.removeEventListener("scroll", this.handleScroll), this._button.removeEventListener("click", this.scrollToTop);
}
handleScroll() {
(window.scrollY || document.documentElement.scrollTop) > 300 ? this._button.classList.remove("hidden") : this._button.classList.add("hidden");
}
scrollToTop() {
window.scrollTo({ top: 0, behavior: "smooth" });
}
}
class ToolTip extends HTMLElement { class ToolTip extends HTMLElement {
static get observedAttributes() { static get observedAttributes() {
return ["position", "timeout"]; return ["position", "timeout"];
@@ -15448,14 +15378,7 @@ class ToolTip extends HTMLElement {
super(), this._tooltipBox = null, this._timeout = 200, this._hideTimeout = null, this._hiddenTimeout = null; super(), this._tooltipBox = null, this._timeout = 200, this._hideTimeout = null, this._hiddenTimeout = null;
} }
connectedCallback() { connectedCallback() {
this.classList.add( this.classList.add("relative", "block", "leading-none", "[&>*]:leading-normal");
"w-full",
"h-full",
"relative",
"block",
"leading-none",
"[&>*]:leading-normal"
);
const ze = this.querySelector(".data-tip"), Wr = ze ? ze.innerHTML : "Tooltip"; const ze = this.querySelector(".data-tip"), Wr = ze ? ze.innerHTML : "Tooltip";
ze && ze.classList.add("hidden"), this._tooltipBox = document.createElement("div"), this._tooltipBox.innerHTML = Wr, this._tooltipBox.className = [ ze && ze.classList.add("hidden"), this._tooltipBox = document.createElement("div"), this._tooltipBox.innerHTML = Wr, this._tooltipBox.className = [
"opacity-0", "opacity-0",
@@ -15548,6 +15471,76 @@ class ToolTip extends HTMLElement {
} }
} }
} }
const ATTR_XSLT_ONLOAD = "script[xslt-onload]", ATTR_XSLT_TEMPLATE = "xslt-template", ATTR_XSLT_STATE = "xslt-transformed", SCROLL_BUTTON_ELEMENT = "scroll-button", TOOLTIP_ELEMENT = "tool-tip";
var ji, oa, ro;
class XSLTParseProcess {
constructor() {
Da(this, oa);
Da(this, ji);
eo(this, ji, /* @__PURE__ */ new Map());
}
setup() {
let ze = htmx.findAll(ATTR_XSLT_ONLOAD);
for (let Wr of ze)
to(this, oa, ro).call(this, Wr);
}
hookupHTMX() {
htmx.on("htmx:load", (ze) => {
this.setup();
});
}
}
ji = new WeakMap(), oa = new WeakSet(), ro = function(ze) {
if (ze.getAttribute(ATTR_XSLT_STATE) === "true" || !ze.hasAttribute(ATTR_XSLT_TEMPLATE))
return;
let Wr = "#" + ze.getAttribute(ATTR_XSLT_TEMPLATE), Yr = Na(this, ji).get(Wr);
if (!Yr) {
let en = htmx.find(Wr);
if (en) {
let tn = en.innerHTML ? new DOMParser().parseFromString(en.innerHTML, "application/xml") : en.contentDocument;
Yr = new XSLTProcessor(), Yr.importStylesheet(tn), Na(this, ji).set(Wr, Yr);
} else
throw new Error("Unknown XSLT template: " + Wr);
}
let Qr = new DOMParser().parseFromString(ze.innerHTML, "application/xml"), Kr = Yr.transformToFragment(Qr, document), Zr = new XMLSerializer().serializeToString(Kr);
ze.outerHTML = Zr;
};
class ScrollButton extends HTMLElement {
constructor() {
super(), this.handleScroll = this.handleScroll.bind(this), this.scrollToTop = this.scrollToTop.bind(this);
}
connectedCallback() {
this.innerHTML = `
<button
class="
scroll-to-top
fixed bottom-5 right-5
hidden
bg-gray-800 text-white
p-2
rounded-md
cursor-pointer
text-2xl
hover:opacity-80
transition-opacity
border-0
"
aria-label="Scroll to top"
>
<i class="ri-arrow-up-double-line"></i>
</button>
`, this._button = this.querySelector(".scroll-to-top"), window.addEventListener("scroll", this.handleScroll), this._button.addEventListener("click", this.scrollToTop);
}
disconnectedCallback() {
window.removeEventListener("scroll", this.handleScroll), this._button.removeEventListener("click", this.scrollToTop);
}
handleScroll() {
(window.scrollY || document.documentElement.scrollTop) > 300 ? this._button.classList.remove("hidden") : this._button.classList.add("hidden");
}
scrollToTop() {
window.scrollTo({ top: 0, behavior: "smooth" });
}
}
function Startup() { function Startup() {
let Gr = null; let Gr = null;
const ze = []; const ze = [];

File diff suppressed because one or more lines are too long

View File

@@ -6,31 +6,47 @@
<div class="mr-4 text-6xl">{{ $model.Letter }}</div> <div class="mr-4 text-6xl">{{ $model.Letter }}</div>
--> -->
<div class="pt-0.5"> <div class="pt-0.5">
<div class="flex flex-row">
<div class="italic">{{ $model.Earliest.Text -}}</div> <div class="italic">{{ $model.Earliest.Text -}}</div>
{{- if $model.IsDraft.IsTrue -}}
<tool-tip position="top">
<div class="ml-3 text-gray-500 align-middle"><i class="ri-draft-line"></i></div>
<div class="data-tip">Entwurf</div>
</tool-tip>
{{- end -}}
</div>
{{- range $sr := $model.SendReceivedPairs -}} {{- range $sr := $model.SendReceivedPairs -}}
<div class="flex flex-row"> <div class="flex flex-row">
<div> <div>
{{- range $i, $p := $sr.Sent.Persons -}} {{- range $i, $p := $sr.Sent.Persons -}}
<div class="inline-block"> {{ if and $i (eq $i (Minus (len $sr.Sent.Persons) 1)) }}
{{- if $i -}} und
{{ else if $i }}
, ,
{{ end -}} {{ end }}
{{- $person := Person $p.Reference -}} {{- $person := Person $p.Reference -}}
{{- $person.Name -}} {{- $person.Name -}}
</div>
{{- end -}} {{- end -}}
</div> </div>
<div class="mx-2"><i class="ri-arrow-right-long-line"></i></div> <div class="mx-3">
{{- if $model.IsDraft.IsTrue -}}
<div class="diagonal-strike">
<i class="ri-arrow-right-long-line"></i>
</div>
{{- else -}}
<i class="ri-arrow-right-long-line"></i>
{{- end -}}
</div>
{{- if $sr.Received -}} {{- if $sr.Received -}}
<div> <div>
{{- range $i, $p := $sr.Received.Persons -}} {{- range $i, $p := $sr.Received.Persons -}}
<div class="inline-block"> {{ if and $i (eq $i (Minus (len $sr.Received.Persons) 1)) }}
{{- if $i -}} und
{{ else if $i }}
, ,
{{ end -}} {{ end }}
{{- $person := Person $p.Reference -}} {{- $person := Person $p.Reference -}}
{{- $person.Name -}} {{- $person.Name -}}
</div>
{{- end -}} {{- end -}}
</div> </div>
{{- else -}} {{- else -}}

View File

@@ -5,6 +5,7 @@ import "../public/js/alpine.min.js";
import "../public/js/htmx.min.js"; import "../public/js/htmx.min.js";
import "../public/js/htmx-response-targets.js"; import "../public/js/htmx-response-targets.js";
import { Previewer } from "pagedjs"; import { Previewer } from "pagedjs";
import { ToolTip } from "./tool-tip.js";
const ATTR_XSLT_ONLOAD = "script[xslt-onload]"; const ATTR_XSLT_ONLOAD = "script[xslt-onload]";
const ATTR_XSLT_TEMPLATE = "xslt-template"; const ATTR_XSLT_TEMPLATE = "xslt-template";
@@ -157,152 +158,6 @@ class ScrollButton extends HTMLElement {
} }
} }
class ToolTip extends HTMLElement {
static get observedAttributes() {
return ["position", "timeout"];
}
constructor() {
super();
this._tooltipBox = null;
this._timeout = 200;
this._hideTimeout = null;
this._hiddenTimeout = null;
}
connectedCallback() {
this.classList.add(
"w-full",
"h-full",
"relative",
"block",
"leading-none",
"[&>*]:leading-normal",
);
const dataTipElem = this.querySelector(".data-tip");
const tipContent = dataTipElem ? dataTipElem.innerHTML : "Tooltip";
if (dataTipElem) {
dataTipElem.classList.add("hidden");
}
this._tooltipBox = document.createElement("div");
this._tooltipBox.innerHTML = tipContent;
this._tooltipBox.className = [
"opacity-0",
"hidden",
"absolute",
"px-2",
"py-1",
"text-sm",
"text-white",
"bg-gray-900",
"rounded",
"shadow",
"z-10",
"whitespace-nowrap",
"transition-all",
"duration-200",
"font-sans",
].join(" ");
this.appendChild(this._tooltipBox);
this._updatePosition();
this.addEventListener("mouseenter", () => this._showTooltip());
this.addEventListener("mouseleave", () => this._hideTooltip());
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "position" && this._tooltipBox) {
this._updatePosition();
}
if (name === "timeout" && newValue) {
this._timeout = parseInt(newValue) || 200;
}
}
_showTooltip() {
clearTimeout(this._hideTimeout);
clearTimeout(this._hiddenTimeout);
this._tooltipBox.classList.remove("hidden");
setTimeout(() => {
this._tooltipBox.classList.remove("opacity-0");
this._tooltipBox.classList.add("opacity-100");
}, 16);
}
_hideTooltip() {
this._hideTimeout = setTimeout(() => {
this._tooltipBox.classList.remove("opacity-100");
this._tooltipBox.classList.add("opacity-0");
this._hiddenTimeout = setTimeout(() => {
this._tooltipBox.classList.add("hidden");
}, this._timeout + 100);
}, this._timeout);
}
_updatePosition() {
this._tooltipBox.classList.remove(
"bottom-full",
"left-1/2",
"-translate-x-1/2",
"mb-2", // top
"top-full",
"mt-2", // bottom
"right-full",
"-translate-y-1/2",
"mr-2",
"top-1/2", // left
"left-full",
"ml-2", // right
);
const pos = this.getAttribute("position") || "top";
switch (pos) {
case "bottom":
this._tooltipBox.classList.add(
"top-full",
"left-1/2",
"transform",
"-translate-x-1/2",
"mt-0.5",
);
break;
case "left":
this._tooltipBox.classList.add(
"right-full",
"top-1/2",
"transform",
"-translate-y-1/2",
"mr-0.5",
);
break;
case "right":
this._tooltipBox.classList.add(
"left-full",
"top-1/2",
"transform",
"-translate-y-1/2",
"ml-0.5",
);
break;
case "top":
default:
// top as default
this._tooltipBox.classList.add(
"bottom-full",
"left-1/2",
"transform",
"-translate-x-1/2",
"mb-0.5",
);
}
}
}
function Startup() { function Startup() {
let pagedPreviewer = null; let pagedPreviewer = null;
const positionedIntervals = []; const positionedIntervals = [];

View File

@@ -87,6 +87,26 @@
@apply font-bold text-red-500; @apply font-bold text-red-500;
} }
.diagonal-strike {
@apply relative inline-block;
}
.diagonal-strike::before {
@apply border-slate-50 border-t-[3px];
position: absolute;
content: "";
left: 10%;
top: 50%;
right: 0;
width: 60%;
-webkit-transform: rotate(-65deg);
-moz-transform: rotate(-65deg);
-ms-transform: rotate(-65deg);
-o-transform: rotate(-65deg);
transform: rotate(-65deg);
}
.text { .text {
@apply font-serif relative; @apply font-serif relative;
} }

138
views/transform/tool-tip.js Normal file
View File

@@ -0,0 +1,138 @@
export class ToolTip extends HTMLElement {
static get observedAttributes() {
return ["position", "timeout"];
}
constructor() {
super();
this._tooltipBox = null;
this._timeout = 200;
this._hideTimeout = null;
this._hiddenTimeout = null;
}
connectedCallback() {
this.classList.add("relative", "block", "leading-none", "[&>*]:leading-normal");
const dataTipElem = this.querySelector(".data-tip");
const tipContent = dataTipElem ? dataTipElem.innerHTML : "Tooltip";
if (dataTipElem) {
dataTipElem.classList.add("hidden");
}
this._tooltipBox = document.createElement("div");
this._tooltipBox.innerHTML = tipContent;
this._tooltipBox.className = [
"opacity-0",
"hidden",
"absolute",
"px-2",
"py-1",
"text-sm",
"text-white",
"bg-gray-900",
"rounded",
"shadow",
"z-10",
"whitespace-nowrap",
"transition-all",
"duration-200",
"font-sans",
].join(" ");
this.appendChild(this._tooltipBox);
this._updatePosition();
this.addEventListener("mouseenter", () => this._showTooltip());
this.addEventListener("mouseleave", () => this._hideTooltip());
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "position" && this._tooltipBox) {
this._updatePosition();
}
if (name === "timeout" && newValue) {
this._timeout = parseInt(newValue) || 200;
}
}
_showTooltip() {
clearTimeout(this._hideTimeout);
clearTimeout(this._hiddenTimeout);
this._tooltipBox.classList.remove("hidden");
setTimeout(() => {
this._tooltipBox.classList.remove("opacity-0");
this._tooltipBox.classList.add("opacity-100");
}, 16);
}
_hideTooltip() {
this._hideTimeout = setTimeout(() => {
this._tooltipBox.classList.remove("opacity-100");
this._tooltipBox.classList.add("opacity-0");
this._hiddenTimeout = setTimeout(() => {
this._tooltipBox.classList.add("hidden");
}, this._timeout + 100);
}, this._timeout);
}
_updatePosition() {
this._tooltipBox.classList.remove(
"bottom-full",
"left-1/2",
"-translate-x-1/2",
"mb-2", // top
"top-full",
"mt-2", // bottom
"right-full",
"-translate-y-1/2",
"mr-2",
"top-1/2", // left
"left-full",
"ml-2", // right
);
const pos = this.getAttribute("position") || "top";
switch (pos) {
case "bottom":
this._tooltipBox.classList.add(
"top-full",
"left-1/2",
"transform",
"-translate-x-1/2",
"mt-0.5",
);
break;
case "left":
this._tooltipBox.classList.add(
"right-full",
"top-1/2",
"transform",
"-translate-y-1/2",
"mr-0.5",
);
break;
case "right":
this._tooltipBox.classList.add(
"left-full",
"top-1/2",
"transform",
"-translate-y-1/2",
"ml-0.5",
);
break;
case "top":
default:
// top as default
this._tooltipBox.classList.add(
"bottom-full",
"left-1/2",
"transform",
"-translate-x-1/2",
"mb-0.5",
);
}
}
}

View File

@@ -13,6 +13,14 @@ const (
False False
) )
func (b OptionalBool) IsTrue() bool {
return b == True
}
func (b OptionalBool) IsFalse() bool {
return b == False || b == Unspecified
}
func (b *OptionalBool) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { func (b *OptionalBool) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var attr struct { var attr struct {
Value string `xml:"value,attr"` Value string `xml:"value,attr"`