@netlib/widerrufsbutton 1.0.6 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -1,79 +1,90 @@
1
- import { jsx as r, jsxs as s, Fragment as C } from "react/jsx-runtime";
2
- import { useState as u, useRef as W, useEffect as E } from "react";
3
- import { createPortal as A } from "react-dom";
4
- async function j(e, a) {
1
+ import { jsx as r, jsxs as d, Fragment as j } from "react/jsx-runtime";
2
+ import { useState as p, useRef as T, useEffect as I } from "react";
3
+ import { createPortal as q } from "react-dom";
4
+ async function O(e, t) {
5
5
  const n = {
6
6
  "Content-Type": "application/json"
7
7
  };
8
8
  e.authToken && (n.Authorization = `Bearer ${e.authToken}`);
9
- const d = {
9
+ const b = {
10
10
  action: e.action,
11
11
  payload: {
12
- ...a,
12
+ ...t,
13
13
  datum: (/* @__PURE__ */ new Date()).toISOString()
14
14
  }
15
15
  }, i = await fetch(e.apiUrl, {
16
16
  method: "PATCH",
17
17
  headers: n,
18
- body: JSON.stringify(d)
18
+ body: JSON.stringify(b)
19
19
  });
20
+ let o;
21
+ try {
22
+ o = await i.json();
23
+ } catch {
24
+ if (!i.ok) throw new Error(`Server antwortete mit Status ${i.status}`);
25
+ return;
26
+ }
27
+ if (o !== null && typeof o == "object" && "ok" in o && o.ok === !1) {
28
+ const l = o.message;
29
+ throw new Error(typeof l == "string" && l ? l : `Server antwortete mit Status ${i.status}`);
30
+ }
20
31
  if (!i.ok)
21
32
  throw new Error(`Server antwortete mit Status ${i.status}`);
22
33
  }
23
- const z = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE", {
34
+ const W = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE", {
24
35
  day: "2-digit",
25
36
  month: "2-digit",
26
37
  year: "numeric"
27
- }), T = {
38
+ }), D = {
28
39
  name: "",
29
40
  email: "",
30
41
  vertragId: "",
31
42
  widerrufsgrund: "",
32
- datum: z
43
+ datum: W
33
44
  };
34
- function x(e) {
35
- const a = {};
36
- return e.name.trim() || (a.name = "Bitte geben Sie Ihren Namen an."), e.email.trim() ? /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.email) || (a.email = "Bitte geben Sie eine gültige E-Mail-Adresse an.") : a.email = "Bitte geben Sie Ihre E-Mail-Adresse an.", e.vertragId.trim() || (a.vertragId = "Bitte geben Sie die Bestellnummer oder Vertragsnummer an."), a;
37
- }
38
- function O({ config: e, onClose: a }) {
39
- const [n, d] = u(T), [i, g] = u({}), [c, y] = u({}), [m, h] = u("idle"), [v, k] = u(""), N = W(null);
40
- E(() => {
41
- var l;
42
- (l = N.current) == null || l.focus();
43
- const t = (o) => o.key === "Escape" && a();
44
- return document.addEventListener("keydown", t), () => document.removeEventListener("keydown", t);
45
- }, [a]);
46
- function f(t, l) {
47
- const o = { ...n, [t]: l };
48
- if (d(o), c[t]) {
49
- const b = x(o);
50
- g((B) => ({ ...B, [t]: b[t] }));
45
+ function N(e) {
46
+ const t = {};
47
+ return e.name.trim() || (t.name = "Bitte geben Sie Ihren Namen an."), e.email.trim() ? /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.email) || (t.email = "Bitte geben Sie eine gültige E-Mail-Adresse an.") : t.email = "Bitte geben Sie Ihre E-Mail-Adresse an.", e.vertragId.trim() || (t.vertragId = "Bitte geben Sie die Bestellnummer oder Vertragsnummer an."), t;
48
+ }
49
+ function F({ config: e, onClose: t }) {
50
+ const [n, b] = p(D), [i, o] = p({}), [l, m] = p({}), [h, v] = p("idle"), [E, S] = p(""), $ = T(null);
51
+ I(() => {
52
+ var c;
53
+ (c = $.current) == null || c.focus();
54
+ const a = (s) => s.key === "Escape" && t();
55
+ return document.addEventListener("keydown", a), () => document.removeEventListener("keydown", a);
56
+ }, [t]);
57
+ function g(a, c) {
58
+ const s = { ...n, [a]: c };
59
+ if (b(s), l[a]) {
60
+ const f = N(s);
61
+ o((x) => ({ ...x, [a]: f[a] }));
51
62
  }
52
63
  }
53
- function w(t) {
54
- y((o) => ({ ...o, [t]: !0 }));
55
- const l = x(n);
56
- g((o) => ({ ...o, [t]: l[t] }));
64
+ function k(a) {
65
+ m((s) => ({ ...s, [a]: !0 }));
66
+ const c = N(n);
67
+ o((s) => ({ ...s, [a]: c[a] }));
57
68
  }
58
- async function S(t) {
59
- t.preventDefault();
60
- const l = Object.fromEntries(
61
- Object.keys(n).map((b) => [b, !0])
69
+ async function C(a) {
70
+ var f, x;
71
+ a.preventDefault();
72
+ const c = Object.fromEntries(
73
+ Object.keys(n).map((y) => [y, !0])
62
74
  );
63
- y(l);
64
- const o = x(n);
65
- if (g(o), !(Object.keys(o).length > 0)) {
66
- h("loading"), k("");
75
+ m(c);
76
+ const s = N(n);
77
+ if (o(s), !(Object.keys(s).length > 0)) {
78
+ v("loading"), S("");
67
79
  try {
68
- await j(e, n), h("success");
69
- } catch (b) {
70
- k(
71
- b instanceof Error ? b.message : "Ein unbekannter Fehler ist aufgetreten."
72
- ), h("error");
80
+ await O(e, n), v("success"), (f = e.onSuccess) == null || f.call(e);
81
+ } catch (y) {
82
+ const z = y instanceof Error ? y : new Error("Ein unbekannter Fehler ist aufgetreten.");
83
+ S(z.message), v("error"), (x = e.onError) == null || x.call(e, z);
73
84
  }
74
85
  }
75
86
  }
76
- const I = e.companyName ? `Widerruf – ${e.companyName}` : "Widerrufsformular";
87
+ const L = e.companyName ? `Widerruf – ${e.companyName}` : "Widerrufsformular";
77
88
  return /* @__PURE__ */ r(
78
89
  "div",
79
90
  {
@@ -81,97 +92,100 @@ function O({ config: e, onClose: a }) {
81
92
  role: "dialog",
82
93
  "aria-modal": "true",
83
94
  "aria-labelledby": "wrb-title",
84
- onClick: (t) => t.target === t.currentTarget && a(),
85
- children: /* @__PURE__ */ s("div", { className: "wrb-modal", children: [
86
- /* @__PURE__ */ s("div", { className: "wrb-modal-header", children: [
87
- /* @__PURE__ */ r("h2", { className: "wrb-modal-title", id: "wrb-title", children: I }),
95
+ onClick: (a) => a.target === a.currentTarget && t(),
96
+ children: /* @__PURE__ */ d("div", { className: "wrb-modal", children: [
97
+ /* @__PURE__ */ d("div", { className: "wrb-modal-header", children: [
98
+ /* @__PURE__ */ r("h2", { className: "wrb-modal-title", id: "wrb-title", children: L }),
88
99
  /* @__PURE__ */ r(
89
100
  "button",
90
101
  {
91
102
  className: "wrb-close-btn",
92
- onClick: a,
103
+ onClick: t,
93
104
  "aria-label": "Schließen",
94
105
  type: "button",
95
106
  children: "✕"
96
107
  }
97
108
  )
98
109
  ] }),
99
- m === "success" ? /* @__PURE__ */ s("div", { className: "wrb-success", children: [
100
- /* @__PURE__ */ r("span", { className: "wrb-success-icon", children: "✓" }),
101
- /* @__PURE__ */ r("h3", { children: "Widerruf eingegangen" }),
102
- /* @__PURE__ */ s("p", { children: [
103
- "Ihr Widerruf wurde erfolgreich übermittelt. Sie erhalten in Kürze eine Bestätigung an ",
104
- /* @__PURE__ */ r("strong", { children: n.email }),
105
- "."
106
- ] })
107
- ] }) : /* @__PURE__ */ s("div", { className: "wrb-modal-body", children: [
110
+ h === "success" ? /* @__PURE__ */ d("div", { className: "wrb-modal-body", children: [
111
+ /* @__PURE__ */ d("div", { className: "wrb-success", children: [
112
+ /* @__PURE__ */ r("span", { className: "wrb-success-icon", children: "✓" }),
113
+ /* @__PURE__ */ r("h3", { children: "Widerruf eingegangen" }),
114
+ /* @__PURE__ */ d("p", { children: [
115
+ "Ihr Widerruf wurde erfolgreich übermittelt. Sie erhalten in Kürze eine Bestätigung an ",
116
+ /* @__PURE__ */ r("strong", { children: n.email }),
117
+ "."
118
+ ] })
119
+ ] }),
120
+ /* @__PURE__ */ r(B, { links: e.legalLinks })
121
+ ] }) : /* @__PURE__ */ d("div", { className: "wrb-modal-body", children: [
108
122
  e.introText && /* @__PURE__ */ r("p", { className: "wrb-intro", children: e.introText }),
109
- m === "error" && v && /* @__PURE__ */ r("div", { className: "wrb-alert wrb-alert-error", role: "alert", children: v }),
110
- /* @__PURE__ */ s("form", { onSubmit: S, noValidate: !0, children: [
123
+ h === "error" && E && /* @__PURE__ */ r("div", { className: "wrb-alert wrb-alert-error", role: "alert", children: E }),
124
+ /* @__PURE__ */ d("form", { onSubmit: C, noValidate: !0, children: [
111
125
  /* @__PURE__ */ r(
112
- p,
126
+ w,
113
127
  {
114
128
  label: "Name",
115
129
  required: !0,
116
- error: c.name ? i.name : void 0,
130
+ error: l.name ? i.name : void 0,
117
131
  children: /* @__PURE__ */ r(
118
132
  "input",
119
133
  {
120
- ref: N,
121
- className: `wrb-input${c.name && i.name ? " wrb-error" : ""}`,
134
+ ref: $,
135
+ className: `wrb-input${l.name && i.name ? " wrb-error" : ""}`,
122
136
  type: "text",
123
137
  autoComplete: "name",
124
138
  value: n.name,
125
- onChange: (t) => f("name", t.target.value),
126
- onBlur: () => w("name"),
139
+ onChange: (a) => g("name", a.target.value),
140
+ onBlur: () => k("name"),
127
141
  placeholder: "Vor- und Nachname"
128
142
  }
129
143
  )
130
144
  }
131
145
  ),
132
146
  /* @__PURE__ */ r(
133
- p,
147
+ w,
134
148
  {
135
149
  label: "E-Mail-Adresse",
136
150
  required: !0,
137
151
  hint: "Hierüber erhalten Sie die Eingangsbestätigung.",
138
- error: c.email ? i.email : void 0,
152
+ error: l.email ? i.email : void 0,
139
153
  children: /* @__PURE__ */ r(
140
154
  "input",
141
155
  {
142
- className: `wrb-input${c.email && i.email ? " wrb-error" : ""}`,
156
+ className: `wrb-input${l.email && i.email ? " wrb-error" : ""}`,
143
157
  type: "email",
144
158
  autoComplete: "email",
145
159
  value: n.email,
146
- onChange: (t) => f("email", t.target.value),
147
- onBlur: () => w("email"),
160
+ onChange: (a) => g("email", a.target.value),
161
+ onBlur: () => k("email"),
148
162
  placeholder: "name@beispiel.de"
149
163
  }
150
164
  )
151
165
  }
152
166
  ),
153
167
  /* @__PURE__ */ r(
154
- p,
168
+ w,
155
169
  {
156
170
  label: "Bestell- / Auftrags- / Vertragsnummer",
157
171
  required: !0,
158
172
  hint: "Zu finden in Ihrer Bestellbestätigung.",
159
- error: c.vertragId ? i.vertragId : void 0,
173
+ error: l.vertragId ? i.vertragId : void 0,
160
174
  children: /* @__PURE__ */ r(
161
175
  "input",
162
176
  {
163
- className: `wrb-input${c.vertragId && i.vertragId ? " wrb-error" : ""}`,
177
+ className: `wrb-input${l.vertragId && i.vertragId ? " wrb-error" : ""}`,
164
178
  type: "text",
165
179
  value: n.vertragId,
166
- onChange: (t) => f("vertragId", t.target.value),
167
- onBlur: () => w("vertragId"),
180
+ onChange: (a) => g("vertragId", a.target.value),
181
+ onBlur: () => k("vertragId"),
168
182
  placeholder: "z.B. 10045678"
169
183
  }
170
184
  )
171
185
  }
172
186
  ),
173
187
  /* @__PURE__ */ r(
174
- p,
188
+ w,
175
189
  {
176
190
  label: "Widerrufsgrund",
177
191
  hint: "Freiwillige Angabe – ein Widerruf ist ohne Angabe von Gründen möglich.",
@@ -180,21 +194,21 @@ function O({ config: e, onClose: a }) {
180
194
  {
181
195
  className: "wrb-textarea",
182
196
  value: n.widerrufsgrund,
183
- onChange: (t) => f("widerrufsgrund", t.target.value),
197
+ onChange: (a) => g("widerrufsgrund", a.target.value),
184
198
  placeholder: "Optional",
185
199
  rows: 3
186
200
  }
187
201
  )
188
202
  }
189
203
  ),
190
- /* @__PURE__ */ r(p, { label: "Datum der Widerrufserklärung", children: /* @__PURE__ */ r("div", { className: "wrb-date-display", children: z }) }),
191
- /* @__PURE__ */ s("div", { className: "wrb-actions", children: [
204
+ /* @__PURE__ */ r(w, { label: "Datum der Widerrufserklärung", children: /* @__PURE__ */ r("div", { className: "wrb-date-display", children: W }) }),
205
+ /* @__PURE__ */ d("div", { className: "wrb-actions", children: [
192
206
  /* @__PURE__ */ r(
193
207
  "button",
194
208
  {
195
209
  type: "button",
196
210
  className: "wrb-cancel-btn",
197
- onClick: a,
211
+ onClick: t,
198
212
  children: e.cancelLabel ?? "Abbrechen"
199
213
  }
200
214
  ),
@@ -203,32 +217,43 @@ function O({ config: e, onClose: a }) {
203
217
  {
204
218
  type: "submit",
205
219
  className: "wrb-submit-btn",
206
- disabled: m === "loading",
207
- children: m === "loading" ? "Wird gesendet…" : e.submitLabel ?? "Absenden"
220
+ disabled: h === "loading",
221
+ children: h === "loading" ? "Wird gesendet…" : e.submitLabel ?? "Absenden"
208
222
  }
209
223
  )
210
224
  ] })
211
- ] })
225
+ ] }),
226
+ /* @__PURE__ */ r(B, { links: e.legalLinks })
212
227
  ] })
213
228
  ] })
214
229
  }
215
230
  );
216
231
  }
217
- function p({ label: e, required: a, hint: n, error: d, children: i }) {
218
- return /* @__PURE__ */ s("div", { className: "wrb-field", children: [
219
- /* @__PURE__ */ s("label", { className: "wrb-label", children: [
232
+ function w({ label: e, required: t, hint: n, error: b, children: i }) {
233
+ return /* @__PURE__ */ d("div", { className: "wrb-field", children: [
234
+ /* @__PURE__ */ d("label", { className: "wrb-label", children: [
220
235
  e,
221
- a && /* @__PURE__ */ r("span", { className: "wrb-required", "aria-hidden": "true", children: "*" })
236
+ t && /* @__PURE__ */ r("span", { className: "wrb-required", "aria-hidden": "true", children: "*" })
222
237
  ] }),
223
238
  i,
224
- n && !d && /* @__PURE__ */ r("p", { className: "wrb-hint", children: n }),
225
- d && /* @__PURE__ */ r("p", { className: "wrb-field-error", role: "alert", children: d })
239
+ n && !b && /* @__PURE__ */ r("p", { className: "wrb-hint", children: n }),
240
+ b && /* @__PURE__ */ r("p", { className: "wrb-field-error", role: "alert", children: b })
226
241
  ] });
227
242
  }
228
- function $() {
229
- const e = "wrb-styles";
230
- if (document.getElementById(e)) return;
231
- const a = `
243
+ function B({ links: e }) {
244
+ return e != null && e.length ? /* @__PURE__ */ r("div", { className: "wrb-legal-links", children: e.map((t) => /* @__PURE__ */ r(
245
+ "a",
246
+ {
247
+ href: t.href,
248
+ className: "wrb-legal-link",
249
+ target: "_blank",
250
+ rel: "noopener noreferrer",
251
+ children: t.name
252
+ },
253
+ t.href
254
+ )) }) : null;
255
+ }
256
+ const A = `
232
257
  /* Widerrufsbutton widget — prefix: wrb- */
233
258
  .wrb-btn {
234
259
  display: inline-flex;
@@ -430,14 +455,37 @@ function $() {
430
455
  font-size: 14px;
431
456
  }
432
457
  .wrb-alert-error { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }
433
- `, n = document.createElement("style");
434
- n.id = e, n.textContent = a, document.head.appendChild(n);
435
- }
436
- function L({ config: e }) {
437
- const [a, n] = u(!1);
438
- return E(() => {
439
- $();
440
- }, []), /* @__PURE__ */ s(C, { children: [
458
+
459
+ /* Legal links */
460
+ .wrb-legal-links {
461
+ display: flex;
462
+ flex-wrap: wrap;
463
+ gap: 4px 16px;
464
+ margin-top: 16px;
465
+ padding-top: 12px;
466
+ border-top: 1px solid #e5e7eb;
467
+ }
468
+ .wrb-legal-link {
469
+ font-size: 12px;
470
+ color: #6b7280;
471
+ text-decoration: none;
472
+ }
473
+ .wrb-legal-link:hover {
474
+ color: #374151;
475
+ text-decoration: underline;
476
+ }
477
+ `;
478
+ function M() {
479
+ const e = "wrb-styles";
480
+ if (document.getElementById(e)) return;
481
+ const t = document.createElement("style");
482
+ t.id = e, t.textContent = A, document.head.appendChild(t);
483
+ }
484
+ function P({ config: e }) {
485
+ const [t, n] = p(!1);
486
+ return I(() => {
487
+ M();
488
+ }, []), /* @__PURE__ */ d(j, { children: [
441
489
  /* @__PURE__ */ r(
442
490
  "button",
443
491
  {
@@ -447,13 +495,131 @@ function L({ config: e }) {
447
495
  children: e.buttonLabel ?? "Vertrag widerrufen"
448
496
  }
449
497
  ),
450
- a && A(
451
- /* @__PURE__ */ r(O, { config: e, onClose: () => n(!1) }),
498
+ t && q(
499
+ /* @__PURE__ */ r(F, { config: e, onClose: () => n(!1) }),
452
500
  document.body
453
501
  )
454
502
  ] });
455
503
  }
504
+ function u(e) {
505
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
506
+ }
507
+ function R(e) {
508
+ var l;
509
+ const t = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE", {
510
+ day: "2-digit",
511
+ month: "2-digit",
512
+ year: "numeric"
513
+ }), n = e.companyName ? `Widerruf – ${u(e.companyName)}` : "Widerrufsformular", b = u(e.formAction ?? e.apiUrl ?? "/rest/v1/apiCancellation"), i = e.introText ? `<p class="wrb-intro">${u(e.introText)}</p>` : "", o = (l = e.legalLinks) != null && l.length ? `
514
+ <div class="wrb-legal-links">
515
+ ${e.legalLinks.map((m) => `<a href="${u(m.href)}" class="wrb-legal-link" rel="noopener noreferrer">${u(m.name)}</a>`).join(`
516
+ `)}
517
+ </div>` : "";
518
+ return `<!DOCTYPE html>
519
+ <html lang="de">
520
+ <head>
521
+ <meta charset="UTF-8">
522
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
523
+ <title>${n}</title>
524
+ <style>
525
+ *, *::before, *::after { box-sizing: border-box; }
526
+ body { margin: 0; background: #f9fafb; }
527
+ .wrb-page {
528
+ min-height: 100vh;
529
+ display: flex;
530
+ align-items: flex-start;
531
+ justify-content: center;
532
+ padding: 40px 16px;
533
+ }
534
+ .wrb-page .wrb-modal { max-height: none; }
535
+ .wrb-page .wrb-modal-title { font-size: 20px; }
536
+ ${A}
537
+ </style>
538
+ </head>
539
+ <body>
540
+ <div class="wrb-page">
541
+ <div class="wrb-modal">
542
+ <div class="wrb-modal-header">
543
+ <h1 class="wrb-modal-title">${n}</h1>
544
+ </div>
545
+ <div class="wrb-modal-body">
546
+ ${i}
547
+ <form method="POST" action="${b}" novalidate>
548
+ <div class="wrb-field">
549
+ <label class="wrb-label">
550
+ Name <span class="wrb-required" aria-hidden="true">*</span>
551
+ </label>
552
+ <input
553
+ class="wrb-input"
554
+ type="text"
555
+ name="name"
556
+ autocomplete="name"
557
+ placeholder="Vor- und Nachname"
558
+ required
559
+ >
560
+ </div>
561
+ <div class="wrb-field">
562
+ <label class="wrb-label">
563
+ E-Mail-Adresse <span class="wrb-required" aria-hidden="true">*</span>
564
+ </label>
565
+ <input
566
+ class="wrb-input"
567
+ type="email"
568
+ name="email"
569
+ autocomplete="email"
570
+ placeholder="name@beispiel.de"
571
+ required
572
+ >
573
+ <p class="wrb-hint">Hierüber erhalten Sie die Eingangsbestätigung.</p>
574
+ </div>
575
+ <div class="wrb-field">
576
+ <label class="wrb-label">
577
+ Bestell- / Auftrags- / Vertragsnummer
578
+ <span class="wrb-required" aria-hidden="true">*</span>
579
+ </label>
580
+ <input
581
+ class="wrb-input"
582
+ type="text"
583
+ name="vertragId"
584
+ placeholder="z.B. 10045678"
585
+ required
586
+ >
587
+ <p class="wrb-hint">Zu finden in Ihrer Bestellbestätigung.</p>
588
+ </div>
589
+ <div class="wrb-field">
590
+ <label class="wrb-label">Widerrufsgrund</label>
591
+ <textarea
592
+ class="wrb-textarea"
593
+ name="widerrufsgrund"
594
+ placeholder="Optional"
595
+ rows="3"
596
+ ></textarea>
597
+ <p class="wrb-hint">
598
+ Freiwillige Angabe – ein Widerruf ist ohne Angabe von Gründen möglich.
599
+ </p>
600
+ </div>
601
+ <div class="wrb-field">
602
+ <label class="wrb-label">Datum der Widerrufserklärung</label>
603
+ <div class="wrb-date-display">${t}</div>
604
+ <input type="hidden" name="datum" value="${t}">
605
+ </div>
606
+ <input type="hidden" name="action" value="${u(e.action)}">
607
+ ${e.successUrl ? `<input type="hidden" name="successUrl" value="${u(e.successUrl)}">` : ""}
608
+ <div class="wrb-actions">
609
+ <button type="submit" class="wrb-submit-btn">
610
+ ${u(e.submitLabel ?? "Absenden")}
611
+ </button>
612
+ </div>
613
+ </form>
614
+ ${o}
615
+ </div>
616
+ </div>
617
+ </div>
618
+ </body>
619
+ </html>`;
620
+ }
456
621
  export {
457
- O as WiderrufsModal,
458
- L as WiderrufsWidget
622
+ F as WiderrufsModal,
623
+ P as WiderrufsWidget,
624
+ R as generateFallbackHtml
459
625
  };
package/dist/styles.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export declare const WRB_CSS = "\n/* Widerrufsbutton widget \u2014 prefix: wrb- */\n.wrb-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 10px 20px;\n background: #c0392b;\n color: #fff;\n border: none;\n border-radius: 6px;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n font-family: inherit;\n transition: background 0.2s;\n text-decoration: none;\n}\n.wrb-btn:hover { background: #a93226; }\n.wrb-btn:focus-visible { outline: 3px solid #e74c3c; outline-offset: 2px; }\n\n/* Overlay */\n.wrb-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0,0,0,0.55);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 99999;\n padding: 16px;\n animation: wrb-fade-in 0.15s ease;\n}\n@keyframes wrb-fade-in { from { opacity: 0 } to { opacity: 1 } }\n\n/* Modal box */\n.wrb-modal {\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n width: 100%;\n max-width: 540px;\n max-height: 90vh;\n overflow-y: auto;\n animation: wrb-slide-up 0.2s ease;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 15px;\n color: #1a1a1a;\n}\n@keyframes wrb-slide-up { from { transform: translateY(20px); opacity: 0 } to { transform: none; opacity: 1 } }\n\n.wrb-modal-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 20px 24px 16px;\n border-bottom: 1px solid #e5e7eb;\n gap: 12px;\n}\n.wrb-modal-title {\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n color: #111;\n line-height: 1.3;\n}\n.wrb-close-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: #6b7280;\n padding: 4px;\n border-radius: 4px;\n font-size: 20px;\n line-height: 1;\n flex-shrink: 0;\n}\n.wrb-close-btn:hover { color: #111; background: #f3f4f6; }\n\n.wrb-modal-body {\n padding: 20px 24px 24px;\n}\n\n.wrb-intro {\n margin: 0 0 20px;\n color: #4b5563;\n line-height: 1.6;\n font-size: 14px;\n}\n\n/* Form */\n.wrb-field {\n margin-bottom: 16px;\n}\n.wrb-label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: #374151;\n margin-bottom: 5px;\n}\n.wrb-required {\n color: #c0392b;\n margin-left: 2px;\n}\n.wrb-input,\n.wrb-textarea {\n width: 100%;\n padding: 9px 12px;\n border: 1.5px solid #d1d5db;\n border-radius: 6px;\n font-size: 15px;\n font-family: inherit;\n color: #111;\n background: #fff;\n box-sizing: border-box;\n transition: border-color 0.15s;\n}\n.wrb-input:focus,\n.wrb-textarea:focus {\n outline: none;\n border-color: #c0392b;\n box-shadow: 0 0 0 3px rgba(192,57,43,0.12);\n}\n.wrb-input.wrb-error,\n.wrb-textarea.wrb-error {\n border-color: #c0392b;\n}\n.wrb-field-error {\n color: #c0392b;\n font-size: 12px;\n margin-top: 4px;\n}\n.wrb-textarea { resize: vertical; min-height: 80px; }\n\n.wrb-hint {\n font-size: 12px;\n color: #6b7280;\n margin-top: 4px;\n}\n\n.wrb-date-display {\n padding: 9px 12px;\n background: #f9fafb;\n border: 1.5px solid #e5e7eb;\n border-radius: 6px;\n color: #6b7280;\n font-size: 15px;\n}\n\n/* Actions */\n.wrb-actions {\n display: flex;\n gap: 10px;\n justify-content: flex-end;\n margin-top: 24px;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n}\n.wrb-submit-btn {\n padding: 10px 22px;\n background: #c0392b;\n color: #fff;\n border: none;\n border-radius: 6px;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n font-family: inherit;\n transition: background 0.2s;\n}\n.wrb-submit-btn:hover:not(:disabled) { background: #a93226; }\n.wrb-submit-btn:disabled { opacity: 0.6; cursor: not-allowed; }\n.wrb-cancel-btn {\n padding: 10px 18px;\n background: transparent;\n color: #374151;\n border: 1.5px solid #d1d5db;\n border-radius: 6px;\n font-size: 15px;\n cursor: pointer;\n font-family: inherit;\n transition: background 0.15s;\n}\n.wrb-cancel-btn:hover { background: #f3f4f6; }\n\n/* Success / Error states */\n.wrb-success {\n text-align: center;\n padding: 32px 24px;\n}\n.wrb-success-icon { font-size: 48px; display: block; margin-bottom: 12px; }\n.wrb-success h3 { margin: 0 0 8px; font-size: 20px; color: #111; }\n.wrb-success p { margin: 0; color: #4b5563; line-height: 1.6; }\n\n.wrb-alert {\n padding: 12px 16px;\n border-radius: 6px;\n margin-bottom: 16px;\n font-size: 14px;\n}\n.wrb-alert-error { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }\n\n/* Legal links */\n.wrb-legal-links {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 16px;\n margin-top: 16px;\n padding-top: 12px;\n border-top: 1px solid #e5e7eb;\n}\n.wrb-legal-link {\n font-size: 12px;\n color: #6b7280;\n text-decoration: none;\n}\n.wrb-legal-link:hover {\n color: #374151;\n text-decoration: underline;\n}\n";
1
2
  /** Injects scoped CSS once into the document head */
2
3
  export declare function injectStyles(): void;
package/dist/types.d.ts CHANGED
@@ -1,3 +1,7 @@
1
+ export interface LegalLink {
2
+ name: string;
3
+ href: string;
4
+ }
1
5
  export interface WiderrufsConfig {
2
6
  /** PATCH endpoint, e.g. /rest/v1/systemLog */
3
7
  apiUrl: string;
@@ -15,8 +19,18 @@ export interface WiderrufsConfig {
15
19
  authToken?: string;
16
20
  /** Label for the cancel button (default: "Abbrechen") */
17
21
  cancelLabel?: string;
18
- /** Label for the submit button (default: "Widerruf bestätigen") */
22
+ /** Label for the submit button (default: "Absenden") */
19
23
  submitLabel?: string;
24
+ /** Legal navigation links shown in modal footer and static fallback page */
25
+ legalLinks?: LegalLink[];
26
+ /** POST action URL for the static HTML fallback form (defaults to apiUrl) */
27
+ formAction?: string;
28
+ /** Redirect URL after successful submit on the static fallback page */
29
+ successUrl?: string;
30
+ /** Called after a successful submit in the modal */
31
+ onSuccess?: () => void;
32
+ /** Called after a failed submit in the modal */
33
+ onError?: (error: Error) => void;
20
34
  }
21
35
  export interface WiderrufsFormData {
22
36
  name: string;