feat: initial commit
This commit is contained in:
commit
0ff7457679
192 changed files with 24379 additions and 0 deletions
111
src/utils/helpers/general.js
Normal file
111
src/utils/helpers/general.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
import { convert } from "html-to-text";
|
||||
import { format } from "date-fns-tz";
|
||||
import markdownIt from "markdown-it";
|
||||
import markdownItAnchor from "markdown-it-anchor";
|
||||
import markdownItFootnote from "markdown-it-footnote";
|
||||
import hljs from "highlight.js";
|
||||
import "highlight.js/styles/github-dark.min.css";
|
||||
import truncateHtml from "truncate-html";
|
||||
|
||||
// arrays
|
||||
export const shuffleArray = (array) => {
|
||||
const shuffled = [...array];
|
||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||
}
|
||||
return shuffled;
|
||||
};
|
||||
|
||||
// countries
|
||||
const regionNames = new Intl.DisplayNames(["en"], { type: "region" });
|
||||
|
||||
export const getCountryName = (countryCode) => {
|
||||
try {
|
||||
return regionNames.of(countryCode.trim()) || countryCode.trim();
|
||||
} catch {
|
||||
return countryCode.trim();
|
||||
}
|
||||
};
|
||||
|
||||
export const parseCountryField = (countryField) => {
|
||||
if (!countryField) return null;
|
||||
|
||||
const delimiters = [",", "/", "&", "and"];
|
||||
return delimiters
|
||||
.reduce(
|
||||
(countries, delimiter) =>
|
||||
countries.flatMap((country) => country.split(delimiter)),
|
||||
[countryField]
|
||||
)
|
||||
.map(getCountryName)
|
||||
.join(", ");
|
||||
};
|
||||
|
||||
// html
|
||||
export const htmlTruncate = (content, limit = 50) =>
|
||||
truncateHtml(content, limit, {
|
||||
byWords: true,
|
||||
ellipsis: "...",
|
||||
});
|
||||
|
||||
export const htmlToText = (html) =>
|
||||
convert(html, {
|
||||
wordwrap: false,
|
||||
selectors: [
|
||||
{ selector: "a", options: { ignoreHref: true } },
|
||||
{ selector: "h1", options: { uppercase: false } },
|
||||
{ selector: "h2", options: { uppercase: false } },
|
||||
{ selector: "h3", options: { uppercase: false } },
|
||||
{ selector: "*", format: "block" },
|
||||
],
|
||||
});
|
||||
|
||||
export const escapeHtml = (input) =>
|
||||
typeof input === "string"
|
||||
? input.replace(/[&<>"']/g, (char) => {
|
||||
const map = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'",
|
||||
};
|
||||
return map[char];
|
||||
})
|
||||
: "";
|
||||
|
||||
// markdown
|
||||
const markdown = markdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
breaks: true,
|
||||
highlight: (code, lang) => {
|
||||
if (lang && hljs.getLanguage(lang))
|
||||
return hljs.highlight(code, { language: lang }).value;
|
||||
return hljs.highlightAuto(code).value;
|
||||
},
|
||||
});
|
||||
markdown
|
||||
.use(markdownItAnchor, {
|
||||
level: [1, 2],
|
||||
permalink: markdownItAnchor.permalink.headerLink({
|
||||
safariReaderFix: true,
|
||||
}),
|
||||
})
|
||||
.use(markdownItFootnote);
|
||||
|
||||
export const md = (string) => markdown.render(string);
|
||||
|
||||
// urls
|
||||
export const encodeAmp = (url) => url.replace(/&/g, "&");
|
||||
export const removeTrailingSlash = (url) => url.replace(/\/$/, "");
|
||||
|
||||
export const isExcludedPath = (path, exclusions) =>
|
||||
exclusions.some((exclusion) => path.includes(exclusion));
|
||||
|
||||
// dates
|
||||
export const dateToRFC822 = (date) =>
|
||||
format(date, "EEE, dd MMM yyyy HH:mm:ss XXX", {
|
||||
timeZone: "America/Los_Angeles",
|
||||
});
|
73
src/utils/helpers/media.js
Normal file
73
src/utils/helpers/media.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
import countries from "i18n-iso-countries";
|
||||
import enLocale from "i18n-iso-countries/langs/en.json";
|
||||
|
||||
countries.registerLocale(enLocale);
|
||||
|
||||
export const filterBooksByStatus = (books, status) =>
|
||||
books.filter((book) => book["status"] === status);
|
||||
|
||||
export const findFavoriteBooks = (books) =>
|
||||
books.filter((book) => book["favorite"] === true);
|
||||
|
||||
export const bookYearLinks = (years) =>
|
||||
years
|
||||
.sort((a, b) => b["value"] - a["value"])
|
||||
.map(
|
||||
(year, index) =>
|
||||
`<a href="/books/years/${year["value"]}">${year["value"]}</a>${
|
||||
index < years.length - 1 ? " / " : ""
|
||||
}`
|
||||
)
|
||||
.join("");
|
||||
|
||||
export const mediaLinks = (data, type, count = 10) => {
|
||||
if (!data || !type) return "";
|
||||
|
||||
const dataSlice = data.slice(0, count);
|
||||
if (dataSlice.length === 0) return null;
|
||||
|
||||
const buildLink = (item) => {
|
||||
switch (type) {
|
||||
case "genre":
|
||||
return `<a href="${item["genre_url"]}">${item["genre_name"]}</a>`;
|
||||
case "artist":
|
||||
return `<a href="${item["url"]}">${item["name"]}</a>`;
|
||||
case "book":
|
||||
return `<a href="${item["url"]}">${item["title"]}</a>`;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
if (dataSlice.length === 1) return buildLink(dataSlice[0]);
|
||||
|
||||
const links = dataSlice.map(buildLink);
|
||||
const allButLast = links.slice(0, -1).join(", ");
|
||||
const last = links[links.length - 1];
|
||||
|
||||
return `${allButLast} and ${last}`;
|
||||
};
|
||||
|
||||
export const parseCountries = (input) => {
|
||||
if (!input) return null;
|
||||
|
||||
const countryCodes = input
|
||||
.split(/\s+and\s+|,\s*|\s+/)
|
||||
.map((code) => code.trim().toUpperCase())
|
||||
.filter((code) => code.length > 0);
|
||||
|
||||
const countryNames = countryCodes.map((code) => {
|
||||
const countryName = countries.getName(code, "en");
|
||||
|
||||
if (!countryName) {
|
||||
console.warn(`Country code "${code}" is not valid.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return countryName;
|
||||
});
|
||||
|
||||
return countryNames.filter((name) => name !== null).join(", ");
|
||||
};
|
||||
|
||||
export const isbnRegex = /^\/books\/(\d{10}|\d{13})$/;
|
Reference in a new issue