@ansiversa/components 0.0.121 → 0.0.123
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/package.json +1 -1
- package/src/resume-templates/ResumeTemplateClassic.astro +3 -23
- package/src/resume-templates/ResumeTemplateExecutiveTimeline.astro +4 -14
- package/src/resume-templates/ResumeTemplateMinimal.astro +8 -37
- package/src/resume-templates/ResumeTemplateModernTwoTone.astro +16 -24
- package/src/resume-templates/typescript-schema.ts +147 -0
- package/src/styles/global.css +22 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { ResumeData } from "./typescript-schema";
|
|
3
|
-
import { formatDateRange } from "./typescript-schema";
|
|
3
|
+
import { buildContactEntries, formatDateRange } from "./typescript-schema";
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
data: ResumeData;
|
|
@@ -13,27 +13,7 @@ const locationLabel =
|
|
|
13
13
|
basics.location?.label ||
|
|
14
14
|
[basics.location?.city, basics.location?.country].filter(Boolean).join(", ");
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
value.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
18
|
-
|
|
19
|
-
const contactItems: Array<{ label: string; href?: string }> = [];
|
|
20
|
-
if (locationLabel) contactItems.push({ label: locationLabel });
|
|
21
|
-
if (basics.contact.email) {
|
|
22
|
-
contactItems.push({ label: basics.contact.email, href: `mailto:${basics.contact.email}` });
|
|
23
|
-
}
|
|
24
|
-
if (basics.contact.phone) {
|
|
25
|
-
const phoneHref = basics.contact.phone.replace(/\s+/g, "");
|
|
26
|
-
contactItems.push({ label: basics.contact.phone, href: `tel:${phoneHref}` });
|
|
27
|
-
}
|
|
28
|
-
if (basics.contact.website) {
|
|
29
|
-
contactItems.push({ label: formatUrlLabel(basics.contact.website), href: basics.contact.website });
|
|
30
|
-
}
|
|
31
|
-
for (const link of basics.links ?? []) {
|
|
32
|
-
const urlLabel = link.url ? formatUrlLabel(link.url) : link.label;
|
|
33
|
-
if (urlLabel) {
|
|
34
|
-
contactItems.push({ label: urlLabel, href: link.url });
|
|
35
|
-
}
|
|
36
|
-
}
|
|
16
|
+
const contactItems = buildContactEntries(basics, locationLabel);
|
|
37
17
|
|
|
38
18
|
const experienceItems = data.experience ?? [];
|
|
39
19
|
const projectItems = data.projects ?? [];
|
|
@@ -47,7 +27,7 @@ const declaration = data.declaration ?? {};
|
|
|
47
27
|
const hasDeclaration = Boolean(declaration.text || declaration.place || declaration.name);
|
|
48
28
|
---
|
|
49
29
|
|
|
50
|
-
<div class="resume-template bg-slate-100 text-slate-900 print:bg-white">
|
|
30
|
+
<div class="resume-template av-print-white bg-slate-100 text-slate-900 print:bg-white">
|
|
51
31
|
<main class="mx-auto max-w-4xl p-4 sm:p-6">
|
|
52
32
|
<section class="rounded-2xl bg-white p-6 shadow-sm ring-1 ring-slate-200 sm:p-10 print:shadow-none print:ring-0">
|
|
53
33
|
<header class="av-print-avoid-break flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { ResumeData } from "./typescript-schema";
|
|
3
|
-
import { formatDateRange } from "./typescript-schema";
|
|
3
|
+
import { buildContactEntries, formatDateRange, truncateText } from "./typescript-schema";
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
data: ResumeData;
|
|
@@ -13,17 +13,7 @@ const locationLabel =
|
|
|
13
13
|
basics.location?.label ||
|
|
14
14
|
[basics.location?.city, basics.location?.country].filter(Boolean).join(", ");
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
value.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
18
|
-
|
|
19
|
-
const contactChips: string[] = [];
|
|
20
|
-
if (locationLabel) contactChips.push(locationLabel);
|
|
21
|
-
if (basics.contact.email) contactChips.push(basics.contact.email);
|
|
22
|
-
if (basics.contact.phone) contactChips.push(basics.contact.phone);
|
|
23
|
-
if (basics.contact.website) contactChips.push(formatUrlLabel(basics.contact.website));
|
|
24
|
-
for (const link of basics.links ?? []) {
|
|
25
|
-
contactChips.push(link.label ?? formatUrlLabel(link.url));
|
|
26
|
-
}
|
|
16
|
+
const contactChips = buildContactEntries(basics, locationLabel).map((item) => item.label);
|
|
27
17
|
|
|
28
18
|
const normalizeSkillLabel = (value?: string) =>
|
|
29
19
|
(value ?? "")
|
|
@@ -41,7 +31,7 @@ const declaration = data.declaration ?? {};
|
|
|
41
31
|
const hasDeclaration = Boolean(declaration.text || declaration.place || declaration.name);
|
|
42
32
|
---
|
|
43
33
|
|
|
44
|
-
<div class="resume-template bg-slate-100 text-slate-900 print:bg-white">
|
|
34
|
+
<div class="resume-template av-print-white bg-slate-100 text-slate-900 print:bg-white">
|
|
45
35
|
<main class="mx-auto max-w-4xl p-4 sm:p-6">
|
|
46
36
|
<section class="rounded-2xl bg-white p-7 shadow-sm ring-1 ring-slate-200 sm:p-10 print:shadow-none print:ring-0">
|
|
47
37
|
<header class="av-print-avoid-break flex flex-col gap-5 sm:flex-row sm:items-end sm:justify-between">
|
|
@@ -92,7 +82,7 @@ const hasDeclaration = Boolean(declaration.text || declaration.place || declarat
|
|
|
92
82
|
{item.bullets && item.bullets.length > 0 && (
|
|
93
83
|
<ul class="mt-3 list-disc space-y-2 pl-5 text-sm leading-6 text-slate-700">
|
|
94
84
|
{item.bullets.map((bullet) => (
|
|
95
|
-
<li>{bullet}</li>
|
|
85
|
+
<li class="break-words print:line-clamp-2">{truncateText(bullet, 140)}</li>
|
|
96
86
|
))}
|
|
97
87
|
</ul>
|
|
98
88
|
)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { ResumeData } from "./typescript-schema";
|
|
3
|
-
import { formatDateRange } from "./typescript-schema";
|
|
3
|
+
import { buildContactEntries, formatDateRange, truncateText } from "./typescript-schema";
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
data: ResumeData;
|
|
@@ -13,23 +13,7 @@ const locationLabel =
|
|
|
13
13
|
basics.location?.label ||
|
|
14
14
|
[basics.location?.city, basics.location?.country].filter(Boolean).join(", ");
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
value.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
18
|
-
|
|
19
|
-
const contactLinks: Array<{ label: string; href?: string }> = [];
|
|
20
|
-
if (basics.contact.email) {
|
|
21
|
-
contactLinks.push({ label: basics.contact.email, href: `mailto:${basics.contact.email}` });
|
|
22
|
-
}
|
|
23
|
-
if (basics.contact.phone) {
|
|
24
|
-
const phoneHref = basics.contact.phone.replace(/\s+/g, "");
|
|
25
|
-
contactLinks.push({ label: basics.contact.phone, href: `tel:${phoneHref}` });
|
|
26
|
-
}
|
|
27
|
-
if (basics.contact.website) {
|
|
28
|
-
contactLinks.push({ label: formatUrlLabel(basics.contact.website), href: basics.contact.website });
|
|
29
|
-
}
|
|
30
|
-
for (const link of basics.links ?? []) {
|
|
31
|
-
contactLinks.push({ label: link.label ?? formatUrlLabel(link.url), href: link.url });
|
|
32
|
-
}
|
|
16
|
+
const contactLinks = buildContactEntries(basics, locationLabel);
|
|
33
17
|
|
|
34
18
|
const headlineLine = [basics.headline, locationLabel].filter(Boolean).join(" · ");
|
|
35
19
|
const experienceItems = data.experience ?? [];
|
|
@@ -41,20 +25,7 @@ const showSkillsAs = data.settings?.showSkillsAs ?? "levels";
|
|
|
41
25
|
const declaration = data.declaration ?? {};
|
|
42
26
|
const hasDeclaration = Boolean(declaration.text || declaration.place || declaration.name);
|
|
43
27
|
---
|
|
44
|
-
|
|
45
|
-
<style>
|
|
46
|
-
@media print {
|
|
47
|
-
.resume-template a {
|
|
48
|
-
text-decoration: none;
|
|
49
|
-
color: inherit;
|
|
50
|
-
}
|
|
51
|
-
.resume-template {
|
|
52
|
-
background: white;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
</style>
|
|
56
|
-
|
|
57
|
-
<div class="resume-template bg-slate-100 text-slate-900 print:bg-white">
|
|
28
|
+
<div class="resume-template av-print-white bg-slate-100 text-slate-900 print:bg-white">
|
|
58
29
|
<main class="mx-auto max-w-4xl p-4 sm:p-6">
|
|
59
30
|
<section class="rounded-2xl bg-white p-8 shadow-sm ring-1 ring-slate-200 sm:p-12 print:shadow-none print:ring-0">
|
|
60
31
|
<header class="space-y-4">
|
|
@@ -67,7 +38,7 @@ const hasDeclaration = Boolean(declaration.text || declaration.place || declarat
|
|
|
67
38
|
<div class="text-sm text-slate-700 sm:text-right">
|
|
68
39
|
<div class="flex flex-col gap-1">
|
|
69
40
|
{contactLinks.map((item) => (
|
|
70
|
-
|
|
41
|
+
<a class="underline underline-offset-4 hover:text-slate-900 print:no-underline" href={item.href}>
|
|
71
42
|
{item.label}
|
|
72
43
|
</a>
|
|
73
44
|
))}
|
|
@@ -77,7 +48,7 @@ const hasDeclaration = Boolean(declaration.text || declaration.place || declarat
|
|
|
77
48
|
</div>
|
|
78
49
|
|
|
79
50
|
{basics.summary && (
|
|
80
|
-
<p class="max-w-3xl text-sm leading-7 text-slate-700">{basics.summary}</p>
|
|
51
|
+
<p class="max-w-3xl text-sm leading-7 text-slate-700">{truncateText(basics.summary, 220)}</p>
|
|
81
52
|
)}
|
|
82
53
|
</header>
|
|
83
54
|
|
|
@@ -99,13 +70,13 @@ const hasDeclaration = Boolean(declaration.text || declaration.place || declarat
|
|
|
99
70
|
<p class="text-sm text-slate-500">{formatDateRange(item)}</p>
|
|
100
71
|
</div>
|
|
101
72
|
{item.summary && (
|
|
102
|
-
<p class="mt-3 text-sm leading-7 text-slate-700">{item.summary}</p>
|
|
73
|
+
<p class="mt-3 text-sm leading-7 text-slate-700">{truncateText(item.summary, 220)}</p>
|
|
103
74
|
)}
|
|
104
75
|
{item.bullets && item.bullets.length > 0 && (
|
|
105
76
|
<ul class="mt-3 space-y-2 text-sm leading-7 text-slate-700">
|
|
106
77
|
{item.bullets.map((bullet) => (
|
|
107
78
|
<li>
|
|
108
|
-
<span class="font-medium">•</span> {bullet}
|
|
79
|
+
<span class="font-medium">•</span> {truncateText(bullet, 140)}
|
|
109
80
|
</li>
|
|
110
81
|
))}
|
|
111
82
|
</ul>
|
|
@@ -141,7 +112,7 @@ const hasDeclaration = Boolean(declaration.text || declaration.place || declarat
|
|
|
141
112
|
)}
|
|
142
113
|
</div>
|
|
143
114
|
{project.summary && (
|
|
144
|
-
<p class="mt-2 text-sm leading-7 text-slate-700">{project.summary}</p>
|
|
115
|
+
<p class="mt-2 text-sm leading-7 text-slate-700">{truncateText(project.summary, 220)}</p>
|
|
145
116
|
)}
|
|
146
117
|
{project.tags && project.tags.length > 0 && (
|
|
147
118
|
<div class="mt-3 flex flex-wrap gap-2">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { ResumeData } from "./typescript-schema";
|
|
3
|
-
import { formatDateRange } from "./typescript-schema";
|
|
3
|
+
import { buildContactEntries, formatDateRange } from "./typescript-schema";
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
data: ResumeData;
|
|
@@ -17,26 +17,14 @@ const locationLabel =
|
|
|
17
17
|
basics.location?.label ||
|
|
18
18
|
[basics.location?.city, basics.location?.country].filter(Boolean).join(", ");
|
|
19
19
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (basics.contact.email) {
|
|
26
|
-
contactItems.push({ label: basics.contact.email, href: `mailto:${basics.contact.email}` });
|
|
27
|
-
}
|
|
28
|
-
if (basics.contact.phone) {
|
|
29
|
-
const phoneHref = basics.contact.phone.replace(/\s+/g, "");
|
|
30
|
-
contactItems.push({ label: basics.contact.phone, href: `tel:${phoneHref}` });
|
|
31
|
-
}
|
|
32
|
-
if (basics.contact.website) {
|
|
33
|
-
contactItems.push({ label: formatUrlLabel(basics.contact.website), href: basics.contact.website });
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const directContactLine = [basics.contact.email, basics.contact.phone].filter(Boolean).join(" • ");
|
|
20
|
+
const contactItems = buildContactEntries(basics, locationLabel);
|
|
21
|
+
const directContactLine = contactItems
|
|
22
|
+
.filter((item) => item.kind === "email" || item.kind === "phone")
|
|
23
|
+
.map((item) => item.label)
|
|
24
|
+
.join(" • ");
|
|
37
25
|
|
|
38
26
|
const skills = data.skills ?? [];
|
|
39
|
-
const links =
|
|
27
|
+
const links = contactItems.filter((item) => item.kind === "link");
|
|
40
28
|
const languages = data.languages ?? [];
|
|
41
29
|
const experienceItems = data.experience ?? [];
|
|
42
30
|
const projects = data.projects ?? [];
|
|
@@ -46,7 +34,7 @@ const declaration = data.declaration ?? {};
|
|
|
46
34
|
const hasDeclaration = Boolean(declaration.text || declaration.place || declaration.name);
|
|
47
35
|
---
|
|
48
36
|
|
|
49
|
-
<div class="resume-template bg-slate-100 text-slate-900 print:bg-white">
|
|
37
|
+
<div class="resume-template av-print-white bg-slate-100 text-slate-900 print:bg-white">
|
|
50
38
|
<main class="mx-auto max-w-4xl p-4 sm:p-6">
|
|
51
39
|
<section class="overflow-hidden rounded-2xl bg-white shadow-sm ring-1 ring-slate-200 print:shadow-none print:ring-0">
|
|
52
40
|
<div class="grid lg:grid-cols-12 print:grid-cols-1">
|
|
@@ -66,7 +54,7 @@ const hasDeclaration = Boolean(declaration.text || declaration.place || declarat
|
|
|
66
54
|
<div class="space-y-1 text-slate-200 print:text-slate-700">
|
|
67
55
|
{directContactLine && <div class="font-medium">{directContactLine}</div>}
|
|
68
56
|
{contactItems
|
|
69
|
-
.filter((item) => item.
|
|
57
|
+
.filter((item) => item.kind !== "email" && item.kind !== "phone")
|
|
70
58
|
.map((item) =>
|
|
71
59
|
item.href ? (
|
|
72
60
|
<a class="underline underline-offset-2 print:no-underline" href={item.href}>
|
|
@@ -96,9 +84,13 @@ const hasDeclaration = Boolean(declaration.text || declaration.place || declarat
|
|
|
96
84
|
<div class="text-sm font-semibold text-slate-100 print:text-slate-900">LINKS</div>
|
|
97
85
|
<div class="space-y-1 text-sm text-slate-200 print:text-slate-700">
|
|
98
86
|
{links.map((link) => (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
87
|
+
link.href ? (
|
|
88
|
+
<a class="underline underline-offset-2 print:no-underline" href={link.href}>
|
|
89
|
+
{link.label}
|
|
90
|
+
</a>
|
|
91
|
+
) : (
|
|
92
|
+
<div>{link.label}</div>
|
|
93
|
+
)
|
|
102
94
|
))}
|
|
103
95
|
</div>
|
|
104
96
|
</div>
|
|
@@ -117,3 +117,150 @@ export function formatDateRange(item: {
|
|
|
117
117
|
const end = item.end.month ? `${m(item.end.month)}/${item.end.year}` : `${item.end.year}`;
|
|
118
118
|
return `${start} — ${end}`;
|
|
119
119
|
}
|
|
120
|
+
|
|
121
|
+
export type ResumeContactEntryKind = "location" | "email" | "phone" | "website" | "link";
|
|
122
|
+
|
|
123
|
+
export type ResumeContactEntry = {
|
|
124
|
+
kind: ResumeContactEntryKind;
|
|
125
|
+
label: string;
|
|
126
|
+
href?: string;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const clean = (value?: string | null) => (value ?? "").replace(/\s+/g, " ").trim();
|
|
130
|
+
|
|
131
|
+
const normalizeEmail = (value?: string | null) => clean(value).toLowerCase();
|
|
132
|
+
|
|
133
|
+
const normalizePhone = (value?: string | null) => clean(value).replace(/[^\d+]/g, "");
|
|
134
|
+
|
|
135
|
+
const normalizeUrlHref = (value?: string | null) => {
|
|
136
|
+
const raw = clean(value);
|
|
137
|
+
if (!raw) return "";
|
|
138
|
+
if (/^mailto:/i.test(raw) || /^tel:/i.test(raw)) return raw;
|
|
139
|
+
const withoutScheme = raw.replace(/^https?:\/\//i, "").replace(/^\/+/, "");
|
|
140
|
+
if (!withoutScheme) return "";
|
|
141
|
+
return `https://${withoutScheme}`;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const canonicalUrlKey = (value?: string | null) => {
|
|
145
|
+
const href = normalizeUrlHref(value);
|
|
146
|
+
if (!href) return "";
|
|
147
|
+
if (/^mailto:/i.test(href) || /^tel:/i.test(href)) return href.toLowerCase();
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const parsed = new URL(href);
|
|
151
|
+
const pathname = parsed.pathname === "/" ? "" : parsed.pathname.replace(/\/$/, "");
|
|
152
|
+
return `${parsed.protocol}//${parsed.hostname.toLowerCase()}${pathname}${parsed.search}`.toLowerCase();
|
|
153
|
+
} catch {
|
|
154
|
+
return href.toLowerCase();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const formatUrlLabel = (value?: string | null) => {
|
|
159
|
+
const href = normalizeUrlHref(value);
|
|
160
|
+
if (!href) return "";
|
|
161
|
+
if (/^mailto:/i.test(href) || /^tel:/i.test(href)) return href;
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const parsed = new URL(href);
|
|
165
|
+
const pathname = parsed.pathname === "/" ? "" : parsed.pathname.replace(/\/$/, "");
|
|
166
|
+
return `${parsed.hostname.toLowerCase()}${pathname}${parsed.search}`;
|
|
167
|
+
} catch {
|
|
168
|
+
return href.replace(/^https?:\/\//i, "").replace(/\/$/, "");
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export const truncateText = (value?: string | null, max = 160) => {
|
|
173
|
+
const normalized = clean(value);
|
|
174
|
+
if (!normalized) return "";
|
|
175
|
+
if (normalized.length <= max) return normalized;
|
|
176
|
+
return `${normalized.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const buildContactEntries = (
|
|
180
|
+
basics: ResumeData["basics"],
|
|
181
|
+
locationLabel?: string,
|
|
182
|
+
): ResumeContactEntry[] => {
|
|
183
|
+
const seen = new Set<string>();
|
|
184
|
+
const entries: ResumeContactEntry[] = [];
|
|
185
|
+
|
|
186
|
+
const pushEntry = (entry: ResumeContactEntry, key: string) => {
|
|
187
|
+
if (!entry.label || !key || seen.has(key)) return;
|
|
188
|
+
seen.add(key);
|
|
189
|
+
entries.push(entry);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const normalizedLocation = clean(locationLabel);
|
|
193
|
+
if (normalizedLocation) {
|
|
194
|
+
pushEntry(
|
|
195
|
+
{ kind: "location", label: normalizedLocation },
|
|
196
|
+
`location:${normalizedLocation.toLowerCase()}`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const email = normalizeEmail(basics.contact.email);
|
|
201
|
+
if (email) {
|
|
202
|
+
pushEntry({ kind: "email", label: email, href: `mailto:${email}` }, `email:${email}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const phoneLabel = clean(basics.contact.phone);
|
|
206
|
+
const phone = normalizePhone(basics.contact.phone);
|
|
207
|
+
if (phoneLabel && phone) {
|
|
208
|
+
pushEntry({ kind: "phone", label: phoneLabel, href: `tel:${phone}` }, `phone:${phone}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const websiteHref = normalizeUrlHref(basics.contact.website);
|
|
212
|
+
if (websiteHref) {
|
|
213
|
+
const websiteKey = canonicalUrlKey(websiteHref);
|
|
214
|
+
pushEntry(
|
|
215
|
+
{
|
|
216
|
+
kind: "website",
|
|
217
|
+
label: formatUrlLabel(websiteHref),
|
|
218
|
+
href: websiteHref,
|
|
219
|
+
},
|
|
220
|
+
`url:${websiteKey}`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
for (const link of basics.links ?? []) {
|
|
225
|
+
const rawLabel = clean(link.label);
|
|
226
|
+
const href = normalizeUrlHref(link.url);
|
|
227
|
+
|
|
228
|
+
if (/^mailto:/i.test(href)) {
|
|
229
|
+
const linkedEmail = normalizeEmail(href.replace(/^mailto:/i, ""));
|
|
230
|
+
if (linkedEmail) {
|
|
231
|
+
pushEntry({ kind: "email", label: linkedEmail, href: `mailto:${linkedEmail}` }, `email:${linkedEmail}`);
|
|
232
|
+
}
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (/^tel:/i.test(href)) {
|
|
237
|
+
const linkedPhone = normalizePhone(href.replace(/^tel:/i, ""));
|
|
238
|
+
if (linkedPhone) {
|
|
239
|
+
pushEntry(
|
|
240
|
+
{ kind: "phone", label: linkedPhone, href: `tel:${linkedPhone}` },
|
|
241
|
+
`phone:${linkedPhone}`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (href) {
|
|
248
|
+
const key = canonicalUrlKey(href);
|
|
249
|
+
pushEntry(
|
|
250
|
+
{
|
|
251
|
+
kind: "link",
|
|
252
|
+
label: rawLabel || formatUrlLabel(href),
|
|
253
|
+
href,
|
|
254
|
+
},
|
|
255
|
+
`url:${key}`,
|
|
256
|
+
);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (rawLabel) {
|
|
261
|
+
pushEntry({ kind: "link", label: rawLabel }, `text:${rawLabel.toLowerCase()}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return entries;
|
|
266
|
+
};
|
package/src/styles/global.css
CHANGED
|
@@ -106,6 +106,15 @@
|
|
|
106
106
|
scrollbar-color: rgba(148, 163, 184, 0.6) transparent;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
@media print {
|
|
110
|
+
html,
|
|
111
|
+
body {
|
|
112
|
+
background: #fff !important;
|
|
113
|
+
background-image: none !important;
|
|
114
|
+
color: #000 !important;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
109
118
|
body::-webkit-scrollbar {
|
|
110
119
|
width: 8px;
|
|
111
120
|
}
|
|
@@ -1980,6 +1989,19 @@
|
|
|
1980
1989
|
page-break-inside: avoid;
|
|
1981
1990
|
}
|
|
1982
1991
|
|
|
1992
|
+
@media print {
|
|
1993
|
+
.av-print-hide {
|
|
1994
|
+
display: none !important;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
.av-print-white {
|
|
1998
|
+
background: #fff !important;
|
|
1999
|
+
color: #000 !important;
|
|
2000
|
+
box-shadow: none !important;
|
|
2001
|
+
background-image: none !important;
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1983
2005
|
/* Loading bar utility */
|
|
1984
2006
|
.av-loading-bar {
|
|
1985
2007
|
height: 4px;
|