@nuxtify/pages 0.1.0

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.
Files changed (53) hide show
  1. package/README.md +203 -0
  2. package/dist/module.cjs +5 -0
  3. package/dist/module.d.mts +139 -0
  4. package/dist/module.d.ts +139 -0
  5. package/dist/module.json +13 -0
  6. package/dist/module.mjs +138 -0
  7. package/dist/runtime/components/EmailSubscribeForm.vue +215 -0
  8. package/dist/runtime/components/FooterCallToAction.vue +38 -0
  9. package/dist/runtime/components/app/AppAnnouncementBar.vue +40 -0
  10. package/dist/runtime/components/app/AppBar.vue +128 -0
  11. package/dist/runtime/components/app/AppDialog.vue +40 -0
  12. package/dist/runtime/components/app/AppFooter.vue +150 -0
  13. package/dist/runtime/components/app/AppLoading.vue +16 -0
  14. package/dist/runtime/components/app/AppLogo.vue +55 -0
  15. package/dist/runtime/components/app/AppNavigationDrawer.vue +101 -0
  16. package/dist/runtime/components/app/AppToast.vue +18 -0
  17. package/dist/runtime/composables/dialog.d.ts +13 -0
  18. package/dist/runtime/composables/dialog.js +15 -0
  19. package/dist/runtime/composables/nuxtify.d.ts +2 -0
  20. package/dist/runtime/composables/nuxtify.js +2 -0
  21. package/dist/runtime/composables/state.d.ts +15 -0
  22. package/dist/runtime/composables/state.js +10 -0
  23. package/dist/runtime/layouts/DefaultLayout.vue +33 -0
  24. package/dist/runtime/pages/DynamicSlug.vue +23 -0
  25. package/dist/runtime/pages/IndexPage.vue +23 -0
  26. package/dist/runtime/server/composables/client.d.ts +1 -0
  27. package/dist/runtime/server/composables/client.js +1 -0
  28. package/dist/runtime/server/tsconfig.json +3 -0
  29. package/dist/runtime/server/utils/client.d.ts +1 -0
  30. package/dist/runtime/server/utils/client.js +1 -0
  31. package/dist/runtime/utils/email.d.ts +5 -0
  32. package/dist/runtime/utils/email.js +26 -0
  33. package/dist/runtime/utils/formRules.d.ts +18 -0
  34. package/dist/runtime/utils/formRules.js +22 -0
  35. package/dist/runtime/utils/icons.d.ts +1 -0
  36. package/dist/runtime/utils/icons.js +1 -0
  37. package/dist/runtime/utils/io.d.ts +2 -0
  38. package/dist/runtime/utils/io.js +19 -0
  39. package/dist/runtime/utils/math.d.ts +2 -0
  40. package/dist/runtime/utils/math.js +13 -0
  41. package/dist/runtime/utils/text.d.ts +23 -0
  42. package/dist/runtime/utils/text.js +159 -0
  43. package/dist/runtime/utils/time.d.ts +2 -0
  44. package/dist/runtime/utils/time.js +7 -0
  45. package/dist/runtime/utils/url.d.ts +9 -0
  46. package/dist/runtime/utils/url.js +27 -0
  47. package/dist/runtime/utils/util.d.ts +7 -0
  48. package/dist/runtime/utils/util.js +5 -0
  49. package/dist/runtime/utils/youtube.d.ts +1 -0
  50. package/dist/runtime/utils/youtube.js +13 -0
  51. package/dist/types.d.mts +7 -0
  52. package/dist/types.d.ts +7 -0
  53. package/package.json +60 -0
@@ -0,0 +1,101 @@
1
+ <script setup lang="ts">
2
+ import { useDisplay, useDrawer, useNuxtifyConfig, mdiArrowTopRight } from '#imports'
3
+
4
+ // App state
5
+ const { smAndDown } = useDisplay()
6
+ const nuxtifyConfig = useNuxtifyConfig()
7
+ const drawer = useDrawer()
8
+
9
+ // Navigation
10
+ const primaryNavLinks = nuxtifyConfig.navigation?.primary
11
+ const secondaryNavLinks = nuxtifyConfig.navigation?.secondary
12
+ const featuredSecondaryLink = secondaryNavLinks?.slice(0, 1)[0] // first link gets featured
13
+ </script>
14
+
15
+ <template>
16
+ <v-navigation-drawer
17
+ v-if="smAndDown"
18
+ v-model="drawer"
19
+ location="right"
20
+ >
21
+ <nav>
22
+ <v-list nav>
23
+ <!-- Primary links -->
24
+ <v-list-item
25
+ v-for="(link, i) in primaryNavLinks"
26
+ :key="i"
27
+ :to="link.to"
28
+ :href="link.href"
29
+ :prepend-icon="link.icon"
30
+ color="primary"
31
+ :target="link.openInNew ? '_blank' : undefined"
32
+ :rel="link.openInNew ? 'noopener nofollow' : undefined"
33
+ slim
34
+ exact
35
+ >
36
+ <v-list-item-title class="text-subtitle-1 font-weight-bold py-1">
37
+ {{ link.text }}
38
+ <v-icon
39
+ v-if="link.openInNew"
40
+ :icon="mdiArrowTopRight"
41
+ size="x-small"
42
+ color="grey"
43
+ class="ml-1"
44
+ />
45
+ </v-list-item-title>
46
+ </v-list-item>
47
+
48
+ <!-- Secondary links -->
49
+ <v-list-item
50
+ v-for="link in secondaryNavLinks?.slice(1).reverse()"
51
+ :key="link.text"
52
+ :to="link.to"
53
+ :href="link.href"
54
+ :prepend-icon="link.icon"
55
+ :target="link.openInNew ? '_blank' : undefined"
56
+ :rel="link.openInNew ? 'noopener nofollow' : undefined"
57
+ slim
58
+ exact
59
+ >
60
+ <v-list-item-title class="text-subtitle-1 font-weight-bold py-1">
61
+ {{ link.text }}
62
+ <v-icon
63
+ v-if="link.openInNew"
64
+ :icon="mdiArrowTopRight"
65
+ size="x-small"
66
+ color="grey"
67
+ class="ml-1"
68
+ />
69
+ </v-list-item-title>
70
+ </v-list-item>
71
+ </v-list>
72
+ </nav>
73
+
74
+ <template #append>
75
+ <!-- Featured secondary link -->
76
+ <div
77
+ v-if="featuredSecondaryLink?.text"
78
+ class="ma-2"
79
+ >
80
+ <v-btn
81
+ :to="featuredSecondaryLink.to"
82
+ :href="featuredSecondaryLink.href"
83
+ :prepend-icon="featuredSecondaryLink.icon"
84
+ variant="flat"
85
+ size="large"
86
+ :target="featuredSecondaryLink.openInNew ? '_blank' : undefined"
87
+ :rel="featuredSecondaryLink.openInNew ? 'noopener nofollow' : undefined"
88
+ block
89
+ >
90
+ {{ featuredSecondaryLink.text }}
91
+ <v-icon
92
+ v-if="featuredSecondaryLink.openInNew"
93
+ :icon="mdiArrowTopRight"
94
+ size="small"
95
+ class="ml-1"
96
+ />
97
+ </v-btn>
98
+ </div>
99
+ </template>
100
+ </v-navigation-drawer>
101
+ </template>
@@ -0,0 +1,18 @@
1
+ <script setup lang="ts">
2
+ import { useToast } from '#imports'
3
+
4
+ // App state
5
+ const toast = useToast()
6
+ </script>
7
+
8
+ <template>
9
+ <v-snackbar
10
+ v-model="toast.show"
11
+ :timeout="5000"
12
+ color="info"
13
+ :min-width="0"
14
+ close-on-content-click
15
+ >
16
+ {{ toast.message }}
17
+ </v-snackbar>
18
+ </template>
@@ -0,0 +1,13 @@
1
+ export interface Dialog {
2
+ show: boolean;
3
+ title: string;
4
+ message: string;
5
+ action: {
6
+ function: () => Promise<void>;
7
+ buttonText: string;
8
+ buttonColor: string;
9
+ };
10
+ closeButtonText: string;
11
+ }
12
+ export declare const dialogInitialState: Dialog;
13
+ export declare const useDialog: () => import("vue").Ref<Dialog, Dialog>;
@@ -0,0 +1,15 @@
1
+ import { useState } from "#imports";
2
+ export const dialogInitialState = {
3
+ show: false,
4
+ title: "",
5
+ message: "",
6
+ action: {
7
+ function: () => new Promise((resolve) => resolve()),
8
+ buttonText: "",
9
+ buttonColor: ""
10
+ },
11
+ closeButtonText: "Cancel"
12
+ };
13
+ export const useDialog = () => useState("dialog", () => {
14
+ return dialogInitialState;
15
+ });
@@ -0,0 +1,2 @@
1
+ import type { ModuleOptions } from '../../types.js';
2
+ export declare const useNuxtifyConfig: () => ModuleOptions;
@@ -0,0 +1,2 @@
1
+ import { useAppConfig } from "#imports";
2
+ export const useNuxtifyConfig = () => useAppConfig().nuxtify;
@@ -0,0 +1,15 @@
1
+ export declare const useDrawer: () => import("vue").Ref<boolean | null, boolean | null>;
2
+ export declare const useToast: () => import("vue").Ref<{
3
+ show: boolean;
4
+ message: string;
5
+ } | {
6
+ show: boolean;
7
+ message: string;
8
+ }, {
9
+ show: boolean;
10
+ message: string;
11
+ } | {
12
+ show: boolean;
13
+ message: string;
14
+ }>;
15
+ export declare const useErrorMessage: () => import("vue").Ref<string, string>;
@@ -0,0 +1,10 @@
1
+ import { ref, useState } from "#imports";
2
+ export const useDrawer = () => useState("drawer", () => null);
3
+ export const useToast = () => useState(
4
+ "toast",
5
+ () => ref({
6
+ show: false,
7
+ message: ""
8
+ })
9
+ );
10
+ export const useErrorMessage = () => useState("errorMessage", () => "");
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ import { useDisplay, useNuxtifyConfig } from '#imports'
3
+
4
+ // App state
5
+ const nuxtifyConfig = useNuxtifyConfig()
6
+ const { mdAndUp } = useDisplay()
7
+ </script>
8
+
9
+ <template>
10
+ <v-app>
11
+ <!-- Accessibility -->
12
+ <NuxtRouteAnnouncer />
13
+
14
+ <AppBar class="d-print-none" />
15
+
16
+ <AppNavigationDrawer class="d-print-none" />
17
+
18
+ <AppAnnouncementBar
19
+ v-if="nuxtifyConfig.announcement?.show"
20
+ class="d-print-none"
21
+ />
22
+
23
+ <v-main :min-height="mdAndUp ? 800 : 550">
24
+ <slot />
25
+ </v-main>
26
+
27
+ <AppToast />
28
+
29
+ <v-footer class="bg-primary justify-center mt-8">
30
+ <AppFooter />
31
+ </v-footer>
32
+ </v-app>
33
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ import { useRoute, capitalizeFirstLetter } from '#imports'
3
+
4
+ // Page info
5
+ const route = useRoute()
6
+ </script>
7
+
8
+ <template>
9
+ <v-container>
10
+ <v-row justify="center">
11
+ <v-col
12
+ cols="12"
13
+ sm="10"
14
+ lg="8"
15
+ xl="6"
16
+ >
17
+ <h1 class="text-h5 mt-4">
18
+ {{ capitalizeFirstLetter(route.params.slug as string) }}
19
+ </h1>
20
+ </v-col>
21
+ </v-row>
22
+ </v-container>
23
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ import { useServerSeoMeta, useNuxtifyConfig } from '#imports'
3
+
4
+ // Page info
5
+ useServerSeoMeta({
6
+ title: '@nuxtify/pages',
7
+ description: 'This is the @nuxtify/pages homepage.',
8
+ })
9
+
10
+ // App state
11
+ const nuxtifyConfig = useNuxtifyConfig()
12
+ </script>
13
+
14
+ <template>
15
+ <v-container class="text-center">
16
+ <ClientOnly>
17
+ <h1>{{ nuxtifyConfig.brand?.name }} Home</h1>
18
+ <template #fallback>
19
+ <AppLoading />
20
+ </template>
21
+ </ClientOnly>
22
+ </v-container>
23
+ </template>
@@ -0,0 +1 @@
1
+ export { useNuxtifyConfig } from '../../composables/nuxtify.js';
@@ -0,0 +1 @@
1
+ export { useNuxtifyConfig } from "../../composables/nuxtify.js";
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../../.nuxt/tsconfig.server.json",
3
+ }
@@ -0,0 +1 @@
1
+ export { slugify, unslugify } from '../../utils/text.js';
@@ -0,0 +1 @@
1
+ export { slugify, unslugify } from "../../utils/text.js";
@@ -0,0 +1,5 @@
1
+ export declare const submitFormData: (submitUrl: string, formData: FormData) => Promise<{
2
+ isSubmitted: boolean;
3
+ isError: boolean;
4
+ errorMessage: string;
5
+ }>;
@@ -0,0 +1,26 @@
1
+ export const submitFormData = async (submitUrl, formData) => {
2
+ let isSubmitted = false;
3
+ let isError = false;
4
+ let errorMessage = "";
5
+ try {
6
+ const res = await fetch(submitUrl, {
7
+ method: "POST",
8
+ body: formData
9
+ });
10
+ if (res.ok) {
11
+ isSubmitted = true;
12
+ } else {
13
+ throw new Error(`${res.status} Error`);
14
+ }
15
+ } catch (error) {
16
+ isError = true;
17
+ if (error instanceof Error) {
18
+ errorMessage = error.message;
19
+ }
20
+ }
21
+ return {
22
+ isSubmitted,
23
+ isError,
24
+ errorMessage
25
+ };
26
+ };
@@ -0,0 +1,18 @@
1
+ export declare const formRules: {
2
+ required: (v: string) => true | "Required";
3
+ requiredArray: <T>(v: Array<T>) => true | "Required";
4
+ validEmail: (v: string) => true | "Must be a valid email address";
5
+ minLength8: (v: string) => true | "Must be at least 8 characters";
6
+ minLength12: (v: string) => true | "Must be at least 12 characters";
7
+ maxLength30: (v: string) => true | "Must not be longer than 30 characters";
8
+ maxLength60: (v: string) => true | "Must not be longer than 60 characters";
9
+ maxLength120: (v: string) => true | "Must not be longer than 120 characters";
10
+ maxLength200: (v: string) => true | "Must not be longer than 200 characters";
11
+ maxLength300: (v: string) => true | "Must not be longer than 300 characters";
12
+ maxLength600: (v: string) => true | "Must not be longer than 600 characters";
13
+ isInteger: (v: string) => true | "Must be an integer";
14
+ gt0: (v: string) => true | "Must be greater than 0";
15
+ gte6: (v: string) => true | "Must be greater than or equal to 6";
16
+ lte12: (v: string) => true | "Must be less than or equal to 12";
17
+ lte365: (v: string) => true | "Must be less than or equal to 365";
18
+ };
@@ -0,0 +1,22 @@
1
+ export const formRules = {
2
+ // Required
3
+ required: (v) => !!v || "Required",
4
+ requiredArray: (v) => !!v.length || "Required",
5
+ // Text
6
+ validEmail: (v) => /.+@.+/.test(v) || "Must be a valid email address",
7
+ // Length
8
+ minLength8: (v) => v ? v.length >= 8 || "Must be at least 8 characters" : true,
9
+ minLength12: (v) => v ? v.length >= 12 || "Must be at least 12 characters" : true,
10
+ maxLength30: (v) => v ? v.length <= 30 || "Must not be longer than 30 characters" : true,
11
+ maxLength60: (v) => v ? v.length <= 60 || "Must not be longer than 60 characters" : true,
12
+ maxLength120: (v) => v ? v.length <= 120 || "Must not be longer than 120 characters" : true,
13
+ maxLength200: (v) => v ? v.length <= 200 || "Must not be longer than 200 characters" : true,
14
+ maxLength300: (v) => v ? v.length <= 300 || "Must not be longer than 300 characters" : true,
15
+ maxLength600: (v) => v ? v.length <= 600 || "Must not be longer than 600 characters" : true,
16
+ // Number
17
+ isInteger: (v) => Number.isInteger(+v) || "Must be an integer",
18
+ gt0: (v) => Number.parseFloat(v) > 0 || "Must be greater than 0",
19
+ gte6: (v) => Number.parseFloat(v) >= 6 || "Must be greater than or equal to 6",
20
+ lte12: (v) => Number.parseFloat(v) <= 12 || "Must be less than or equal to 12",
21
+ lte365: (v) => Number.parseFloat(v) <= 365 || "Must be less than or equal to 365"
22
+ };
@@ -0,0 +1 @@
1
+ export { mdiArrowRight, mdiClose, mdiMenu, mdiArrowTopRight } from '@mdi/js';
@@ -0,0 +1 @@
1
+ export { mdiArrowRight, mdiClose, mdiMenu, mdiArrowTopRight } from "@mdi/js";
@@ -0,0 +1,2 @@
1
+ export declare function blobToDataURL(blob: Blob): Promise<string>;
2
+ export declare function filenameToUrl(filename: string, timestamped?: boolean): string;
@@ -0,0 +1,19 @@
1
+ export function blobToDataURL(blob) {
2
+ return new Promise((resolve, reject) => {
3
+ const reader = new FileReader();
4
+ reader.onload = (_e) => resolve(reader.result);
5
+ reader.onerror = (_e) => reject(reader.error);
6
+ reader.onabort = (_e) => reject(new Error("Read aborted"));
7
+ reader.readAsDataURL(blob);
8
+ });
9
+ }
10
+ export function filenameToUrl(filename, timestamped = false) {
11
+ const filenameClean = encodeURIComponent(
12
+ filename.trim().replace(/[^a-z0-9.-]/gi, "-").replace(/-{2,}/g, "-").replace(/\.{2,}/g, ".").replace(/-\./g, ".").replace(/-$/g, "").replace(/^-/g, "").slice(0, 500)
13
+ // limit to the first 500 characters
14
+ );
15
+ if (timestamped) {
16
+ return `${Date.now()}-${filenameClean}`;
17
+ }
18
+ return filenameClean;
19
+ }
@@ -0,0 +1,2 @@
1
+ export declare function toPercentage(val: number, decimals?: number): string;
2
+ export declare function roundToFixed(val: number | string, positions?: number): number;
@@ -0,0 +1,13 @@
1
+ export function toPercentage(val, decimals = 0) {
2
+ const percentage = (val * 100).toFixed(decimals);
3
+ return `${percentage}%`;
4
+ }
5
+ export function roundToFixed(val, positions = 2) {
6
+ let text = 0;
7
+ if (typeof val === "string") {
8
+ text = +Number.parseFloat(val).toFixed(positions);
9
+ } else if (typeof val === "number") {
10
+ text = +val.toFixed(positions);
11
+ }
12
+ return text;
13
+ }
@@ -0,0 +1,23 @@
1
+ export declare const titleCase: (text: string, skipShortWords?: boolean) => string;
2
+ export declare function capitalizeFirstLetter(text: string): string;
3
+ export declare function fullName(firstName: string, lastName: string): string;
4
+ export declare function parseFullName(fullName: string | undefined | null): {
5
+ firstName: string;
6
+ lastName: string;
7
+ };
8
+ export declare const honoraryName: (name: string, honorificSuffix?: string) => string;
9
+ export declare function getPronouns(gender: string): {
10
+ single: string;
11
+ plural: string;
12
+ possessive: string;
13
+ possessivePlural: string;
14
+ };
15
+ export declare const formatPhone: (input: string, separator?: string) => string;
16
+ export declare function formatDate(date: string, locale?: string): string;
17
+ export declare function formatDateTime(date: string, locale?: string): string;
18
+ export declare function booleanToText(bool: boolean): "Yes" | "No";
19
+ export declare const truncate: (text: string, maxLength?: number, ellipses?: boolean) => string;
20
+ export declare const slugify: (text: string) => string;
21
+ export declare const unslugify: (text: string) => string;
22
+ export declare function pluralize(value: number, units: string, showValue?: boolean): string;
23
+ export declare const getLanguageName: (languageCode: string, displayLanguage?: string) => string | undefined;
@@ -0,0 +1,159 @@
1
+ export const titleCase = (text, skipShortWords = true) => {
2
+ const shortWords = ["and", "an", "the", "a", "but", "for", "at", "by", "to"];
3
+ const words = text.split(" ");
4
+ const titleWords = [];
5
+ for (const [i] of words.entries()) {
6
+ if (skipShortWords && shortWords.includes(words[i])) {
7
+ if (i === 0) {
8
+ titleWords.push(words[i].charAt(0).toUpperCase() + words[i].slice(1));
9
+ } else {
10
+ titleWords.push(words[i]);
11
+ }
12
+ } else {
13
+ titleWords.push(words[i].charAt(0).toUpperCase() + words[i].slice(1));
14
+ }
15
+ }
16
+ return titleWords.join(" ");
17
+ };
18
+ export function capitalizeFirstLetter(text) {
19
+ if (!text) {
20
+ return "";
21
+ }
22
+ return text.charAt(0).toUpperCase() + text.slice(1);
23
+ }
24
+ export function fullName(firstName, lastName) {
25
+ let name = "";
26
+ if (firstName && lastName) {
27
+ name = `${firstName} ${lastName}`;
28
+ } else if (firstName) {
29
+ name = firstName;
30
+ } else if (lastName) {
31
+ name = lastName;
32
+ }
33
+ return name;
34
+ }
35
+ export function parseFullName(fullName2) {
36
+ let firstName = "";
37
+ let lastName = "";
38
+ if (fullName2) {
39
+ const nameSplit = fullName2.split(" ");
40
+ firstName = nameSplit[0];
41
+ if (nameSplit.length > 1) {
42
+ lastName = nameSplit.slice(1).join(" ");
43
+ }
44
+ }
45
+ return { firstName, lastName };
46
+ }
47
+ export const honoraryName = (name, honorificSuffix = "") => {
48
+ return honorificSuffix ? `${name}, ${honorificSuffix}` : name;
49
+ };
50
+ export function getPronouns(gender) {
51
+ if (gender === "male") {
52
+ return {
53
+ single: "he",
54
+ plural: "he's",
55
+ possessive: "his",
56
+ possessivePlural: "his"
57
+ };
58
+ } else if (gender === "female") {
59
+ return {
60
+ single: "she",
61
+ plural: "she's",
62
+ possessive: "her",
63
+ possessivePlural: "her's"
64
+ };
65
+ } else {
66
+ return {
67
+ single: "they",
68
+ plural: "they're",
69
+ possessive: "their",
70
+ possessivePlural: "theirs"
71
+ };
72
+ }
73
+ }
74
+ export const formatPhone = (input, separator = "-") => {
75
+ let phone = input;
76
+ if (phone.slice(0, 2) === "+1") {
77
+ phone = phone.slice(2);
78
+ }
79
+ if (phone.length === 10) {
80
+ phone = phone.replace(
81
+ /(\d{3})(\d{3})(\d{4})/,
82
+ `$1${separator}$2${separator}$3`
83
+ );
84
+ }
85
+ return phone;
86
+ };
87
+ export function formatDate(date, locale = "en-us") {
88
+ return new Date(date).toLocaleDateString(locale, {
89
+ year: "numeric",
90
+ month: "long",
91
+ day: "numeric"
92
+ });
93
+ }
94
+ export function formatDateTime(date, locale = "en-us") {
95
+ return new Date(date).toLocaleString(locale, {
96
+ year: "numeric",
97
+ month: "long",
98
+ day: "numeric",
99
+ hour: "numeric",
100
+ minute: "numeric"
101
+ });
102
+ }
103
+ export function booleanToText(bool) {
104
+ return bool ? "Yes" : "No";
105
+ }
106
+ export const truncate = (text, maxLength = 80, ellipses = true) => {
107
+ if (text.length > maxLength) {
108
+ let trimmedString = text.substring(0, maxLength + 1);
109
+ trimmedString = trimmedString.substring(
110
+ 0,
111
+ Math.min(trimmedString.length, trimmedString.lastIndexOf(" "))
112
+ );
113
+ return `${trimmedString}${ellipses ? "..." : ""}`;
114
+ }
115
+ return text;
116
+ };
117
+ export const slugify = (text) => {
118
+ const slug = text.normalize("NFKD").toLowerCase().trim().replace(/\s+/g, "-").replace(/_/g, "-").replace(/-{2,}/g, "-").replace(/-$/g, "");
119
+ return slug;
120
+ };
121
+ export const unslugify = (text) => {
122
+ const unslug = text.replace(/-/g, " ");
123
+ return unslug;
124
+ };
125
+ export function pluralize(value, units, showValue = true) {
126
+ let text = "";
127
+ let unitsPlural = units;
128
+ if (unitsPlural.slice(-1) === "y") {
129
+ unitsPlural = unitsPlural.slice(0, -1) + "ies";
130
+ } else {
131
+ unitsPlural = units + "s";
132
+ }
133
+ if (showValue) {
134
+ if (value === 1) {
135
+ text = `${value} ${units}`;
136
+ } else {
137
+ text = `${value} ${unitsPlural}`;
138
+ }
139
+ } else if (!value || value === 1) {
140
+ text = `${units}`;
141
+ } else {
142
+ text = `${unitsPlural}`;
143
+ }
144
+ return text;
145
+ }
146
+ export const getLanguageName = (languageCode, displayLanguage = "en") => {
147
+ try {
148
+ const languageNames = new Intl.DisplayNames([displayLanguage], {
149
+ type: "language"
150
+ });
151
+ return languageNames.of(languageCode);
152
+ } catch (error) {
153
+ console.error(
154
+ `Error getting language name for code '${languageCode}' with locale '${displayLanguage}':`,
155
+ error
156
+ );
157
+ return languageCode;
158
+ }
159
+ };
@@ -0,0 +1,2 @@
1
+ export declare const delay: (ms: number) => Promise<unknown>;
2
+ export declare function daysSince(targetDate: Date): number;
@@ -0,0 +1,7 @@
1
+ export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2
+ export function daysSince(targetDate) {
3
+ const currentDate = /* @__PURE__ */ new Date();
4
+ const timeDifference = currentDate.getTime() - targetDate.getTime();
5
+ const daysDifference = Math.floor(timeDifference / (1e3 * 60 * 60 * 24));
6
+ return daysDifference;
7
+ }
@@ -0,0 +1,9 @@
1
+ export declare const getBaseUrl: (url: string) => string;
2
+ export declare const getUtmParams: (url: string) => {
3
+ utmSource: string | null;
4
+ utmMedium: string | null;
5
+ utmCampaign: string | null;
6
+ utmTerm: string | null;
7
+ utmContent: string | null;
8
+ };
9
+ export declare const isExternalUrl: (url: string, hostname: string) => boolean;
@@ -0,0 +1,27 @@
1
+ export const getBaseUrl = (url) => {
2
+ const urlObject = new URL(url);
3
+ urlObject.search = "";
4
+ urlObject.hash = "";
5
+ return urlObject.href;
6
+ };
7
+ export const getUtmParams = (url) => {
8
+ const urlSearchParams = new URLSearchParams(new URL(url).search);
9
+ return {
10
+ utmSource: urlSearchParams.get("utm_source"),
11
+ utmMedium: urlSearchParams.get("utm_medium"),
12
+ utmCampaign: urlSearchParams.get("utm_campaign"),
13
+ utmTerm: urlSearchParams.get("utm_term"),
14
+ utmContent: urlSearchParams.get("utm_content")
15
+ };
16
+ };
17
+ export const isExternalUrl = (url, hostname) => {
18
+ if (!url) {
19
+ return false;
20
+ }
21
+ if (url.startsWith("/")) {
22
+ return false;
23
+ } else {
24
+ const linkHostname = new URL(url).hostname;
25
+ return linkHostname !== hostname;
26
+ }
27
+ };
@@ -0,0 +1,7 @@
1
+ export declare const addOrReplaceArrayItem: (array: {
2
+ id: string | number;
3
+ [key: string]: unknown;
4
+ }[], element: {
5
+ id: string | number;
6
+ [key: string]: unknown;
7
+ }) => void;
@@ -0,0 +1,5 @@
1
+ export const addOrReplaceArrayItem = (array, element) => {
2
+ const i = array.findIndex((e) => e.id === element.id);
3
+ if (i > -1) array[i] = element;
4
+ else array.push(element);
5
+ };