Genesis
This commit is contained in:
26
src/utils/LazyHtmlParser.js
Normal file
26
src/utils/LazyHtmlParser.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import React, { lazy, Suspense, memo } from 'react';
|
||||
|
||||
// @note Lazy load html-react-parser to reduce initial bundle size
|
||||
const HtmlParserComponent = lazy(() =>
|
||||
import('html-react-parser').then(module => ({
|
||||
default: ({ html, options }) => module.default(html, options)
|
||||
}))
|
||||
);
|
||||
|
||||
/**
|
||||
* LazyHtmlParser - A component that lazy loads html-react-parser
|
||||
* This reduces the initial bundle size by ~20KB
|
||||
*/
|
||||
class LazyHtmlParser extends React.Component {
|
||||
render() {
|
||||
const { html, options, fallback = null } = this.props;
|
||||
|
||||
return (
|
||||
<Suspense fallback={fallback}>
|
||||
<HtmlParserComponent html={html} options={options} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default memo(LazyHtmlParser);
|
||||
232
src/utils/animatedBorderStyles.js
Normal file
232
src/utils/animatedBorderStyles.js
Normal file
@@ -0,0 +1,232 @@
|
||||
// Utility for generating animated border styles with configurable gradients
|
||||
|
||||
/**
|
||||
* Generates animated border CSS styles with configurable gradients
|
||||
* @param {Object} config - Configuration object
|
||||
* @param {Array} config.gradientColors - Array of color strings for the gradient
|
||||
* @param {string} config.animationDirection - 'spin' or 'spinReverse'
|
||||
* @param {number} config.animationDuration - Duration in seconds (default: 5)
|
||||
* @param {string} config.className - CSS class name for the card
|
||||
* @param {Object} config.boxShadow - Box shadow configuration
|
||||
* @param {number} config.borderPadding - Border padding in px (default: 8)
|
||||
* @param {number} config.borderRadius - Border radius in px (default: 24)
|
||||
* @returns {string} CSS string for the animated border
|
||||
*/
|
||||
export const generateAnimatedBorderStyle = ({
|
||||
gradientColors,
|
||||
animationDirection = "spin",
|
||||
animationDuration = 5,
|
||||
className,
|
||||
boxShadow,
|
||||
borderPadding = 8,
|
||||
borderRadius = 24,
|
||||
}) => {
|
||||
const gradientString = gradientColors.join(", ");
|
||||
|
||||
return `
|
||||
.${className} {
|
||||
padding: ${borderPadding}px !important;
|
||||
border-radius: ${borderRadius}px !important;
|
||||
${boxShadow ? `box-shadow: ${boxShadow} !important;` : ""}
|
||||
}
|
||||
|
||||
.${className}::before {
|
||||
content: '' !important;
|
||||
position: absolute !important;
|
||||
top: -50% !important;
|
||||
left: -50% !important;
|
||||
width: 200% !important;
|
||||
height: 200% !important;
|
||||
background: conic-gradient(
|
||||
from 0deg,
|
||||
${gradientString}
|
||||
) !important;
|
||||
animation: ${animationDirection} ${animationDuration}s linear infinite !important;
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base CSS for animated border functionality
|
||||
*/
|
||||
export const baseAnimatedBorderStyle = `
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spinReverse {
|
||||
from {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Seamless carousel animation */
|
||||
@keyframes seamless-scroll {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-33.333%);
|
||||
}
|
||||
}
|
||||
|
||||
/* Carousel container styles */
|
||||
.carousel-container {
|
||||
overflow: hidden !important;
|
||||
position: relative !important;
|
||||
width: 100% !important;
|
||||
padding: 20px 0 !important;
|
||||
}
|
||||
|
||||
.carousel-track {
|
||||
display: flex !important;
|
||||
animation: seamless-scroll 45s linear infinite !important;
|
||||
gap: 20px !important;
|
||||
width: fit-content !important;
|
||||
}
|
||||
|
||||
.carousel-track:hover {
|
||||
animation-play-state: paused !important;
|
||||
}
|
||||
|
||||
.carousel-item {
|
||||
flex: 0 0 130px !important;
|
||||
height: 130px !important;
|
||||
}
|
||||
|
||||
/* Base container with border space */
|
||||
.animated-border-card {
|
||||
position: relative !important;
|
||||
display: block !important;
|
||||
border-radius: 20px !important;
|
||||
padding: 4px !important;
|
||||
overflow: hidden !important;
|
||||
background: #000 !important;
|
||||
}
|
||||
|
||||
/* Inner content with white background */
|
||||
.animated-border-card > a {
|
||||
position: relative !important;
|
||||
display: block !important;
|
||||
background: white !important;
|
||||
border-radius: 16px !important;
|
||||
z-index: 1 !important;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Predefined gradient configurations
|
||||
*/
|
||||
export const gradientPresets = {
|
||||
seeds: {
|
||||
gradientColors: [
|
||||
"#1B5E20",
|
||||
"#2E7D32",
|
||||
"#388E3C",
|
||||
"#43A047",
|
||||
"#4CAF50",
|
||||
"#66BB6A",
|
||||
"#81C784",
|
||||
"#A5D6A7",
|
||||
"#C8E6C9",
|
||||
"#E8F5E8",
|
||||
"#C8E6C9",
|
||||
"#A5D6A7",
|
||||
"#81C784",
|
||||
"#66BB6A",
|
||||
"#4CAF50",
|
||||
"#43A047",
|
||||
"#388E3C",
|
||||
"#2E7D32",
|
||||
"#1B5E20",
|
||||
],
|
||||
animationDirection: "spin",
|
||||
className: "seeds-card",
|
||||
borderPadding: 8,
|
||||
borderRadius: 24,
|
||||
boxShadow:
|
||||
"0 0 40px rgba(0, 200, 83, 0.5), 0 0 80px rgba(76, 175, 80, 0.3)",
|
||||
},
|
||||
cutlings: {
|
||||
gradientColors: [
|
||||
"#D4B896",
|
||||
"#C8A882",
|
||||
"#B8A082",
|
||||
"#A8956E",
|
||||
"#9E8B5A",
|
||||
"#8D7F46",
|
||||
"#7A7F32",
|
||||
"#6B8E23",
|
||||
"#7CB342",
|
||||
"#8BC34A",
|
||||
"#9CCC65",
|
||||
"#AED581",
|
||||
"#C5E1A5",
|
||||
"#DCEDC8",
|
||||
"#C5E1A5",
|
||||
"#AED581",
|
||||
"#9CCC65",
|
||||
"#8BC34A",
|
||||
"#7CB342",
|
||||
"#6B8E23",
|
||||
"#7A7F32",
|
||||
"#8D7F46",
|
||||
"#9E8B5A",
|
||||
"#A8956E",
|
||||
"#B8A082",
|
||||
"#C8A882",
|
||||
"#D4B896",
|
||||
],
|
||||
animationDirection: "spinReverse",
|
||||
className: "cutlings-card",
|
||||
borderPadding: 8,
|
||||
borderRadius: 24,
|
||||
boxShadow:
|
||||
"0 0 20px rgba(212, 184, 150, 0.4), 0 0 40px rgba(168, 149, 110, 0.3)",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates complete animated border styles using presets or custom config
|
||||
* @param {string|Object} config - Preset name or custom configuration object
|
||||
* @returns {string} Complete CSS string
|
||||
*/
|
||||
export const getAnimatedBorderStyles = (config) => {
|
||||
let styleConfig;
|
||||
|
||||
if (typeof config === "string" && gradientPresets[config]) {
|
||||
styleConfig = gradientPresets[config];
|
||||
} else if (typeof config === "object") {
|
||||
styleConfig = config;
|
||||
} else {
|
||||
throw new Error("Invalid configuration provided");
|
||||
}
|
||||
|
||||
return baseAnimatedBorderStyle + generateAnimatedBorderStyle(styleConfig);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates styles for multiple presets efficiently (avoids duplicate base styles)
|
||||
* @param {Array<string>} presetNames - Array of preset names
|
||||
* @returns {string} Complete CSS string with all presets
|
||||
*/
|
||||
export const getCombinedAnimatedBorderStyles = (presetNames) => {
|
||||
const specificStyles = presetNames
|
||||
.map((presetName) => {
|
||||
if (!gradientPresets[presetName]) {
|
||||
throw new Error(`Preset '${presetName}' not found`);
|
||||
}
|
||||
return generateAnimatedBorderStyle(gradientPresets[presetName]);
|
||||
})
|
||||
.join("");
|
||||
|
||||
return baseAnimatedBorderStyle + specificStyles;
|
||||
};
|
||||
51
src/utils/cartUtils.js
Normal file
51
src/utils/cartUtils.js
Normal file
@@ -0,0 +1,51 @@
|
||||
// Cart-Sync-Utilities
|
||||
|
||||
/**
|
||||
* Vereint lokalen und Server-Warenkorb intelligent.
|
||||
*/
|
||||
export const mergeCarts = (localCart = [], serverCart = []) => {
|
||||
try {
|
||||
const localCartMap = new Map();
|
||||
localCart.forEach(item => {
|
||||
if (item?.id) localCartMap.set(item.id, item);
|
||||
});
|
||||
const mergedCart = [];
|
||||
const processedIds = new Set();
|
||||
serverCart.forEach(serverItem => {
|
||||
if (!serverItem?.id) return;
|
||||
const localItem = localCartMap.get(serverItem.id);
|
||||
if (localItem) {
|
||||
mergedCart.push({
|
||||
...serverItem,
|
||||
quantity: Math.max(serverItem.quantity, localItem.quantity),
|
||||
});
|
||||
} else {
|
||||
mergedCart.push({ ...serverItem });
|
||||
}
|
||||
processedIds.add(serverItem.id);
|
||||
});
|
||||
localCart.forEach(localItem => {
|
||||
if (localItem?.id && !processedIds.has(localItem.id)) {
|
||||
mergedCart.push({ ...localItem });
|
||||
}
|
||||
});
|
||||
return mergedCart;
|
||||
} catch (error) {
|
||||
console.error('Error merging carts:', error);
|
||||
return localCart || [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Nutzt lokalen Warenkorb und archiviert den Server-Warenkorb.
|
||||
*/
|
||||
export const localAndArchiveServer = (localCart, serverCart) => {
|
||||
try {
|
||||
window.archivedServerCart = serverCart;
|
||||
window.cart = localCart;
|
||||
return localCart;
|
||||
} catch (error) {
|
||||
console.error('Error applying local cart and archiving server cart:', error);
|
||||
return window.cart || [];
|
||||
}
|
||||
};
|
||||
53
src/utils/nnsort.js
Normal file
53
src/utils/nnsort.js
Normal file
@@ -0,0 +1,53 @@
|
||||
export function sortByFuzzySimilarity(list, searchTerm) {
|
||||
const searchWords = searchTerm.toLowerCase().split(/\W+/).filter(Boolean);
|
||||
|
||||
return list.slice().sort((textA, textB) => {
|
||||
const scoreA = fuzzySimilarityScore(textA, searchWords);
|
||||
const scoreB = fuzzySimilarityScore(textB, searchWords);
|
||||
return scoreB - scoreA;
|
||||
});
|
||||
}
|
||||
|
||||
function fuzzySimilarityScore(text, searchWords) {
|
||||
const textWords = text.toLowerCase().split(/\W+/).filter(Boolean);
|
||||
let totalScore = 0;
|
||||
|
||||
for (let searchWord of searchWords) {
|
||||
let bestSimilarity = 0;
|
||||
for (let word of textWords) {
|
||||
const similarity = stringSimilarity(searchWord, word);
|
||||
if (similarity > bestSimilarity) bestSimilarity = similarity;
|
||||
}
|
||||
if (bestSimilarity > 0.5) totalScore += bestSimilarity;
|
||||
}
|
||||
|
||||
return totalScore;
|
||||
}
|
||||
|
||||
function stringSimilarity(a, b) {
|
||||
const distance = levenshteinDistance(a, b);
|
||||
const maxLen = Math.max(a.length, b.length);
|
||||
return maxLen === 0 ? 1 : 1 - (distance / maxLen);
|
||||
}
|
||||
|
||||
function levenshteinDistance(a, b) {
|
||||
const matrix = [];
|
||||
|
||||
for (let i = 0; i <= a.length; i++) matrix[i] = [i];
|
||||
for (let j = 0; j <= b.length; j++) matrix[0][j] = j;
|
||||
|
||||
for (let i = 1; i <= a.length; i++) {
|
||||
for (let j = 1; j <= b.length; j++) {
|
||||
if (a.charAt(i - 1) === b.charAt(j - 1)) {
|
||||
matrix[i][j] = matrix[i - 1][j - 1];
|
||||
} else {
|
||||
matrix[i][j] = Math.min(
|
||||
matrix[i - 1][j - 1] + 1,
|
||||
matrix[i][j - 1] + 1,
|
||||
matrix[i - 1][j] + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matrix[a.length][b.length];
|
||||
}
|
||||
24
src/utils/sessionStorage.js
Normal file
24
src/utils/sessionStorage.js
Normal file
@@ -0,0 +1,24 @@
|
||||
if (!window.sessionSettings) {
|
||||
window.sessionSettings = {};
|
||||
}
|
||||
export const getSessionSetting = (key) => {
|
||||
return window.sessionSettings[key];
|
||||
};
|
||||
export const setSessionSetting = (key, value) => {
|
||||
window.sessionSettings[key] = value;
|
||||
};
|
||||
export const removeSessionSetting = (key) => {
|
||||
delete window.sessionSettings[key];
|
||||
};
|
||||
export const clearAllSessionSettings = () => {
|
||||
window.sessionSettings = {};
|
||||
};
|
||||
export const getAllSettingsWithPrefix = (prefix) => {
|
||||
const filteredSettings = {};
|
||||
Object.keys(window.sessionSettings).forEach(key => {
|
||||
if (key.startsWith(prefix)) {
|
||||
filteredSettings[key] = window.sessionSettings[key];
|
||||
}
|
||||
});
|
||||
return filteredSettings;
|
||||
};
|
||||
Reference in New Issue
Block a user