@contentful/experiences-core 1.40.1 → 1.40.2-beta.1
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/index.d.ts +2 -1
- package/dist/index.js +85 -27
- package/dist/index.js.map +1 -1
- package/dist/utils/styleUtils/ssrStyles.d.ts +1 -13
- package/dist/utils/styleUtils/toMediaQuery.d.ts +15 -0
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,8 @@ export { Fieldset, UnresolvedFieldset, isDeepPath, lastPathNamedSegmentEq, parse
|
|
|
9
9
|
export { addLocale, buildTemplate, getTemplateValue, resolveHyperlinkPattern } from './utils/resolveHyperlinkPattern.js';
|
|
10
10
|
export { sanitizeNodeProps } from './utils/sanitizeNodeProps.js';
|
|
11
11
|
export { addMinHeightForEmptyStructures, buildCfStyles, buildStyleTag, calculateNodeDefaultHeight, stringifyCssProperties, toCSSAttribute } from './utils/styleUtils/stylesUtils.js';
|
|
12
|
-
export { detachExperienceStyles, flattenDesignTokenRegistry, indexByBreakpoint, isCfStyleAttribute, maybePopulateDesignTokenValue, resolveBackgroundImageBinding
|
|
12
|
+
export { detachExperienceStyles, flattenDesignTokenRegistry, indexByBreakpoint, isCfStyleAttribute, maybePopulateDesignTokenValue, resolveBackgroundImageBinding } from './utils/styleUtils/ssrStyles.js';
|
|
13
|
+
export { toMediaQuery } from './utils/styleUtils/toMediaQuery.js';
|
|
13
14
|
export { transformVisibility } from './utils/styleUtils/styleTransformers.js';
|
|
14
15
|
export { transformBoundContentValue } from './utils/transformers/transformBoundContentValue.js';
|
|
15
16
|
export { treeMap, treeVisit } from './utils/treeTraversal.js';
|
package/dist/index.js
CHANGED
|
@@ -1453,10 +1453,11 @@ const CF_DEBUG_KEY = 'cf_debug';
|
|
|
1453
1453
|
* SecurityError (e.g. Brave or Chromium with specific settings).
|
|
1454
1454
|
*/
|
|
1455
1455
|
const checkLocalStorageAvailability = () => {
|
|
1456
|
-
if (typeof localStorage === 'undefined' || localStorage === null) {
|
|
1457
|
-
return false;
|
|
1458
|
-
}
|
|
1459
1456
|
try {
|
|
1457
|
+
// Even the typeof check can throw an error in an agressive browser like Brave (requires using the deprecated flag #block-all-cookies-toggle)
|
|
1458
|
+
if (typeof localStorage === 'undefined' || localStorage === null) {
|
|
1459
|
+
return false;
|
|
1460
|
+
}
|
|
1460
1461
|
// Attempt to set and remove an item to check if localStorage is enabled
|
|
1461
1462
|
const TEST_KEY = 'cf_test_local_storage';
|
|
1462
1463
|
localStorage.setItem(TEST_KEY, 'yes');
|
|
@@ -2102,6 +2103,55 @@ const getOptimizedBackgroundImageAsset = (file, widthStyle, quality = '100%', fo
|
|
|
2102
2103
|
}
|
|
2103
2104
|
};
|
|
2104
2105
|
|
|
2106
|
+
/**
|
|
2107
|
+
* Turns a condition like `<768px` or `>1024px` into a media query rule.
|
|
2108
|
+
* For example, `<768px` becomes `max-width:768px` and `>1024px` becomes `min-width:1024px`.
|
|
2109
|
+
*/
|
|
2110
|
+
const toMediaQueryRule = (condition) => {
|
|
2111
|
+
const [evaluation, pixelValue] = [condition[0], condition.substring(1)];
|
|
2112
|
+
const mediaQueryRule = evaluation === '<' ? 'max-width' : 'min-width';
|
|
2113
|
+
return `(${mediaQueryRule}:${pixelValue})`;
|
|
2114
|
+
};
|
|
2115
|
+
/**
|
|
2116
|
+
* Converts a map of class names to CSS strings into a single CSS string.
|
|
2117
|
+
*
|
|
2118
|
+
* @param cssByClassName map of class names to CSS strings containing all rules for each class
|
|
2119
|
+
* @returns joined string of all CSS class definitions
|
|
2120
|
+
*/
|
|
2121
|
+
const toCompoundCss = (cssByClassName) => {
|
|
2122
|
+
return Object.entries(cssByClassName).reduce((acc, [className, css]) => {
|
|
2123
|
+
if (css === '')
|
|
2124
|
+
return acc;
|
|
2125
|
+
return `${acc}.${className}{${css}}`;
|
|
2126
|
+
}, ``);
|
|
2127
|
+
};
|
|
2128
|
+
/**
|
|
2129
|
+
* Create a single CSS string containing all class definitions for a given media query.
|
|
2130
|
+
*
|
|
2131
|
+
* @param cssByClassName map of class names to CSS strings containing all rules for each class
|
|
2132
|
+
* @param condition e.g. "*", "<520px", ">520px"
|
|
2133
|
+
* @param nextCondition optional next condition to create a disjunct media query that doesn't affect the next breakpoint
|
|
2134
|
+
* @returns joined string of all CSS class definitions wrapped into media queries
|
|
2135
|
+
*/
|
|
2136
|
+
const toMediaQuery = ({ cssByClassName, condition, nextCondition, }) => {
|
|
2137
|
+
const compoundCss = toCompoundCss(cssByClassName);
|
|
2138
|
+
if (compoundCss === '') {
|
|
2139
|
+
return '';
|
|
2140
|
+
}
|
|
2141
|
+
const queryRule = toMediaQueryRule(condition);
|
|
2142
|
+
if (!nextCondition) {
|
|
2143
|
+
if (condition === '*') {
|
|
2144
|
+
return compoundCss;
|
|
2145
|
+
}
|
|
2146
|
+
return `@media${queryRule}{${compoundCss}}`;
|
|
2147
|
+
}
|
|
2148
|
+
const nextRule = toMediaQueryRule(nextCondition);
|
|
2149
|
+
if (condition === '*') {
|
|
2150
|
+
return `@media not ${nextRule}{${compoundCss}}`;
|
|
2151
|
+
}
|
|
2152
|
+
return `@media${queryRule} and (not ${nextRule}){${compoundCss}}`;
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2105
2155
|
const detachExperienceStyles = (experience) => {
|
|
2106
2156
|
const experienceTreeRoot = experience.entityStore?.experienceEntryFields
|
|
2107
2157
|
?.componentTree;
|
|
@@ -2117,6 +2167,7 @@ const detachExperienceStyles = (experience) => {
|
|
|
2117
2167
|
[breakpoint.id]: {
|
|
2118
2168
|
condition: breakpoint.query,
|
|
2119
2169
|
cssByClassName: {},
|
|
2170
|
+
visibilityCssByClassName: {},
|
|
2120
2171
|
},
|
|
2121
2172
|
}), {});
|
|
2122
2173
|
// getting the breakpoint ids
|
|
@@ -2193,6 +2244,11 @@ const detachExperienceStyles = (experience) => {
|
|
|
2193
2244
|
return experience.entityStore?.entities.find((entity) => entity.sys.id === id);
|
|
2194
2245
|
},
|
|
2195
2246
|
});
|
|
2247
|
+
// When the node is hidden for any breakpoint, we need to handle this separately with a disjunct media query.
|
|
2248
|
+
const isAnyVisibilityValueHidden = Object.values(propsByBreakpoint).some((designProperties) => designProperties.cfVisibility === false);
|
|
2249
|
+
// We always need an explicit value when using disjunct media queries
|
|
2250
|
+
// Example: desktop uses "false" and tablet is undefined -> we need to set `display: none` for tablet as well.
|
|
2251
|
+
let previousVisibilityValue;
|
|
2196
2252
|
/* [Data format] `propsByBreakpoint` is a map of "breakpointId > propertyName > plainValue":
|
|
2197
2253
|
* {
|
|
2198
2254
|
* desktop: {
|
|
@@ -2243,12 +2299,21 @@ const detachExperienceStyles = (experience) => {
|
|
|
2243
2299
|
if (!currentNodeClassNames.includes(className)) {
|
|
2244
2300
|
currentNodeClassNames.push(className);
|
|
2245
2301
|
}
|
|
2246
|
-
// if
|
|
2247
|
-
if (mediaQueryDataByBreakpoint[breakpointId].cssByClassName[className]) {
|
|
2248
|
-
|
|
2302
|
+
// Only if the hash was not used yet, save the CSS to the stylesheet
|
|
2303
|
+
if (!mediaQueryDataByBreakpoint[breakpointId].cssByClassName[className]) {
|
|
2304
|
+
mediaQueryDataByBreakpoint[breakpointId].cssByClassName[className] = generatedCss;
|
|
2305
|
+
}
|
|
2306
|
+
// Special case for visibility to override any custom `display` values but only for a specific breakpoint.
|
|
2307
|
+
if (isAnyVisibilityValueHidden) {
|
|
2308
|
+
const visibilityValue = propsByBreakpointWithResolvedDesignTokens.cfVisibility ?? previousVisibilityValue;
|
|
2309
|
+
previousVisibilityValue = visibilityValue;
|
|
2310
|
+
const visibilityStyles = transformVisibility(visibilityValue);
|
|
2311
|
+
const visibilityCss = stringifyCssProperties(visibilityStyles);
|
|
2312
|
+
if (!mediaQueryDataByBreakpoint[breakpointId].visibilityCssByClassName[className]) {
|
|
2313
|
+
mediaQueryDataByBreakpoint[breakpointId].visibilityCssByClassName[className] =
|
|
2314
|
+
visibilityCss;
|
|
2315
|
+
}
|
|
2249
2316
|
}
|
|
2250
|
-
// otherwise, save it to the stylesheet
|
|
2251
|
-
mediaQueryDataByBreakpoint[breakpointId].cssByClassName[className] = generatedCss;
|
|
2252
2317
|
}
|
|
2253
2318
|
// all generated classNames are saved in the tree node
|
|
2254
2319
|
// to be handled by the sdk later
|
|
@@ -2290,7 +2355,18 @@ const detachExperienceStyles = (experience) => {
|
|
|
2290
2355
|
});
|
|
2291
2356
|
// once the whole tree was traversed, for each breakpoint, I aggregate the styles
|
|
2292
2357
|
// for each generated className into one css string
|
|
2293
|
-
const stylesheet = Object.
|
|
2358
|
+
const stylesheet = Object.values(mediaQueryDataByBreakpoint).reduce((acc, { condition, cssByClassName, visibilityCssByClassName }, index) => {
|
|
2359
|
+
const mediaQueryCss = toMediaQuery({ cssByClassName, condition });
|
|
2360
|
+
// Handle visibility separately to use disjunct media queries ("if desktop but not tablet ...")
|
|
2361
|
+
// Enables to hide on one breakpoint but render any unknown custom `display` value on another breakpoint.
|
|
2362
|
+
const visibilityMediaQueryCss = toMediaQuery({
|
|
2363
|
+
cssByClassName: visibilityCssByClassName,
|
|
2364
|
+
condition,
|
|
2365
|
+
// Breakpoint validation ensures that it starts with the '*' breakpoint
|
|
2366
|
+
nextCondition: Object.values(mediaQueryDataByBreakpoint)[index + 1]?.condition,
|
|
2367
|
+
});
|
|
2368
|
+
return `${acc}${mediaQueryCss}${visibilityMediaQueryCss}`;
|
|
2369
|
+
}, '');
|
|
2294
2370
|
return stylesheet;
|
|
2295
2371
|
};
|
|
2296
2372
|
/**
|
|
@@ -2634,24 +2710,6 @@ const flattenDesignTokenRegistry = (designTokenRegistry) => {
|
|
|
2634
2710
|
};
|
|
2635
2711
|
}, {});
|
|
2636
2712
|
};
|
|
2637
|
-
/**
|
|
2638
|
-
* Create a single CSS string containing all class definitions for a given media query.
|
|
2639
|
-
*
|
|
2640
|
-
* @param condition e.g. "*", "<520px", ">520px"
|
|
2641
|
-
* @param cssByClassName map of class names to CSS strings containing all rules for each class
|
|
2642
|
-
* @returns joined string of all CSS class definitions wrapped into media queries
|
|
2643
|
-
*/
|
|
2644
|
-
const toMediaQuery = ({ condition, cssByClassName }) => {
|
|
2645
|
-
const mediaQueryStyles = Object.entries(cssByClassName).reduce((acc, [className, css]) => {
|
|
2646
|
-
return `${acc}.${className}{${css}}`;
|
|
2647
|
-
}, ``);
|
|
2648
|
-
if (condition === '*') {
|
|
2649
|
-
return mediaQueryStyles;
|
|
2650
|
-
}
|
|
2651
|
-
const [evaluation, pixelValue] = [condition[0], condition.substring(1)];
|
|
2652
|
-
const mediaQueryRule = evaluation === '<' ? 'max-width' : 'min-width';
|
|
2653
|
-
return `@media(${mediaQueryRule}:${pixelValue}){${mediaQueryStyles}}`;
|
|
2654
|
-
};
|
|
2655
2713
|
|
|
2656
2714
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2657
2715
|
function get(obj, path) {
|