usemods-nuxt 0.0.22 → 1.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.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "usemods-nuxt",
3
3
  "configKey": "usemods",
4
- "version": "0.0.22",
4
+ "version": "1.1.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.6.0",
7
7
  "unbuild": "2.0.0"
@@ -0,0 +1,2 @@
1
+ # Synced Files
2
+ Please note all Mod files sync via the `./docs.mjs` here for automagical imports into the Nuxt Website and Nuxt Module. For any changes to the functions please refer to the `./src` folder.
@@ -4,6 +4,7 @@
4
4
  export declare function scrollToAnchor(id: string): Promise<void>;
5
5
  /**
6
6
  * Toggles the body scroll with specified class names and returns a promise
7
+ * @info Use your own class names, or ensure fixed is within your Tailwindcss JIT
7
8
  */
8
9
  export declare function toggleBodyScroll(className?: string, action?: 'add' | 'remove' | 'toggle'): Promise<void>;
9
10
  /**
@@ -4,6 +4,7 @@ export function scrollToAnchor(id) {
4
4
  const selector = `#${id}`;
5
5
  const element = document.querySelector(selector);
6
6
  if (!element) {
7
+ console.warn(`[MODS] Element with id '${id}' not found.`);
7
8
  reject(`Element with id '${id}' not found.`);
8
9
  return;
9
10
  }
@@ -22,17 +23,28 @@ export function toggleBodyScroll(className = "fixed", action = "toggle") {
22
23
  const isFixed = body.classList.contains(className);
23
24
  const scrollY = isFixed ? parseInt(body.style.top, 10) : window.scrollY;
24
25
  body.style.top = isFixed ? "" : `-${scrollY}px`;
25
- body.classList[action](className);
26
+ if (action === "add") {
27
+ body.classList.add(className);
28
+ } else if (action === "remove") {
29
+ body.classList.remove(className);
30
+ } else {
31
+ body.classList.toggle(className);
32
+ }
26
33
  if (isFixed)
27
34
  window.scrollTo(0, -scrollY);
28
35
  resolve();
29
36
  } catch (error) {
37
+ console.warn("[MODS] Failed to toggle body scroll.");
30
38
  reject(error);
31
39
  }
32
40
  });
33
41
  }
34
42
  export function toggleElementScroll(element) {
35
43
  return new Promise((resolve) => {
44
+ if (!element) {
45
+ console.warn("[MODS] Element is required to toggle scroll.");
46
+ return resolve();
47
+ }
36
48
  if (element.dataset.isScrollLocked === "true") {
37
49
  element.style.overflow = "";
38
50
  delete element.dataset.isScrollLocked;
@@ -43,14 +55,16 @@ export function toggleElementScroll(element) {
43
55
  resolve();
44
56
  });
45
57
  }
46
- export function copyToClipboard(value) {
58
+ export async function copyToClipboard(value) {
47
59
  if (!navigator.clipboard || !navigator.clipboard.writeText) {
48
- return Promise.reject("Clipboard API is not available");
60
+ throw new Error("Clipboard API is not available");
49
61
  }
50
- return navigator.clipboard.writeText(String(value)).catch((error) => {
62
+ try {
63
+ await navigator.clipboard.writeText(String(value));
64
+ } catch (error) {
51
65
  console.error("Failed to copy text: ", error);
52
66
  throw error;
53
- });
67
+ }
54
68
  }
55
69
  export function toggleFullScreen() {
56
70
  return new Promise((resolve, reject) => {
@@ -16,7 +16,7 @@ export declare function dataRemoveDuplicates<T extends string | number>(...array
16
16
  /**
17
17
  * Flatten an array of arrays or an object of objects into a single array or object. That was hard to say.
18
18
  */
19
- export declare function dataFlatten(items: object | string[] | number[]): object | string[] | number[];
19
+ export declare function dataFlatten(items: object | any[]): object | any[];
20
20
  /**
21
21
  * Returns an array without a property or properties.
22
22
  */
@@ -32,18 +32,17 @@ export function dataReverse(items) {
32
32
  }
33
33
  export function dataRemoveDuplicates(...arrays) {
34
34
  const mergedArray = arrays.flat();
35
- return mergedArray.filter((item, index) => mergedArray.indexOf(item) === index);
35
+ return Array.from(new Set(mergedArray));
36
36
  }
37
37
  export function dataFlatten(items) {
38
38
  if (isObject(items)) {
39
39
  const flattened = {};
40
- Object.keys(items).forEach((key) => {
41
- const item = items[key];
42
- flattened[key] = Array.isArray(item) ? dataFlatten(item) : item;
43
- });
40
+ for (const [key, value] of Object.entries(items)) {
41
+ flattened[key] = Array.isArray(value) ? dataFlatten(value) : value;
42
+ }
44
43
  return flattened;
45
44
  } else if (Array.isArray(items)) {
46
- return items.reduce((acc, val) => acc.concat(Array.isArray(val) ? dataFlatten(val) : val), []);
45
+ return items.flatMap((item) => Array.isArray(item) ? dataFlatten(item) : item);
47
46
  } else {
48
47
  return items;
49
48
  }
@@ -7,6 +7,7 @@ export declare function detectScrollPosition(): {
7
7
  };
8
8
  /**
9
9
  * Detect the absolute mouse position with the page
10
+ * @info Don't forget to add a mousemove event listener to the window
10
11
  */
11
12
  export declare function detectMousePosition(event: MouseEvent): {
12
13
  x: number;
@@ -14,6 +15,7 @@ export declare function detectMousePosition(event: MouseEvent): {
14
15
  };
15
16
  /**
16
17
  * Detect the relative mouse position with the window size and returns a percentage value
18
+ * @info Don't forget to add a mousemove event listener to the window
17
19
  */
18
20
  export declare function detectRelativeMousePosition(event: MouseEvent): {
19
21
  x: number;
@@ -26,6 +26,15 @@ export declare function formatPercentage(value: number, options?: {
26
26
  decimals?: number;
27
27
  locale?: string;
28
28
  }): string;
29
+ /**
30
+ * Format a number into a your unit of choice
31
+ */
32
+ export declare function formatUnit(value: number, options: {
33
+ unit: string;
34
+ decimals?: number;
35
+ unitDisplay?: 'short' | 'long';
36
+ locale?: string;
37
+ }): string;
29
38
  /**
30
39
  * Format time into a human-readable string
31
40
  */
@@ -40,7 +49,7 @@ export declare function formatDurationNumbers(seconds: number): string;
40
49
  /**
41
50
  * Format numbers into words
42
51
  */
43
- export declare function formatNumberToWords(value: number): string;
52
+ export declare function formatNumberToWords(number: number): string;
44
53
  /**
45
54
  * Generate initials from any string while ignoring common titles
46
55
  */
@@ -68,8 +77,6 @@ export declare function formatTitle(text: string): string;
68
77
  export declare function formatSentenceCase(text: string): string;
69
78
  /**
70
79
  * Adds a space between the last two words in a string to prevent lonely words.
80
+ * @info Remember `text-wrap: pretty` and `text-wrap: balance` are available for most browsers.
71
81
  */
72
82
  export declare function formatTextWrap(value: string): string;
73
- /**
74
- * Format a number into a unit formatting
75
- */
@@ -79,6 +79,17 @@ export function formatPercentage(value, options) {
79
79
  };
80
80
  return new Intl.NumberFormat(options?.locale ?? "en-US", config).format(value);
81
81
  }
82
+ export function formatUnit(value, options) {
83
+ const safeDecimals = Math.max(0, Math.min(options?.decimals ?? 2, 20));
84
+ const config = {
85
+ unit: options.unit,
86
+ style: "unit",
87
+ unitDisplay: options.unitDisplay ?? "long",
88
+ minimumFractionDigits: safeDecimals === 0 ? 0 : safeDecimals === 1 ? 1 : 2,
89
+ maximumFractionDigits: safeDecimals
90
+ };
91
+ return new Intl.NumberFormat(options.locale ?? "en-US", config).format(value);
92
+ }
82
93
  export function formatDurationLabels(seconds, options) {
83
94
  const time = [
84
95
  { unit: options?.labels === "short" ? "yr" : " year", secondsInUnit: 31536e3 },
@@ -116,7 +127,7 @@ export function formatDurationNumbers(seconds) {
116
127
  const remainingSeconds = seconds - hours * 3600 - minutes * 60;
117
128
  return [hours, minutes, remainingSeconds].map((value) => value.toString().padStart(2, "0")).join(":");
118
129
  }
119
- export function formatNumberToWords(value) {
130
+ export function formatNumberToWords(number) {
120
131
  const underTwenty = [
121
132
  "zero",
122
133
  "one",
@@ -140,29 +151,27 @@ export function formatNumberToWords(value) {
140
151
  "nineteen"
141
152
  ];
142
153
  const tens = ["twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];
143
- if (value < 20)
144
- return underTwenty[value];
145
- if (value < 100)
146
- return `${tens[Math.floor(value / 10) - 2]}${value % 10 ? "-" + underTwenty[value % 10] : ""}`;
147
- const formatGroup = (number) => {
148
- if (number >= 100) {
149
- const remainder = number % 100;
150
- return `${underTwenty[Math.floor(number / 100)]} hundred${remainder ? ` and ${formatGroup(remainder)}` : ""}`;
151
- } else if (number >= 20) {
152
- return `${tens[Math.floor(number / 10) - 2]}${number % 10 ? "-" + underTwenty[number % 10] : ""}`;
153
- } else {
154
- return underTwenty[number];
155
- }
156
- };
157
154
  const scales = ["", " thousand", " million", " billion", " trillion", " quadrillion", " quintillion"];
155
+ if (number < 20)
156
+ return underTwenty[number];
157
+ if (number < 100)
158
+ return `${tens[Math.floor(number / 10) - 2]}${number % 10 ? "-" + underTwenty[number % 10] : ""}`;
159
+ const formatGroup = (num) => {
160
+ if (num < 20)
161
+ return underTwenty[num];
162
+ if (num < 100)
163
+ return `${tens[Math.floor(num / 10) - 2]}${num % 10 ? "-" + underTwenty[num % 10] : ""}`;
164
+ const remainder = num % 100;
165
+ return `${underTwenty[Math.floor(num / 100)]} hundred${remainder ? ` and ${formatGroup(remainder)}` : ""}`;
166
+ };
158
167
  let scaleIndex = 0;
159
168
  let result = "";
160
- while (value > 0) {
161
- const groupValue = value % 1e3;
169
+ while (number > 0) {
170
+ const groupValue = number % 1e3;
162
171
  if (groupValue > 0) {
163
172
  result = formatGroup(groupValue) + scales[scaleIndex] + (result ? ", " + result : "");
164
173
  }
165
- value = Math.floor(value / 1e3);
174
+ number = Math.floor(number / 1e3);
166
175
  scaleIndex++;
167
176
  }
168
177
  return result.trim();
@@ -170,10 +179,14 @@ export function formatNumberToWords(value) {
170
179
  export function formatInitials(text, options) {
171
180
  if (!text)
172
181
  return "";
173
- text = text.replace(/(Mrs|Mr|Ms|Dr|Jr|Sr|Prof|Hon|Snr|Jnr|St)\.?/g, "").trim();
182
+ text = text.replace(/\b(Mrs|Mr|Ms|Dr|Jr|Sr|Prof|Hon|Snr|Jnr|St)\b\.?/g, " ").trim();
174
183
  return text.split(" ").filter((word) => !["the", "third"].includes(word.toLowerCase())).map((word) => word.charAt(0).toUpperCase()).join("").substring(0, options?.length ?? 2);
175
184
  }
176
185
  export function formatUnixTime(timestamp) {
186
+ if (isNaN(timestamp) || timestamp < 0 || timestamp > 9999999999) {
187
+ console.warn("[MODS] Invalid Unix timestamp:", timestamp);
188
+ return String(timestamp);
189
+ }
177
190
  return new Date(timestamp * 1e3).toISOString().replace("T", " ").replace("Z", "");
178
191
  }
179
192
  export function formatList(items, options) {
@@ -15,9 +15,19 @@ export declare function generateUuid(): string;
15
15
  */
16
16
  export declare function generateShortId(length?: number): string;
17
17
  /**
18
- * Generate a random, secure password with a mix of character types.
18
+ * Generate a random, secure password with a mix of character types and pleasant special characters.
19
+ * @info Don't forget to use our Password Checker in the Goodies section
19
20
  */
20
- export declare function generatePassword(length?: number): string;
21
+ export declare function generatePassword(options?: {
22
+ length?: number;
23
+ uppercase?: number;
24
+ number?: number;
25
+ special?: number;
26
+ }): string;
27
+ /**
28
+ * Random number generator using cryptographic methods to avoid random().
29
+ */
30
+ export declare function generateRandomIndex(max: number): number;
21
31
  /**
22
32
  * Generate Lorem Ipsum text in various formats.
23
33
  */
@@ -1,6 +1,10 @@
1
1
  export function generateNumber(length) {
2
- const min = Math.pow(10, length - 1);
3
- const max = Math.pow(10, length) - 1;
2
+ if (!Number.isInteger(length) || length <= 0) {
3
+ console.warn("[MODS] Warning: length must be a positive integer");
4
+ return 0;
5
+ }
6
+ const min = 10 ** (length - 1);
7
+ const max = 10 ** length - 1;
4
8
  return Math.floor(Math.random() * (max - min + 1) + min);
5
9
  }
6
10
  export function generateNumberBetween(min, max) {
@@ -19,28 +23,46 @@ export function generateShortId(length = 19) {
19
23
  const randomPart = Math.random().toString().slice(2).toUpperCase();
20
24
  return (timestampPart + randomPart).slice(0, length);
21
25
  }
22
- export function generatePassword(length = 8) {
23
- length = Math.max(length, 8);
24
- const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
25
- const lowercase = uppercase.toLowerCase();
26
- const numbers = "0123456789";
27
- const symbols = "!@#$%^&*";
28
- const allChars = uppercase + lowercase + numbers + symbols;
29
- const passwordArray = [];
30
- const types = [uppercase, lowercase, numbers, symbols];
31
- types.forEach((type) => {
32
- const randomIndex = window.crypto.getRandomValues(new Uint32Array(1))[0] % type.length;
33
- passwordArray.push(type[randomIndex]);
34
- });
35
- for (let i = passwordArray.length; i < length; i++) {
36
- const randomIndex = window.crypto.getRandomValues(new Uint32Array(1))[0] % allChars.length;
37
- passwordArray.push(allChars[randomIndex]);
26
+ export function generatePassword(options) {
27
+ const { length = 8, uppercase = 1, number = 1, special = 1 } = options || {};
28
+ const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
29
+ const numberChars = "0123456789";
30
+ const specialChars = "!@#$%&()_+?";
31
+ const allChars = "abcdefghijklmnopqrstuvwxyz" + uppercaseChars + numberChars + specialChars;
32
+ let password = "";
33
+ password += allChars.charAt(generateRandomIndex(52));
34
+ for (let i = 1; i < length; i++) {
35
+ password += allChars.charAt(Math.floor(Math.random() * allChars.length));
38
36
  }
39
- for (let i = passwordArray.length - 1; i > 0; i--) {
40
- const j = window.crypto.getRandomValues(new Uint32Array(1))[0] % (i + 1);
41
- [passwordArray[i], passwordArray[j]] = [passwordArray[j], passwordArray[i]];
37
+ const ensureCriteria = (regex, chars, count) => {
38
+ while ((password.match(regex) || []).length < count) {
39
+ const randomIndex = generateRandomIndex(password.length);
40
+ password = password.substring(0, randomIndex) + chars.charAt(Math.floor(Math.random() * chars.length)) + password.substring(randomIndex + 1);
41
+ }
42
+ };
43
+ ensureCriteria(/[A-Z]/g, uppercaseChars, uppercase);
44
+ ensureCriteria(/[0-9]/g, numberChars, number);
45
+ ensureCriteria(/[^a-zA-Z0-9]/g, specialChars, special);
46
+ return password;
47
+ }
48
+ export function generateRandomIndex(max) {
49
+ if (max <= 0)
50
+ throw new Error("[MODS] Max value must be a positive integer");
51
+ if (max > 256)
52
+ throw new Error("[MODS] Max value must be less than 256");
53
+ const range = 256 - 256 % max;
54
+ let randomValue;
55
+ if (typeof window !== "undefined" && window.crypto && window.crypto.getRandomValues) {
56
+ do {
57
+ randomValue = window.crypto.getRandomValues(new Uint8Array(1))[0];
58
+ } while (randomValue >= range);
59
+ } else {
60
+ const crypto2 = require("crypto");
61
+ do {
62
+ randomValue = crypto2.randomBytes(1)[0];
63
+ } while (randomValue >= range);
42
64
  }
43
- return passwordArray.join("");
65
+ return randomValue % max;
44
66
  }
45
67
  export function generateLoremIpsum(count = 5, format = "words") {
46
68
  const lorem = "lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".split(" ");
@@ -1,16 +1,23 @@
1
1
  /**
2
2
  * Wraps each word, sentence or paragraph in a string with a tag.
3
+ * @info Don't forget to render the HTML safely.
3
4
  */
4
5
  export declare function splitByWords(text: string): string;
5
6
  /**
6
7
  * Check the strength of a password against a given policy.
8
+ * @info Don't forget to use our Password Generator in the Generators section
7
9
  */
8
- export declare function checkPasswordStrength(value: string, length: number, uppercase: number, numbers: number, special: number): object;
9
- /**
10
- * Returns the reading time of a string in Hours, Minutes, and Seconds.
11
- */
12
- export declare function readingTime(text: string, wordsPerMinute?: number): string;
10
+ export declare function checkPasswordStrength(value: string, options?: {
11
+ length?: number;
12
+ uppercase?: number;
13
+ number?: number;
14
+ special?: number;
15
+ }): object;
13
16
  /**
14
17
  * Replaces placeholders in a string with values from an object.
15
18
  */
16
19
  export declare function mergeFields(text: string, fields: Record<string | number, string | number>): string;
20
+ /**
21
+ * Returns the reading time of a string in Hours, Minutes, and Seconds.
22
+ */
23
+ export declare function readingTime(text: string, wordsPerMinute?: number): string;
@@ -15,7 +15,8 @@ export function splitByWords(text) {
15
15
  }
16
16
  return combinedSentences.join(" ");
17
17
  }
18
- export function checkPasswordStrength(value, length, uppercase, numbers, special) {
18
+ export function checkPasswordStrength(value, options) {
19
+ const { length = 8, uppercase = 1, number = 1, special = 1 } = options || {};
19
20
  let strength = 0;
20
21
  const counts = {
21
22
  uppercase: (value.match(/[A-Z]/g) || []).length,
@@ -26,15 +27,15 @@ export function checkPasswordStrength(value, length, uppercase, numbers, special
26
27
  return { score: 1, label: `Password must be at least ${length} characters long` };
27
28
  if (counts.uppercase < uppercase)
28
29
  return { score: 1, label: `Password must contain ${uppercase} uppercase letter` };
29
- if (counts.numbers < numbers)
30
- return { score: 1, label: `Password must contain ${numbers} number` };
30
+ if (counts.numbers < number)
31
+ return { score: 1, label: `Password must contain ${number} number` };
31
32
  if (counts.special < special)
32
33
  return { score: 1, label: `Password must contain ${special} special character` };
33
34
  if (value.length >= length)
34
35
  strength++;
35
36
  if (counts.uppercase >= uppercase)
36
37
  strength++;
37
- if (counts.numbers >= numbers)
38
+ if (counts.numbers >= number)
38
39
  strength++;
39
40
  if (counts.special >= special)
40
41
  strength++;
@@ -48,11 +49,6 @@ export function checkPasswordStrength(value, length, uppercase, numbers, special
48
49
  return { score: 1, label: "Weak" };
49
50
  return { score: 0, label: "Very Weak" };
50
51
  }
51
- export function readingTime(text, wordsPerMinute = 200) {
52
- const words = text.split(" ").length;
53
- const minutes = Math.ceil(words / wordsPerMinute);
54
- return formatDurationLabels(minutes * 60);
55
- }
56
52
  export function mergeFields(text, fields) {
57
53
  const pattern = /\{\{\s*(\w+)\s*\}\}/g;
58
54
  return text.replace(pattern, (match, key) => {
@@ -64,3 +60,8 @@ export function mergeFields(text, fields) {
64
60
  }
65
61
  });
66
62
  }
63
+ export function readingTime(text, wordsPerMinute = 200) {
64
+ const words = text.split(" ").length;
65
+ const minutes = Math.ceil(words / wordsPerMinute);
66
+ return formatDurationLabels(minutes * 60);
67
+ }
@@ -20,10 +20,12 @@ export declare function endWithout(text: string, end: string): string;
20
20
  export declare function surroundWith(text: string, start: string, end: string): string;
21
21
  /**
22
22
  * Adds plurals to a string except for excluded words.
23
+ * @info This handles most english pluralisation rules, but there are exceptions.
23
24
  */
24
25
  export declare function pluralize(value: string, count: number): string;
25
26
  /**
26
27
  * Removes plurals from a string.
28
+ * @info This handles most english pluralisation rules, but there are exceptions.
27
29
  */
28
30
  export declare function singularize(value: string): string;
29
31
  /**
@@ -139,7 +139,7 @@ export function ordinalize(value) {
139
139
  return value + (suffixes[(remainder - 20) % 10] || suffixes[remainder] || suffixes[0]);
140
140
  }
141
141
  export function stripHtml(text) {
142
- return text.replace(/<[^>]*>/gm, "");
142
+ return text.replace(/<\/?[^>]+(>|$)/g, "");
143
143
  }
144
144
  export function stripWhitespace(text) {
145
145
  return text.replace(/\s+/g, "");
@@ -59,7 +59,6 @@ export declare function range(numbers: number[]): number;
59
59
  */
60
60
  export declare function standardDeviation(numbers: number[]): number;
61
61
  /**
62
- * Returns the measure of asymmetry of the probability distribution of an array of numbers.
63
- * The skewness value can be positive, zero, negative, or undefined.
62
+ * Returns the measure of asymmetry of the probability distribution of an array of numbers. The skewness value can be positive, zero, negative, or undefined.
64
63
  */
65
- export declare function skewness(numbers: number[]): number | undefined;
64
+ export declare function skewness(numbers: number[]): number;
@@ -1,10 +1,16 @@
1
1
  export function sum(numbers) {
2
+ if (numbers.length === 0)
3
+ return 0;
2
4
  return numbers.reduce((a, b) => a + b, 0);
3
5
  }
4
6
  export function mean(numbers) {
7
+ if (numbers.length === 0)
8
+ return 0;
5
9
  return sum(numbers) / numbers.length;
6
10
  }
7
11
  export function average(numbers) {
12
+ if (numbers.length === 0)
13
+ return 0;
8
14
  return mean(numbers);
9
15
  }
10
16
  export function margin(value, percentage) {
@@ -63,7 +69,7 @@ export function skewness(numbers) {
63
69
  const n = numbers.length;
64
70
  const meanValue = mean(numbers);
65
71
  if (standardDeviation(numbers) === 0)
66
- return void 0;
72
+ return 0;
67
73
  let sum2 = 0;
68
74
  for (const num of numbers)
69
75
  sum2 += (num - meanValue) ** 3;
@@ -1,5 +1,5 @@
1
1
  export function isEmail(value) {
2
- const regex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
2
+ const regex = /^(?!.*[._+-]{2})(?!.*[._+-]$)[a-zA-Z0-9._+-]+(?<!\.)@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
3
3
  return regex.test(value);
4
4
  }
5
5
  export function isNumber(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usemods-nuxt",
3
- "version": "0.0.22",
3
+ "version": "1.1.0",
4
4
  "description": "Zippy little modifiers and utilities for your Nuxt app.",
5
5
  "repository": {
6
6
  "type": "git",