@contentful/experiences-design 0.5.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/CHANGELOG.md +55 -0
- package/README.md +36 -0
- package/dist/css-properties.d.ts +14 -0
- package/dist/css-properties.js +169 -0
- package/dist/css-properties.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/viewport.d.ts +44 -0
- package/dist/viewport.js +88 -0
- package/dist/viewport.js.map +1 -0
- package/package.json +35 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
## 0.5.1 (2026-07-17)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- design-token resolution via resolveToken + useDesignValues [AIS-149] ([#53](https://github.com/contentful/experiences/pull/53))
|
|
6
|
+
|
|
7
|
+
### 🧱 Updated Dependencies
|
|
8
|
+
|
|
9
|
+
- Updated core to 0.5.1
|
|
10
|
+
|
|
11
|
+
## 0.5.0 (2026-07-17)
|
|
12
|
+
|
|
13
|
+
### 🧱 Updated Dependencies
|
|
14
|
+
|
|
15
|
+
- Updated core to 0.5.0
|
|
16
|
+
|
|
17
|
+
## 0.4.0 (2026-07-08)
|
|
18
|
+
|
|
19
|
+
### 🧱 Updated Dependencies
|
|
20
|
+
|
|
21
|
+
- Updated core to 0.4.0
|
|
22
|
+
|
|
23
|
+
## 0.3.0 (2026-06-24)
|
|
24
|
+
|
|
25
|
+
### 🧱 Updated Dependencies
|
|
26
|
+
|
|
27
|
+
- Updated core to 0.3.0
|
|
28
|
+
|
|
29
|
+
## 0.2.0 (2026-06-24)
|
|
30
|
+
|
|
31
|
+
### 🧱 Updated Dependencies
|
|
32
|
+
|
|
33
|
+
- Updated core to 0.2.0
|
|
34
|
+
|
|
35
|
+
## 0.1.2 (2026-06-24)
|
|
36
|
+
|
|
37
|
+
### 🩹 Fixes
|
|
38
|
+
|
|
39
|
+
- **design:** clarify toCssMediaQuery README on unrecognized formats ([54fc9a1](https://github.com/contentful/experiences/commit/54fc9a1))
|
|
40
|
+
|
|
41
|
+
## 0.1.1 (2026-06-23)
|
|
42
|
+
|
|
43
|
+
### 🩹 Fixes
|
|
44
|
+
|
|
45
|
+
- **design:** anchor media-query regex so leading-prefix garbage no longer matches ([1db1619](https://github.com/contentful/experiences/commit/1db1619))
|
|
46
|
+
|
|
47
|
+
## 0.1.0 (2026-06-23)
|
|
48
|
+
|
|
49
|
+
### 🚀 Features
|
|
50
|
+
|
|
51
|
+
- initial Experiences SDK monorepo with React adapter [] ([#16](https://github.com/contentful/experiences/pull/16))
|
|
52
|
+
|
|
53
|
+
### 🧱 Updated Dependencies
|
|
54
|
+
|
|
55
|
+
- Updated core to 0.1.0
|
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# @contentful/experiences-design
|
|
2
|
+
|
|
3
|
+
> ⚠️ **Internal package.** Customers do not install this directly. The framework adapter (e.g. [`@contentful/experiences-react`](../adapter-react/)) re-exports the utilities customers need.
|
|
4
|
+
|
|
5
|
+
Pure, framework-agnostic viewport math for resolving Contentful's `DesignPropValue` envelopes against an active viewport.
|
|
6
|
+
|
|
7
|
+
## What lives here
|
|
8
|
+
|
|
9
|
+
- **`toCssMediaQuery(viewport)`** — convert Contentful's media-query DSL (`<992px`, `>1200px`, `*`) to a CSS media query string. The wildcard and any unrecognized format return `undefined`.
|
|
10
|
+
- **`getViewportIndex(viewports, id?)`** — resolve a viewport id to its index in the cascade list.
|
|
11
|
+
- **`getValueForViewport(prop, viewports, activeIdx)`** — unwrap a `DesignPropValue`. `ManualDesignValue` → its scalar; `ValuesByViewport` → cascade-lookup at the active viewport, then unwrap; `DesignToken` → pass the envelope through (customer-resolved in v1).
|
|
12
|
+
- **`resolveDesignProperties(designProps, viewports, activeIdx)`** — apply `getValueForViewport` to every key of a node's design-property bag.
|
|
13
|
+
- **`applyTokenResolver(props, resolveToken?)`** — run a resolved design record through the Config's `resolveToken`. Scalars pass through; `DesignToken` envelopes are resolved, and any that resolve to `undefined` are dropped and reported in `unresolved`. With no resolver the record is returned unchanged — and if it still contains tokens, a one-time console warning fires (they'd otherwise reach components as raw envelope objects).
|
|
14
|
+
- **`toCssKey(key)`** — normalize a design-record key to a candidate CSS property name: strip an optional `cf` prefix and camelCase kebab/snake (`cf-font-size` → `fontSize`).
|
|
15
|
+
- **`isCssProperty(key)` / `CSS_PROPERTIES`** — membership test and the underlying `Set` of CSS property names the adapters' `toCss` will emit.
|
|
16
|
+
|
|
17
|
+
## `toCss` and the CSS-property whitelist
|
|
18
|
+
|
|
19
|
+
The adapters' `toCss` helper keeps only keys whose normalized form is in `CSS_PROPERTIES` and **drops everything else** — that's how semantic design values (`variant`, `as`, `ratio`, …) stay out of a style object. The trade-off is that a genuine CSS property missing from the set is dropped silently.
|
|
20
|
+
|
|
21
|
+
If you hit that:
|
|
22
|
+
|
|
23
|
+
- **Read the value directly.** The full resolved record is available from `useDesignValues()` / `getDesignValues()`; `toCss` is only a convenience over it, so a key it drops is still readable by name.
|
|
24
|
+
- **Extend the set.** `CSS_PROPERTIES` is exported and mutable (`CSS_PROPERTIES.add('containerType')`) — a key added there flows through `toCss` on the next render.
|
|
25
|
+
|
|
26
|
+
The list is intentionally curated rather than exhaustive (a full CSS-property warning would misfire on the semantic keys `toCss` is meant to drop). Open a PR to add commonly-needed properties.
|
|
27
|
+
|
|
28
|
+
## Cascade behavior
|
|
29
|
+
|
|
30
|
+
Viewport order in the payload encodes cascade direction (desktop-first descending, mobile-first ascending). `getValueForViewport` walks backwards from the active viewport toward `viewports[0]`, returning the first defined value — emulating CSS cascade behavior at runtime.
|
|
31
|
+
|
|
32
|
+
See [`../../AGENTS.md`](../../AGENTS.md) for the design rationale and multi-framework story.
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT. See the repository [`LICENSE`](../../LICENSE) and [`NOTICE`](../../NOTICE) for full attribution.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a design-record key to a candidate CSS property name: strip an
|
|
3
|
+
* optional `cf` prefix and camelCase kebab/snake (`cf-font-size` → `fontSize`).
|
|
4
|
+
*/
|
|
5
|
+
declare function toCssKey(key: string): string;
|
|
6
|
+
/** Whether a normalized key names a CSS property `toCss` will emit. */
|
|
7
|
+
declare function isCssProperty(normalizedKey: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Whitelist of CSS property names (camelCase) `toCss` emits. Curated, not
|
|
10
|
+
* exhaustive — `add()` a property here to have `toCss` pass it through.
|
|
11
|
+
*/
|
|
12
|
+
declare const CSS_PROPERTIES: Set<string>;
|
|
13
|
+
|
|
14
|
+
export { CSS_PROPERTIES, isCssProperty, toCssKey };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
function toCssKey(key) {
|
|
2
|
+
const stripped = key.replace(/^cf[-_]?/, "");
|
|
3
|
+
const base = stripped || key;
|
|
4
|
+
return base.replace(/[-_]+([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()).replace(/^([A-Z])/, (c) => c.toLowerCase());
|
|
5
|
+
}
|
|
6
|
+
function isCssProperty(normalizedKey) {
|
|
7
|
+
return CSS_PROPERTIES.has(normalizedKey);
|
|
8
|
+
}
|
|
9
|
+
const CSS_PROPERTIES = /* @__PURE__ */ new Set([
|
|
10
|
+
// Box model / sizing
|
|
11
|
+
"width",
|
|
12
|
+
"minWidth",
|
|
13
|
+
"maxWidth",
|
|
14
|
+
"height",
|
|
15
|
+
"minHeight",
|
|
16
|
+
"maxHeight",
|
|
17
|
+
"margin",
|
|
18
|
+
"marginTop",
|
|
19
|
+
"marginRight",
|
|
20
|
+
"marginBottom",
|
|
21
|
+
"marginLeft",
|
|
22
|
+
"marginBlock",
|
|
23
|
+
"marginInline",
|
|
24
|
+
"padding",
|
|
25
|
+
"paddingTop",
|
|
26
|
+
"paddingRight",
|
|
27
|
+
"paddingBottom",
|
|
28
|
+
"paddingLeft",
|
|
29
|
+
"paddingBlock",
|
|
30
|
+
"paddingInline",
|
|
31
|
+
"boxSizing",
|
|
32
|
+
"aspectRatio",
|
|
33
|
+
// Positioning / display
|
|
34
|
+
"display",
|
|
35
|
+
"position",
|
|
36
|
+
"top",
|
|
37
|
+
"right",
|
|
38
|
+
"bottom",
|
|
39
|
+
"left",
|
|
40
|
+
"inset",
|
|
41
|
+
"zIndex",
|
|
42
|
+
"overflow",
|
|
43
|
+
"overflowX",
|
|
44
|
+
"overflowY",
|
|
45
|
+
"visibility",
|
|
46
|
+
"float",
|
|
47
|
+
"clear",
|
|
48
|
+
// Flexbox / grid
|
|
49
|
+
"flex",
|
|
50
|
+
"flexDirection",
|
|
51
|
+
"flexWrap",
|
|
52
|
+
"flexFlow",
|
|
53
|
+
"flexGrow",
|
|
54
|
+
"flexShrink",
|
|
55
|
+
"flexBasis",
|
|
56
|
+
"justifyContent",
|
|
57
|
+
"justifyItems",
|
|
58
|
+
"justifySelf",
|
|
59
|
+
"alignItems",
|
|
60
|
+
"alignContent",
|
|
61
|
+
"alignSelf",
|
|
62
|
+
"placeItems",
|
|
63
|
+
"placeContent",
|
|
64
|
+
"gap",
|
|
65
|
+
"rowGap",
|
|
66
|
+
"columnGap",
|
|
67
|
+
"order",
|
|
68
|
+
"grid",
|
|
69
|
+
"gridTemplate",
|
|
70
|
+
"gridTemplateColumns",
|
|
71
|
+
"gridTemplateRows",
|
|
72
|
+
"gridTemplateAreas",
|
|
73
|
+
"gridColumn",
|
|
74
|
+
"gridRow",
|
|
75
|
+
"gridArea",
|
|
76
|
+
"gridAutoFlow",
|
|
77
|
+
"gridAutoColumns",
|
|
78
|
+
"gridAutoRows",
|
|
79
|
+
// Typography
|
|
80
|
+
"color",
|
|
81
|
+
"font",
|
|
82
|
+
"fontFamily",
|
|
83
|
+
"fontSize",
|
|
84
|
+
"fontWeight",
|
|
85
|
+
"fontStyle",
|
|
86
|
+
"fontVariant",
|
|
87
|
+
"lineHeight",
|
|
88
|
+
"letterSpacing",
|
|
89
|
+
"wordSpacing",
|
|
90
|
+
"textAlign",
|
|
91
|
+
"textAlignLast",
|
|
92
|
+
"textDecoration",
|
|
93
|
+
"textDecorationLine",
|
|
94
|
+
"textDecorationColor",
|
|
95
|
+
"textDecorationStyle",
|
|
96
|
+
"textTransform",
|
|
97
|
+
"textOverflow",
|
|
98
|
+
"textIndent",
|
|
99
|
+
"textShadow",
|
|
100
|
+
"whiteSpace",
|
|
101
|
+
"wordBreak",
|
|
102
|
+
"wordWrap",
|
|
103
|
+
"overflowWrap",
|
|
104
|
+
"writingMode",
|
|
105
|
+
"verticalAlign",
|
|
106
|
+
// Background
|
|
107
|
+
"background",
|
|
108
|
+
"backgroundColor",
|
|
109
|
+
"backgroundImage",
|
|
110
|
+
"backgroundPosition",
|
|
111
|
+
"backgroundSize",
|
|
112
|
+
"backgroundRepeat",
|
|
113
|
+
"backgroundAttachment",
|
|
114
|
+
"backgroundClip",
|
|
115
|
+
"backgroundOrigin",
|
|
116
|
+
"backgroundBlendMode",
|
|
117
|
+
// Border / outline
|
|
118
|
+
"border",
|
|
119
|
+
"borderWidth",
|
|
120
|
+
"borderStyle",
|
|
121
|
+
"borderColor",
|
|
122
|
+
"borderTop",
|
|
123
|
+
"borderRight",
|
|
124
|
+
"borderBottom",
|
|
125
|
+
"borderLeft",
|
|
126
|
+
"borderRadius",
|
|
127
|
+
"borderTopLeftRadius",
|
|
128
|
+
"borderTopRightRadius",
|
|
129
|
+
"borderBottomLeftRadius",
|
|
130
|
+
"borderBottomRightRadius",
|
|
131
|
+
"outline",
|
|
132
|
+
"outlineWidth",
|
|
133
|
+
"outlineStyle",
|
|
134
|
+
"outlineColor",
|
|
135
|
+
"outlineOffset",
|
|
136
|
+
// Effects
|
|
137
|
+
"opacity",
|
|
138
|
+
"boxShadow",
|
|
139
|
+
"filter",
|
|
140
|
+
"backdropFilter",
|
|
141
|
+
"mixBlendMode",
|
|
142
|
+
"transform",
|
|
143
|
+
"transformOrigin",
|
|
144
|
+
"transition",
|
|
145
|
+
"transitionProperty",
|
|
146
|
+
"transitionDuration",
|
|
147
|
+
"transitionTimingFunction",
|
|
148
|
+
"transitionDelay",
|
|
149
|
+
"animation",
|
|
150
|
+
"cursor",
|
|
151
|
+
"pointerEvents",
|
|
152
|
+
"userSelect",
|
|
153
|
+
// Misc
|
|
154
|
+
"content",
|
|
155
|
+
"listStyle",
|
|
156
|
+
"listStyleType",
|
|
157
|
+
"listStylePosition",
|
|
158
|
+
"objectFit",
|
|
159
|
+
"objectPosition",
|
|
160
|
+
"fill",
|
|
161
|
+
"stroke",
|
|
162
|
+
"strokeWidth"
|
|
163
|
+
]);
|
|
164
|
+
export {
|
|
165
|
+
CSS_PROPERTIES,
|
|
166
|
+
isCssProperty,
|
|
167
|
+
toCssKey
|
|
168
|
+
};
|
|
169
|
+
//# sourceMappingURL=css-properties.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/css-properties.ts"],"sourcesContent":["/**\n * Normalize a design-record key to a candidate CSS property name: strip an\n * optional `cf` prefix and camelCase kebab/snake (`cf-font-size` → `fontSize`).\n */\nexport function toCssKey(key: string): string {\n const stripped = key.replace(/^cf[-_]?/, '');\n const base = stripped || key; // `cf` alone → keep original\n return base\n .replace(/[-_]+([a-zA-Z0-9])/g, (_, c: string) => c.toUpperCase())\n .replace(/^([A-Z])/, (c) => c.toLowerCase());\n}\n\n/** Whether a normalized key names a CSS property `toCss` will emit. */\nexport function isCssProperty(normalizedKey: string): boolean {\n return CSS_PROPERTIES.has(normalizedKey);\n}\n\n/**\n * Whitelist of CSS property names (camelCase) `toCss` emits. Curated, not\n * exhaustive — `add()` a property here to have `toCss` pass it through.\n */\nexport const CSS_PROPERTIES: Set<string> = new Set([\n // Box model / sizing\n 'width',\n 'minWidth',\n 'maxWidth',\n 'height',\n 'minHeight',\n 'maxHeight',\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'marginBlock',\n 'marginInline',\n 'padding',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'paddingBlock',\n 'paddingInline',\n 'boxSizing',\n 'aspectRatio',\n // Positioning / display\n 'display',\n 'position',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'inset',\n 'zIndex',\n 'overflow',\n 'overflowX',\n 'overflowY',\n 'visibility',\n 'float',\n 'clear',\n // Flexbox / grid\n 'flex',\n 'flexDirection',\n 'flexWrap',\n 'flexFlow',\n 'flexGrow',\n 'flexShrink',\n 'flexBasis',\n 'justifyContent',\n 'justifyItems',\n 'justifySelf',\n 'alignItems',\n 'alignContent',\n 'alignSelf',\n 'placeItems',\n 'placeContent',\n 'gap',\n 'rowGap',\n 'columnGap',\n 'order',\n 'grid',\n 'gridTemplate',\n 'gridTemplateColumns',\n 'gridTemplateRows',\n 'gridTemplateAreas',\n 'gridColumn',\n 'gridRow',\n 'gridArea',\n 'gridAutoFlow',\n 'gridAutoColumns',\n 'gridAutoRows',\n // Typography\n 'color',\n 'font',\n 'fontFamily',\n 'fontSize',\n 'fontWeight',\n 'fontStyle',\n 'fontVariant',\n 'lineHeight',\n 'letterSpacing',\n 'wordSpacing',\n 'textAlign',\n 'textAlignLast',\n 'textDecoration',\n 'textDecorationLine',\n 'textDecorationColor',\n 'textDecorationStyle',\n 'textTransform',\n 'textOverflow',\n 'textIndent',\n 'textShadow',\n 'whiteSpace',\n 'wordBreak',\n 'wordWrap',\n 'overflowWrap',\n 'writingMode',\n 'verticalAlign',\n // Background\n 'background',\n 'backgroundColor',\n 'backgroundImage',\n 'backgroundPosition',\n 'backgroundSize',\n 'backgroundRepeat',\n 'backgroundAttachment',\n 'backgroundClip',\n 'backgroundOrigin',\n 'backgroundBlendMode',\n // Border / outline\n 'border',\n 'borderWidth',\n 'borderStyle',\n 'borderColor',\n 'borderTop',\n 'borderRight',\n 'borderBottom',\n 'borderLeft',\n 'borderRadius',\n 'borderTopLeftRadius',\n 'borderTopRightRadius',\n 'borderBottomLeftRadius',\n 'borderBottomRightRadius',\n 'outline',\n 'outlineWidth',\n 'outlineStyle',\n 'outlineColor',\n 'outlineOffset',\n // Effects\n 'opacity',\n 'boxShadow',\n 'filter',\n 'backdropFilter',\n 'mixBlendMode',\n 'transform',\n 'transformOrigin',\n 'transition',\n 'transitionProperty',\n 'transitionDuration',\n 'transitionTimingFunction',\n 'transitionDelay',\n 'animation',\n 'cursor',\n 'pointerEvents',\n 'userSelect',\n // Misc\n 'content',\n 'listStyle',\n 'listStyleType',\n 'listStylePosition',\n 'objectFit',\n 'objectPosition',\n 'fill',\n 'stroke',\n 'strokeWidth',\n]);\n"],"mappings":"AAIO,SAAS,SAAS,KAAqB;AAC5C,QAAM,WAAW,IAAI,QAAQ,YAAY,EAAE;AAC3C,QAAM,OAAO,YAAY;AACzB,SAAO,KACJ,QAAQ,uBAAuB,CAAC,GAAG,MAAc,EAAE,YAAY,CAAC,EAChE,QAAQ,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC;AAC/C;AAGO,SAAS,cAAc,eAAgC;AAC5D,SAAO,eAAe,IAAI,aAAa;AACzC;AAMO,MAAM,iBAA8B,oBAAI,IAAI;AAAA;AAAA,EAEjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;","names":[]}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyTokenResolver,
|
|
3
|
+
getValueForViewport,
|
|
4
|
+
getViewportIndex,
|
|
5
|
+
resolveDesignProperties,
|
|
6
|
+
toCssMediaQuery
|
|
7
|
+
} from "./viewport";
|
|
8
|
+
import { CSS_PROPERTIES, isCssProperty, toCssKey } from "./css-properties";
|
|
9
|
+
export {
|
|
10
|
+
CSS_PROPERTIES,
|
|
11
|
+
applyTokenResolver,
|
|
12
|
+
getValueForViewport,
|
|
13
|
+
getViewportIndex,
|
|
14
|
+
isCssProperty,
|
|
15
|
+
resolveDesignProperties,
|
|
16
|
+
toCssKey,
|
|
17
|
+
toCssMediaQuery
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n applyTokenResolver,\n getValueForViewport,\n getViewportIndex,\n resolveDesignProperties,\n toCssMediaQuery,\n} from './viewport';\nexport { CSS_PROPERTIES, isCssProperty, toCssKey } from './css-properties';\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gBAAgB,eAAe,gBAAgB;","names":[]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DesignToken, ResolveToken, DesignPropValue, ViewportDef } from '@contentful/experiences-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert a Contentful viewport `query` string into a CSS media query.
|
|
5
|
+
* Returns `undefined` for the wildcard "*" (always matches; no media query needed)
|
|
6
|
+
* or for any unrecognized format.
|
|
7
|
+
*/
|
|
8
|
+
declare function toCssMediaQuery(viewport: ViewportDef): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a viewport ID to its index in the ordered viewports list. Returns 0
|
|
11
|
+
* (the wildcard viewport) when the id is unknown or undefined.
|
|
12
|
+
*/
|
|
13
|
+
declare function getViewportIndex(viewports: ViewportDef[], viewportId?: string): number;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a single design-property envelope to its render-time value:
|
|
16
|
+
* - ManualDesignValue → its scalar
|
|
17
|
+
* - DesignToken → the envelope passes through (customer-owned resolution)
|
|
18
|
+
* - ValuesByViewport → cascade-lookup against the active viewport, then
|
|
19
|
+
* unwrap the inner Manual/Token as above
|
|
20
|
+
* - undefined → returns undefined
|
|
21
|
+
*/
|
|
22
|
+
declare function getValueForViewport(prop: DesignPropValue | undefined, viewports: ViewportDef[], activeViewportIndex: number): string | number | boolean | DesignToken | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve every design-property envelope on a node into a flat record keyed
|
|
25
|
+
* by property name. Customer components receive these values as plain props.
|
|
26
|
+
*/
|
|
27
|
+
declare function resolveDesignProperties(designProperties: Record<string, DesignPropValue> | undefined, viewports: ViewportDef[], activeViewportIndex: number): Record<string, string | number | boolean | DesignToken>;
|
|
28
|
+
/**
|
|
29
|
+
* Resolves `DesignToken` envelopes in a viewport-resolved design record via
|
|
30
|
+
* `resolveToken`; scalars pass through, and with no resolver the record is
|
|
31
|
+
* returned unchanged. Keys that resolve to `undefined` are dropped and their
|
|
32
|
+
* token ids collected in `unresolved` for a single grouped warning.
|
|
33
|
+
*
|
|
34
|
+
* When no `resolveToken` is configured but the record contains `DesignToken`
|
|
35
|
+
* envelopes, warns once — those keys reach components as raw envelope objects
|
|
36
|
+
* (e.g. rendering as `[object Object]`), which is almost always a missing-
|
|
37
|
+
* config mistake rather than intent.
|
|
38
|
+
*/
|
|
39
|
+
declare function applyTokenResolver(props: Record<string, string | number | boolean | DesignToken>, resolveToken?: ResolveToken): {
|
|
40
|
+
props: Record<string, unknown>;
|
|
41
|
+
unresolved: string[];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export { applyTokenResolver, getValueForViewport, getViewportIndex, resolveDesignProperties, toCssMediaQuery };
|
package/dist/viewport.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const MEDIA_QUERY_REGEXP = /^(<|>)(\d+)(px|cm|mm|in|pt|pc)$/;
|
|
2
|
+
function toCssMediaQuery(viewport) {
|
|
3
|
+
const { query } = viewport;
|
|
4
|
+
if (query === "*") return void 0;
|
|
5
|
+
const match = query.match(MEDIA_QUERY_REGEXP);
|
|
6
|
+
if (!match) return void 0;
|
|
7
|
+
const [, operator, value, unit] = match;
|
|
8
|
+
if (!operator || !value || !unit) return void 0;
|
|
9
|
+
if (operator === "<") {
|
|
10
|
+
return `(max-width: ${Number(value) - 1}${unit})`;
|
|
11
|
+
}
|
|
12
|
+
if (operator === ">") {
|
|
13
|
+
return `(min-width: ${Number(value) + 1}${unit})`;
|
|
14
|
+
}
|
|
15
|
+
return void 0;
|
|
16
|
+
}
|
|
17
|
+
function getViewportIndex(viewports, viewportId) {
|
|
18
|
+
if (!viewportId) return 0;
|
|
19
|
+
const index = viewports.findIndex((v) => v.id === viewportId);
|
|
20
|
+
return index === -1 ? 0 : index;
|
|
21
|
+
}
|
|
22
|
+
function unwrapInner(inner) {
|
|
23
|
+
if (!inner) return void 0;
|
|
24
|
+
if (inner.type === "ManualDesignValue") return inner.value;
|
|
25
|
+
return inner;
|
|
26
|
+
}
|
|
27
|
+
function resolveValuesByViewport(bag, viewports, activeViewportIndex) {
|
|
28
|
+
for (let i = activeViewportIndex; i >= 0; i--) {
|
|
29
|
+
const viewport = viewports[i];
|
|
30
|
+
if (!viewport) continue;
|
|
31
|
+
const candidate = bag.values[viewport.id];
|
|
32
|
+
if (candidate !== void 0 && candidate !== null) return candidate;
|
|
33
|
+
}
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
function getValueForViewport(prop, viewports, activeViewportIndex) {
|
|
37
|
+
if (!prop) return void 0;
|
|
38
|
+
if (prop.type === "ManualDesignValue") return prop.value;
|
|
39
|
+
if (prop.type === "DesignToken") return prop;
|
|
40
|
+
return unwrapInner(resolveValuesByViewport(prop, viewports, activeViewportIndex));
|
|
41
|
+
}
|
|
42
|
+
function resolveDesignProperties(designProperties, viewports, activeViewportIndex) {
|
|
43
|
+
const out = {};
|
|
44
|
+
if (!designProperties) return out;
|
|
45
|
+
for (const [key, prop] of Object.entries(designProperties)) {
|
|
46
|
+
const value = getValueForViewport(prop, viewports, activeViewportIndex);
|
|
47
|
+
if (value !== void 0) out[key] = value;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
let warnedMissingResolver = false;
|
|
52
|
+
function applyTokenResolver(props, resolveToken) {
|
|
53
|
+
if (!resolveToken) {
|
|
54
|
+
if (!warnedMissingResolver && typeof console !== "undefined") {
|
|
55
|
+
const tokenKeys = Object.entries(props).filter(([, v]) => typeof v === "object" && v !== null && v.type === "DesignToken").map(([k]) => k);
|
|
56
|
+
if (tokenKeys.length) {
|
|
57
|
+
warnedMissingResolver = true;
|
|
58
|
+
console.warn(
|
|
59
|
+
`[@contentful/experiences] Design tokens are present but no \`resolveToken\` is configured on the Config; token-valued design props (${tokenKeys.join(", ")}) reach components unresolved. Add \`resolveToken\` to your Config to map token ids to values.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { props, unresolved: [] };
|
|
64
|
+
}
|
|
65
|
+
const out = {};
|
|
66
|
+
const unresolved = [];
|
|
67
|
+
for (const [key, value] of Object.entries(props)) {
|
|
68
|
+
if (typeof value === "object" && value !== null && value.type === "DesignToken") {
|
|
69
|
+
const resolved = resolveToken(value);
|
|
70
|
+
if (resolved === void 0) {
|
|
71
|
+
unresolved.push(value.value);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
out[key] = resolved;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
out[key] = value;
|
|
78
|
+
}
|
|
79
|
+
return { props: out, unresolved };
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
applyTokenResolver,
|
|
83
|
+
getValueForViewport,
|
|
84
|
+
getViewportIndex,
|
|
85
|
+
resolveDesignProperties,
|
|
86
|
+
toCssMediaQuery
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=viewport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/viewport.ts"],"sourcesContent":["/*\n * Viewport math + design-property resolution for the Experiences SDK.\n *\n * ## Cascade model\n *\n * Viewport order in the payload encodes cascade direction:\n * - desktop-first: viewports descend by screen width (`*`, `<992px`, `<576px`)\n * - mobile-first: viewports ascend by screen width (`*`, `>576px`, `>992px`)\n *\n * \"Active viewport\" = the last-matching media query in the ordered list.\n * `getValueForViewport` walks backwards from the active viewport toward\n * viewport[0], returning the first defined value — emulating CSS cascade\n * behavior at runtime.\n *\n * ## DesignPropValue unwrapping\n *\n * v1 design properties arrive as a discriminated union:\n * - ManualDesignValue → unwrap to its scalar `value`\n * - DesignToken → pass the envelope through unchanged so customer\n * components can resolve it themselves (token\n * resolution lands in the future tokens package)\n * - ValuesByViewport → cascade-lookup the inner ManualDesignValue/DesignToken\n * at the active viewport, then unwrap as above\n */\n\nimport type {\n DesignPropValue,\n DesignToken,\n ManualDesignValue,\n ResolveToken,\n ValuesByViewport,\n ViewportDef,\n} from '@contentful/experiences-core';\n\nconst MEDIA_QUERY_REGEXP = /^(<|>)(\\d+)(px|cm|mm|in|pt|pc)$/;\n\n/**\n * Convert a Contentful viewport `query` string into a CSS media query.\n * Returns `undefined` for the wildcard \"*\" (always matches; no media query needed)\n * or for any unrecognized format.\n */\nexport function toCssMediaQuery(viewport: ViewportDef): string | undefined {\n const { query } = viewport;\n if (query === '*') return undefined;\n const match = query.match(MEDIA_QUERY_REGEXP);\n if (!match) return undefined;\n const [, operator, value, unit] = match;\n if (!operator || !value || !unit) return undefined;\n if (operator === '<') {\n return `(max-width: ${Number(value) - 1}${unit})`;\n }\n if (operator === '>') {\n return `(min-width: ${Number(value) + 1}${unit})`;\n }\n return undefined;\n}\n\n/**\n * Resolve a viewport ID to its index in the ordered viewports list. Returns 0\n * (the wildcard viewport) when the id is unknown or undefined.\n */\nexport function getViewportIndex(viewports: ViewportDef[], viewportId?: string): number {\n if (!viewportId) return 0;\n const index = viewports.findIndex((v) => v.id === viewportId);\n return index === -1 ? 0 : index;\n}\n\n/**\n * Unwrap a non-viewport-keyed envelope into a value the customer component\n * can consume directly. ManualDesignValue → its scalar; DesignToken → the\n * envelope passes through (customer-owned resolution).\n */\nfunction unwrapInner(\n inner: ManualDesignValue | DesignToken | undefined\n): string | number | boolean | DesignToken | undefined {\n if (!inner) return undefined;\n if (inner.type === 'ManualDesignValue') return inner.value;\n return inner;\n}\n\n/**\n * Cascade-lookup for a `ValuesByViewport` envelope. Walks backwards from\n * `activeViewportIndex` toward viewport[0], returning the first defined value.\n */\nfunction resolveValuesByViewport(\n bag: ValuesByViewport,\n viewports: ViewportDef[],\n activeViewportIndex: number\n): ManualDesignValue | DesignToken | undefined {\n for (let i = activeViewportIndex; i >= 0; i--) {\n const viewport = viewports[i];\n if (!viewport) continue;\n const candidate = bag.values[viewport.id];\n if (candidate !== undefined && candidate !== null) return candidate;\n }\n return undefined;\n}\n\n/**\n * Resolve a single design-property envelope to its render-time value:\n * - ManualDesignValue → its scalar\n * - DesignToken → the envelope passes through (customer-owned resolution)\n * - ValuesByViewport → cascade-lookup against the active viewport, then\n * unwrap the inner Manual/Token as above\n * - undefined → returns undefined\n */\nexport function getValueForViewport(\n prop: DesignPropValue | undefined,\n viewports: ViewportDef[],\n activeViewportIndex: number\n): string | number | boolean | DesignToken | undefined {\n if (!prop) return undefined;\n if (prop.type === 'ManualDesignValue') return prop.value;\n if (prop.type === 'DesignToken') return prop;\n return unwrapInner(resolveValuesByViewport(prop, viewports, activeViewportIndex));\n}\n\n/**\n * Resolve every design-property envelope on a node into a flat record keyed\n * by property name. Customer components receive these values as plain props.\n */\nexport function resolveDesignProperties(\n designProperties: Record<string, DesignPropValue> | undefined,\n viewports: ViewportDef[],\n activeViewportIndex: number\n): Record<string, string | number | boolean | DesignToken> {\n const out: Record<string, string | number | boolean | DesignToken> = {};\n if (!designProperties) return out;\n for (const [key, prop] of Object.entries(designProperties)) {\n const value = getValueForViewport(prop, viewports, activeViewportIndex);\n if (value !== undefined) out[key] = value;\n }\n return out;\n}\n\nlet warnedMissingResolver = false;\n\n/**\n * Resolves `DesignToken` envelopes in a viewport-resolved design record via\n * `resolveToken`; scalars pass through, and with no resolver the record is\n * returned unchanged. Keys that resolve to `undefined` are dropped and their\n * token ids collected in `unresolved` for a single grouped warning.\n *\n * When no `resolveToken` is configured but the record contains `DesignToken`\n * envelopes, warns once — those keys reach components as raw envelope objects\n * (e.g. rendering as `[object Object]`), which is almost always a missing-\n * config mistake rather than intent.\n */\nexport function applyTokenResolver(\n props: Record<string, string | number | boolean | DesignToken>,\n resolveToken?: ResolveToken\n): { props: Record<string, unknown>; unresolved: string[] } {\n if (!resolveToken) {\n if (!warnedMissingResolver && typeof console !== 'undefined') {\n const tokenKeys = Object.entries(props)\n .filter(([, v]) => typeof v === 'object' && v !== null && v.type === 'DesignToken')\n .map(([k]) => k);\n if (tokenKeys.length) {\n warnedMissingResolver = true;\n console.warn(\n `[@contentful/experiences] Design tokens are present but no \\`resolveToken\\` is configured on the Config; token-valued design props (${tokenKeys.join(', ')}) reach components unresolved. Add \\`resolveToken\\` to your Config to map token ids to values.`\n );\n }\n }\n return { props, unresolved: [] };\n }\n const out: Record<string, unknown> = {};\n const unresolved: string[] = [];\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'object' && value !== null && value.type === 'DesignToken') {\n const resolved = resolveToken(value);\n if (resolved === undefined) {\n unresolved.push(value.value);\n continue;\n }\n out[key] = resolved;\n continue;\n }\n out[key] = value;\n }\n return { props: out, unresolved };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contentful/experiences-design",
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "Viewport math and design-property resolution utilities for Contentful Experiences",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"CHANGELOG.md"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/contentful/experiences.git",
|
|
30
|
+
"directory": "packages/design"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@contentful/experiences-core": "0.5.1"
|
|
34
|
+
}
|
|
35
|
+
}
|