@rudra-js/react 0.7.3 → 0.7.4
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/README.md +15 -7
- package/dist/blocks/block-renderers.d.ts.map +1 -1
- package/dist/blocks/block-renderers.js +2 -2
- package/dist/blocks/block-renderers.js.map +1 -1
- package/dist/blocks/product-card.js +2 -3
- package/dist/blocks/product-card.js.map +1 -1
- package/dist/rudra-component.d.ts +8 -5
- package/dist/rudra-component.d.ts.map +1 -1
- package/dist/rudra-component.js +7 -3
- package/dist/rudra-component.js.map +1 -1
- package/package.json +3 -3
- package/src/blocks/block-renderers.tsx +3 -4
- package/src/blocks/product-card.tsx +5 -5
- package/src/rudra-component.tsx +13 -5
package/README.md
CHANGED
|
@@ -16,13 +16,13 @@ still reads it.
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
npm install @rudra-js/react @rudra-js/core react
|
|
19
|
+
npm install @rudra-js/react @rudra-js/core @rudra-js/attested react
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Both `@rudra-js/core` and `react` are peer dependencies. The specification you pass in comes from
|
|
23
23
|
your copy of core, and the elements this renders have to come from the same React your app renders.
|
|
24
24
|
With two copies of either, you'd get a spec that fails its own type check, or a component tree React
|
|
25
|
-
refuses to render.
|
|
25
|
+
refuses to render. `@rudra-js/attested` is core's peer: npm and pnpm add it for you, Yarn doesn't.
|
|
26
26
|
|
|
27
27
|
```tsx
|
|
28
28
|
import { RudraComponent } from '@rudra-js/react';
|
|
@@ -69,8 +69,9 @@ later and may be the fresher of the two.
|
|
|
69
69
|
## Styling
|
|
70
70
|
|
|
71
71
|
We ship no CSS. A stylesheet of ours would only fight whatever your site already has. So out of the
|
|
72
|
-
box the block renders as a run-on line — every card element is inline, so
|
|
73
|
-
|
|
72
|
+
box the block renders as a run-on line — every card element is inline, so a card's parts sit on one
|
|
73
|
+
line with spaces between them, and nothing separates one card from the next. That's the starting
|
|
74
|
+
point you style from.
|
|
74
75
|
|
|
75
76
|
Check out `examples/shop/public/demo-styles.css`. It's a working stylesheet written against nothing
|
|
76
77
|
but the table below, so copy it as a starting point rather than as a supported API. The example shop applies it by default, and `?styles=off`
|
|
@@ -98,6 +99,12 @@ keep working.
|
|
|
98
99
|
here uses JavaScript to scroll it for you. `.rudra-card--featured` is applied alongside
|
|
99
100
|
`.rudra-card`, so write `.rudra-card--featured { ... }` after the base rule and let it layer on top.
|
|
100
101
|
|
|
102
|
+
`.rudra-banner__cta` and `.rudra-bundle__cta` are plain text, not links or buttons, because there is
|
|
103
|
+
nothing for them to do. Don't style them as buttons unless a renderer of your own wraps them in one.
|
|
104
|
+
The hero's call to action sits inside the product link, so that one is clickable. Every card in a
|
|
105
|
+
carousel is a link, so tabbing through them scrolls the track, which keeps it usable by keyboard
|
|
106
|
+
even with the scrollbar hidden.
|
|
107
|
+
|
|
101
108
|
### Attributes
|
|
102
109
|
|
|
103
110
|
The same markup carries what the model decided, so you can hang styling or analytics off it.
|
|
@@ -162,9 +169,10 @@ reach the renderer, since they don't depend on the catalog.
|
|
|
162
169
|
### What `products` may be
|
|
163
170
|
|
|
164
171
|
A list of products, or anything keyed by SKU that answers `get(sku)` and `has(sku)`. A `Map` does
|
|
165
|
-
it, and so does your own index.
|
|
166
|
-
your catalog is too big to copy into a `Map` on every request, hand over
|
|
167
|
-
instead.
|
|
172
|
+
it, and so does your own index. The renderers only ever call `get`, and `has` is how we tell a keyed
|
|
173
|
+
catalog from a list, so if your catalog is too big to copy into a `Map` on every request, hand over
|
|
174
|
+
a view of your own store instead. The prop is typed as a `ReadonlyMap`, so in TypeScript either
|
|
175
|
+
implement `ReadonlyMap` or cast with `as unknown as ReadonlyMap<string, Product>`.
|
|
168
176
|
|
|
169
177
|
We look for those two methods rather than for `instanceof Map`, which is per-realm. A `Map` arriving
|
|
170
178
|
from a worker or a `node:vm` sandbox is a perfectly good catalog and fails `instanceof` anyway.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-renderers.d.ts","sourceRoot":"","sources":["../../src/blocks/block-renderers.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAGhF,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,kBAAkB,CAAC;CAC7B,+
|
|
1
|
+
{"version":3,"file":"block-renderers.d.ts","sourceRoot":"","sources":["../../src/blocks/block-renderers.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAGhF,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,kBAAkB,CAAC;CAC7B,+BAmBA;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,kBAAkB,CAAC;CAC7B,+BAWA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,kBAAkB,CAAC;CAC7B,+BAaA;AAED,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,+BAO/D;AAED,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,+BAO3D"}
|
|
@@ -3,7 +3,7 @@ import { sellableProduct } from '../render-context.js';
|
|
|
3
3
|
import { ProductCard } from './product-card.js';
|
|
4
4
|
export function HeroRenderer({ block, context, }) {
|
|
5
5
|
const product = block.sku ? sellableProduct(context.products, block.sku) : undefined;
|
|
6
|
-
return (_jsxs("section", { className: "rudra-hero", children: [_jsx("h3", { className: "rudra-hero__headline", children: block.headline }), block.body ? _jsx("p", { className: "rudra-hero__body", children: block.body }) : null, product ? (_jsxs("a", { className: "rudra-hero__link", href: context.hrefForSku(product.sku), "data-rudra-sku": product.sku, children: [product.title, _jsx("span", { className: "rudra-hero__price", children: context.formatPrice(product) }), block.ctaLabel ? _jsx("span", { className: "rudra-hero__cta", children: block.ctaLabel }) : null] })) : null] }));
|
|
6
|
+
return (_jsxs("section", { className: "rudra-hero", children: [block.headline ? _jsx("h3", { className: "rudra-hero__headline", children: block.headline }) : null, block.body ? _jsx("p", { className: "rudra-hero__body", children: block.body }) : null, product ? (_jsxs("a", { className: "rudra-hero__link", href: context.hrefForSku(product.sku), "data-rudra-sku": product.sku, children: [product.title, " ", _jsx("span", { className: "rudra-hero__price", children: context.formatPrice(product) }), ' ', block.ctaLabel ? _jsx("span", { className: "rudra-hero__cta", children: block.ctaLabel }) : null] })) : null] }));
|
|
7
7
|
}
|
|
8
8
|
export function GridRenderer({ block, context, }) {
|
|
9
9
|
return (_jsxs("section", { className: "rudra-grid", "data-rudra-columns": block.columns, children: [block.title ? _jsx("h3", { className: "rudra-grid__title", children: block.title }) : null, _jsx("div", { className: "rudra-grid__items", children: block.items.map((reference) => (_jsx(ProductCard, { reference: reference, context: context }, reference.sku))) })] }));
|
|
@@ -12,7 +12,7 @@ export function CarouselRenderer({ block, context, }) {
|
|
|
12
12
|
return (_jsxs("section", { className: "rudra-carousel", children: [block.title ? _jsx("h3", { className: "rudra-carousel__title", children: block.title }) : null, _jsx("div", { className: "rudra-carousel__track", children: block.items.map((reference) => (_jsx(ProductCard, { reference: reference, context: context }, reference.sku))) })] }));
|
|
13
13
|
}
|
|
14
14
|
export function BannerRenderer({ block }) {
|
|
15
|
-
return (_jsxs("aside", { className: "rudra-banner", "data-rudra-banner-tone": block.tone, children: [_jsx("span", { className: "rudra-banner__text", children: block.text }), block.ctaLabel ? _jsx("span", { className: "rudra-banner__cta", children: block.ctaLabel }) : null] }));
|
|
15
|
+
return (_jsxs("aside", { className: "rudra-banner", "data-rudra-banner-tone": block.tone, children: [_jsx("span", { className: "rudra-banner__text", children: block.text }), ' ', block.ctaLabel ? _jsx("span", { className: "rudra-banner__cta", children: block.ctaLabel }) : null] }));
|
|
16
16
|
}
|
|
17
17
|
export function CopyRenderer({ block }) {
|
|
18
18
|
return (_jsxs("section", { className: "rudra-copy", children: [block.title ? _jsx("h3", { className: "rudra-copy__title", children: block.title }) : null, _jsx("p", { className: "rudra-copy__body", children: block.body })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-renderers.js","sourceRoot":"","sources":["../../src/blocks/block-renderers.tsx"],"names":[],"mappings":";AAWA,OAAO,EAAE,eAAe,EAA2B,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,UAAU,YAAY,CAAC,EAC3B,KAAK,EACL,OAAO,GAIR;IACC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAErF,OAAO,CACL,mBAAS,SAAS,EAAC,YAAY,
|
|
1
|
+
{"version":3,"file":"block-renderers.js","sourceRoot":"","sources":["../../src/blocks/block-renderers.tsx"],"names":[],"mappings":"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"}
|
|
@@ -10,11 +10,10 @@ import { sellableProduct } from '../render-context.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export function ProductCard({ reference, context, }) {
|
|
12
12
|
const product = sellableProduct(context.products, reference.sku);
|
|
13
|
-
//
|
|
14
|
-
// unreachable. Rendering nothing beats rendering a card with holes in it.
|
|
13
|
+
// Gone from the catalog, or sold out since the spec was made: render nothing, not half a card.
|
|
15
14
|
if (!product)
|
|
16
15
|
return null;
|
|
17
16
|
const isFeatured = reference.emphasis === 'featured';
|
|
18
|
-
return (_jsxs("a", { href: context.hrefForSku(product.sku), "data-rudra-sku": product.sku, "data-rudra-basis": reference.basis, className: isFeatured ? 'rudra-card rudra-card--featured' : 'rudra-card', children: [product.imageUrl ? (_jsx("img", { className: "rudra-card__image", src: product.imageUrl, alt: "", loading: "lazy" })) : null, _jsxs("span", { className: "rudra-card__body", children: [reference.badge ? _jsx("span", { className: "rudra-card__badge", children: reference.badge }) : null, _jsx("span", { className: "rudra-card__title", children: product.title }), _jsx("span", { className: "rudra-card__price", children: context.formatPrice(product) }), reference.reason ? _jsx("span", { className: "rudra-card__reason", children: reference.reason }) : null] })] }));
|
|
17
|
+
return (_jsxs("a", { href: context.hrefForSku(product.sku), "data-rudra-sku": product.sku, "data-rudra-basis": reference.basis, className: isFeatured ? 'rudra-card rudra-card--featured' : 'rudra-card', children: [product.imageUrl ? (_jsx("img", { className: "rudra-card__image", src: product.imageUrl, alt: "", loading: "lazy" })) : null, _jsxs("span", { className: "rudra-card__body", children: [reference.badge ? _jsx("span", { className: "rudra-card__badge", children: reference.badge }) : null, ' ', _jsx("span", { className: "rudra-card__title", children: product.title }), ' ', _jsx("span", { className: "rudra-card__price", children: context.formatPrice(product) }), ' ', reference.reason ? _jsx("span", { className: "rudra-card__reason", children: reference.reason }) : null] })] }));
|
|
19
18
|
}
|
|
20
19
|
//# sourceMappingURL=product-card.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-card.js","sourceRoot":"","sources":["../../src/blocks/product-card.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAA2B,MAAM,sBAAsB,CAAC;AAEhF;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,SAAS,EACT,OAAO,GAIR;IACC,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IACjE
|
|
1
|
+
{"version":3,"file":"product-card.js","sourceRoot":"","sources":["../../src/blocks/product-card.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAA2B,MAAM,sBAAsB,CAAC;AAEhF;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,SAAS,EACT,OAAO,GAIR;IACC,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IACjE,+FAA+F;IAC/F,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,KAAK,UAAU,CAAC;IAErD,OAAO,CACL,aACE,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,oBACrB,OAAO,CAAC,GAAG,sBACT,SAAS,CAAC,KAAK,EACjC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,YAAY,aAEvE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAClB,cAAK,SAAS,EAAC,mBAAmB,EAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAC,EAAE,EAAC,OAAO,EAAC,MAAM,GAAG,CACnF,CAAC,CAAC,CAAC,IAAI,EAGR,gBAAM,SAAS,EAAC,kBAAkB,aAC/B,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,mBAAmB,YAAE,SAAS,CAAC,KAAK,GAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,EAC3F,eAAM,SAAS,EAAC,mBAAmB,YAAE,OAAO,CAAC,KAAK,GAAQ,EAAC,GAAG,EAC9D,eAAM,SAAS,EAAC,mBAAmB,YAAE,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,GAAQ,EAAC,GAAG,EAC5E,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,oBAAoB,YAAE,SAAS,CAAC,MAAM,GAAQ,CAAC,CAAC,CAAC,IAAI,IACpF,IACL,CACL,CAAC;AACJ,CAAC"}
|
|
@@ -8,7 +8,9 @@ export interface RudraComponentProps {
|
|
|
8
8
|
*
|
|
9
9
|
* A list of products, or anything keyed by SKU — a `Map`, or your own view
|
|
10
10
|
* over a catalog too large to hold in one. The renderers only ever call
|
|
11
|
-
* `get(sku)
|
|
11
|
+
* `get(sku)`, and `has` is how a keyed catalog is told apart from a list. The
|
|
12
|
+
* type asks for a `ReadonlyMap`, so a view of your own implements one or is
|
|
13
|
+
* cast with `as unknown as ReadonlyMap<string, Product>`.
|
|
12
14
|
*
|
|
13
15
|
* Validate them with `productSchema` from `@rudra-js/core` — the same schema
|
|
14
16
|
* your candidates already passed — not with `parseTrackingInput`, which
|
|
@@ -43,14 +45,15 @@ export interface RudraComponentProps {
|
|
|
43
45
|
hasDiagnostics?: boolean;
|
|
44
46
|
className?: string;
|
|
45
47
|
}
|
|
46
|
-
/** A list of products, or
|
|
48
|
+
/** A list of products, or a `ReadonlyMap` keyed by SKU, which a view of your own can implement. */
|
|
47
49
|
export type ProductCatalog = readonly Product[] | ReadonlyMap<string, Product>;
|
|
48
50
|
/**
|
|
49
51
|
* Renders a component specification.
|
|
50
52
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
53
|
+
* Plain React with no hooks, no state and no effects, so it renders with
|
|
54
|
+
* `renderToString`, in a client-rendered app, or as a Server Component where the
|
|
55
|
+
* framework supports them. Rendered on the server, the whole component arrives
|
|
56
|
+
* in the initial HTML response, which is what removes the pop-in a
|
|
54
57
|
* client-fetched recommendation rail has — and what makes the content visible
|
|
55
58
|
* to a crawler that does not run JavaScript.
|
|
56
59
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rudra-component.d.ts","sourceRoot":"","sources":["../src/rudra-component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAQ5E,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,aAAa,CAAC;IACpB
|
|
1
|
+
{"version":3,"file":"rudra-component.d.ts","sourceRoot":"","sources":["../src/rudra-component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAQ5E,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,aAAa,CAAC;IACpB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,EAAE,cAAc,CAAC;IACzB,oFAAoF;IACpF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;IAC3C,0FAA0F;IAC1F,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mGAAmG;AACnG,MAAM,MAAM,cAAc,GAAG,SAAS,OAAO,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAsF/E;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAA0B,EAC1B,UAA8B,EAC9B,WAAW,EACX,iBAAiB,EACjB,MAAM,EACN,cAAsB,EACtB,SAAS,GACV,EAAE,mBAAmB,sCA8CrB"}
|
package/dist/rudra-component.js
CHANGED
|
@@ -28,6 +28,9 @@ function hasContent(block, products, bundles) {
|
|
|
28
28
|
case 'carousel':
|
|
29
29
|
return block.items.some((reference) => sellableProduct(products, reference.sku) !== undefined);
|
|
30
30
|
case 'hero':
|
|
31
|
+
return (block.headline.length > 0 ||
|
|
32
|
+
(block.body !== null && block.body.length > 0) ||
|
|
33
|
+
(block.sku !== null && sellableProduct(products, block.sku) !== undefined));
|
|
31
34
|
case 'banner':
|
|
32
35
|
case 'copy':
|
|
33
36
|
return true;
|
|
@@ -66,9 +69,10 @@ function renderBlock(block, context, registry, index) {
|
|
|
66
69
|
/**
|
|
67
70
|
* Renders a component specification.
|
|
68
71
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
+
* Plain React with no hooks, no state and no effects, so it renders with
|
|
73
|
+
* `renderToString`, in a client-rendered app, or as a Server Component where the
|
|
74
|
+
* framework supports them. Rendered on the server, the whole component arrives
|
|
75
|
+
* in the initial HTML response, which is what removes the pop-in a
|
|
72
76
|
* client-fetched recommendation rail has — and what makes the content visible
|
|
73
77
|
* to a crawler that does not run JavaScript.
|
|
74
78
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rudra-component.js","sourceRoot":"","sources":["../src/rudra-component.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,GAEhB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAsB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"rudra-component.js","sourceRoot":"","sources":["../src/rudra-component.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rudra-js/react",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "Renders a rudra component specification with React 18 or 19, on the server or in the browser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@rudra-js/core": "^0.7.
|
|
54
|
+
"@rudra-js/core": "^0.7.4",
|
|
55
55
|
"react": "^18.2.0 || ^19.0.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@rudra-js/core": "0.7.
|
|
58
|
+
"@rudra-js/core": "0.7.4",
|
|
59
59
|
"@types/react": "^19.3.0",
|
|
60
60
|
"@types/react-dom": "^19.3.0",
|
|
61
61
|
"react": "^19.3.0",
|
|
@@ -23,7 +23,7 @@ export function HeroRenderer({
|
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<section className="rudra-hero">
|
|
26
|
-
<h3 className="rudra-hero__headline">{block.headline}</h3>
|
|
26
|
+
{block.headline ? <h3 className="rudra-hero__headline">{block.headline}</h3> : null}
|
|
27
27
|
{block.body ? <p className="rudra-hero__body">{block.body}</p> : null}
|
|
28
28
|
{product ? (
|
|
29
29
|
<a
|
|
@@ -31,8 +31,7 @@ export function HeroRenderer({
|
|
|
31
31
|
href={context.hrefForSku(product.sku)}
|
|
32
32
|
data-rudra-sku={product.sku}
|
|
33
33
|
>
|
|
34
|
-
{product.title}
|
|
35
|
-
<span className="rudra-hero__price">{context.formatPrice(product)}</span>
|
|
34
|
+
{product.title} <span className="rudra-hero__price">{context.formatPrice(product)}</span>{' '}
|
|
36
35
|
{block.ctaLabel ? <span className="rudra-hero__cta">{block.ctaLabel}</span> : null}
|
|
37
36
|
</a>
|
|
38
37
|
) : null}
|
|
@@ -83,7 +82,7 @@ export function CarouselRenderer({
|
|
|
83
82
|
export function BannerRenderer({ block }: { block: BannerBlock }) {
|
|
84
83
|
return (
|
|
85
84
|
<aside className="rudra-banner" data-rudra-banner-tone={block.tone}>
|
|
86
|
-
<span className="rudra-banner__text">{block.text}</span>
|
|
85
|
+
<span className="rudra-banner__text">{block.text}</span>{' '}
|
|
87
86
|
{block.ctaLabel ? <span className="rudra-banner__cta">{block.ctaLabel}</span> : null}
|
|
88
87
|
</aside>
|
|
89
88
|
);
|
|
@@ -17,8 +17,7 @@ export function ProductCard({
|
|
|
17
17
|
context: BlockRenderContext;
|
|
18
18
|
}) {
|
|
19
19
|
const product = sellableProduct(context.products, reference.sku);
|
|
20
|
-
//
|
|
21
|
-
// unreachable. Rendering nothing beats rendering a card with holes in it.
|
|
20
|
+
// Gone from the catalog, or sold out since the spec was made: render nothing, not half a card.
|
|
22
21
|
if (!product) return null;
|
|
23
22
|
|
|
24
23
|
const isFeatured = reference.emphasis === 'featured';
|
|
@@ -34,10 +33,11 @@ export function ProductCard({
|
|
|
34
33
|
<img className="rudra-card__image" src={product.imageUrl} alt="" loading="lazy" />
|
|
35
34
|
) : null}
|
|
36
35
|
|
|
36
|
+
{/* The spaces are the link's accessible name: "Shoe $53.67 Popular", not "Shoe$53.67Popular". */}
|
|
37
37
|
<span className="rudra-card__body">
|
|
38
|
-
{reference.badge ? <span className="rudra-card__badge">{reference.badge}</span> : null}
|
|
39
|
-
<span className="rudra-card__title">{product.title}</span>
|
|
40
|
-
<span className="rudra-card__price">{context.formatPrice(product)}</span>
|
|
38
|
+
{reference.badge ? <span className="rudra-card__badge">{reference.badge}</span> : null}{' '}
|
|
39
|
+
<span className="rudra-card__title">{product.title}</span>{' '}
|
|
40
|
+
<span className="rudra-card__price">{context.formatPrice(product)}</span>{' '}
|
|
41
41
|
{reference.reason ? <span className="rudra-card__reason">{reference.reason}</span> : null}
|
|
42
42
|
</span>
|
|
43
43
|
</a>
|
package/src/rudra-component.tsx
CHANGED
|
@@ -16,7 +16,9 @@ export interface RudraComponentProps {
|
|
|
16
16
|
*
|
|
17
17
|
* A list of products, or anything keyed by SKU — a `Map`, or your own view
|
|
18
18
|
* over a catalog too large to hold in one. The renderers only ever call
|
|
19
|
-
* `get(sku)
|
|
19
|
+
* `get(sku)`, and `has` is how a keyed catalog is told apart from a list. The
|
|
20
|
+
* type asks for a `ReadonlyMap`, so a view of your own implements one or is
|
|
21
|
+
* cast with `as unknown as ReadonlyMap<string, Product>`.
|
|
20
22
|
*
|
|
21
23
|
* Validate them with `productSchema` from `@rudra-js/core` — the same schema
|
|
22
24
|
* your candidates already passed — not with `parseTrackingInput`, which
|
|
@@ -52,7 +54,7 @@ export interface RudraComponentProps {
|
|
|
52
54
|
className?: string;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
/** A list of products, or
|
|
57
|
+
/** A list of products, or a `ReadonlyMap` keyed by SKU, which a view of your own can implement. */
|
|
56
58
|
export type ProductCatalog = readonly Product[] | ReadonlyMap<string, Product>;
|
|
57
59
|
|
|
58
60
|
// Asks what the renderers call rather than which class the host built, since
|
|
@@ -91,6 +93,11 @@ function hasContent(
|
|
|
91
93
|
(reference) => sellableProduct(products, reference.sku) !== undefined,
|
|
92
94
|
);
|
|
93
95
|
case 'hero':
|
|
96
|
+
return (
|
|
97
|
+
block.headline.length > 0 ||
|
|
98
|
+
(block.body !== null && block.body.length > 0) ||
|
|
99
|
+
(block.sku !== null && sellableProduct(products, block.sku) !== undefined)
|
|
100
|
+
);
|
|
94
101
|
case 'banner':
|
|
95
102
|
case 'copy':
|
|
96
103
|
return true;
|
|
@@ -137,9 +144,10 @@ function renderBlock(
|
|
|
137
144
|
/**
|
|
138
145
|
* Renders a component specification.
|
|
139
146
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
147
|
+
* Plain React with no hooks, no state and no effects, so it renders with
|
|
148
|
+
* `renderToString`, in a client-rendered app, or as a Server Component where the
|
|
149
|
+
* framework supports them. Rendered on the server, the whole component arrives
|
|
150
|
+
* in the initial HTML response, which is what removes the pop-in a
|
|
143
151
|
* client-fetched recommendation rail has — and what makes the content visible
|
|
144
152
|
* to a crawler that does not run JavaScript.
|
|
145
153
|
*
|