@asteby/storefront-react 0.2.0 → 0.4.0
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 +201 -0
- package/dist/chunk-C6QZNKRE.js +162 -0
- package/dist/chunk-C6QZNKRE.js.map +1 -0
- package/dist/components-B5XhSH-m.d.cts +54 -0
- package/dist/components-B5XhSH-m.d.ts +54 -0
- package/dist/index.d.cts +6 -54
- package/dist/index.d.ts +6 -54
- package/dist/index.js +6 -159
- package/dist/index.js.map +1 -1
- package/dist/mockups.cjs +567 -0
- package/dist/mockups.cjs.map +1 -0
- package/dist/mockups.d.cts +262 -0
- package/dist/mockups.d.ts +262 -0
- package/dist/mockups.js +440 -0
- package/dist/mockups.js.map +1 -0
- package/package.json +12 -2
package/dist/mockups.cjs
ADDED
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var mockup = require('@asteby/storefront/mockup');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
require('@asteby/storefront');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
// src/mockups/product-image.tsx
|
|
9
|
+
var DEFAULT_IMAGE_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600];
|
|
10
|
+
var defaultImageLoader = (src, width, height) => {
|
|
11
|
+
try {
|
|
12
|
+
const isAbsolute = /^[a-z][a-z0-9+.-]*:/i.test(src);
|
|
13
|
+
const url = new URL(src, "http://relative.invalid");
|
|
14
|
+
url.searchParams.set("width", String(width));
|
|
15
|
+
if (height) url.searchParams.set("height", String(height));
|
|
16
|
+
return isAbsolute ? url.toString() : url.pathname + url.search + url.hash;
|
|
17
|
+
} catch {
|
|
18
|
+
return src;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
function parseRatio(ratio) {
|
|
22
|
+
if (!ratio) return null;
|
|
23
|
+
const [w, h] = ratio.split(/[/:]/).map((n) => Number.parseFloat(n));
|
|
24
|
+
return w && h ? w / h : null;
|
|
25
|
+
}
|
|
26
|
+
function Image({
|
|
27
|
+
data,
|
|
28
|
+
sizes = "100vw",
|
|
29
|
+
widths = DEFAULT_IMAGE_WIDTHS,
|
|
30
|
+
loader = defaultImageLoader,
|
|
31
|
+
priority = false,
|
|
32
|
+
aspectRatio,
|
|
33
|
+
alt,
|
|
34
|
+
loading,
|
|
35
|
+
decoding,
|
|
36
|
+
...rest
|
|
37
|
+
}) {
|
|
38
|
+
const ratio = parseRatio(aspectRatio) ?? (data.width && data.height ? data.width / data.height : null);
|
|
39
|
+
let candidates = [...widths].sort((a, b) => a - b);
|
|
40
|
+
if (data.width) {
|
|
41
|
+
const capped = candidates.filter((w) => w <= data.width);
|
|
42
|
+
candidates = capped.length ? capped : [data.width];
|
|
43
|
+
}
|
|
44
|
+
const srcSet = loader ? candidates.map((w) => `${loader(data.url, w, ratio ? Math.round(w / ratio) : void 0)} ${w}w`).join(", ") : void 0;
|
|
45
|
+
const width = data.width ?? candidates[candidates.length - 1];
|
|
46
|
+
const height = data.height ?? (ratio && width ? Math.round(width / ratio) : void 0);
|
|
47
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
48
|
+
"img",
|
|
49
|
+
{
|
|
50
|
+
src: data.url,
|
|
51
|
+
alt: alt ?? data.alt ?? "",
|
|
52
|
+
...srcSet ? { srcSet, sizes } : {},
|
|
53
|
+
...width ? { width } : {},
|
|
54
|
+
...height ? { height } : {},
|
|
55
|
+
loading: loading ?? (priority ? "eager" : "lazy"),
|
|
56
|
+
decoding: decoding ?? (priority ? "auto" : "async"),
|
|
57
|
+
...priority ? { fetchPriority: "high" } : {},
|
|
58
|
+
...rest
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
var MOCKUP_SIZE = 400;
|
|
63
|
+
var MOCKUP_TOKENS = {
|
|
64
|
+
bg: "var(--mockup-bg, var(--color-muted, light-dark(#f4f4f5, #27272a)))",
|
|
65
|
+
surface: "var(--mockup-surface, light-dark(#3f3f46, #0a0a0a))",
|
|
66
|
+
"surface-ink": "var(--mockup-surface-ink, light-dark(#e4e4e7, #a1a1aa))",
|
|
67
|
+
label: "var(--mockup-label, var(--color-background, light-dark(#ffffff, #18181b)))",
|
|
68
|
+
"label-ink": "var(--mockup-label-ink, var(--color-foreground, light-dark(#18181b, #fafafa)))",
|
|
69
|
+
ink: "var(--mockup-ink, var(--color-foreground, light-dark(#18181b, #fafafa)))",
|
|
70
|
+
muted: "var(--mockup-muted, var(--color-muted-foreground, light-dark(#71717a, #a1a1aa)))",
|
|
71
|
+
accent: "var(--mockup-accent, var(--color-primary, light-dark(#52525b, #d4d4d8)))",
|
|
72
|
+
"accent-ink": "var(--mockup-accent-ink, var(--color-primary-foreground, light-dark(#ffffff, #18181b)))"
|
|
73
|
+
};
|
|
74
|
+
var ROOT_STYLE = {
|
|
75
|
+
...Object.fromEntries(Object.entries(MOCKUP_TOKENS).map(([k, v]) => [`--mk-${k}`, v])),
|
|
76
|
+
fontFamily: "var(--mockup-font, var(--font-heading, var(--font-sans, ui-sans-serif, system-ui, sans-serif)))",
|
|
77
|
+
textTransform: "var(--mockup-text-transform, none)"
|
|
78
|
+
};
|
|
79
|
+
function tone(t, prop = "fill") {
|
|
80
|
+
return { [prop]: `var(--mk-${t})` };
|
|
81
|
+
}
|
|
82
|
+
var MOCKUP_MONO = "var(--mockup-font-mono, var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace))";
|
|
83
|
+
function wrapMockupText(text, maxChars, maxLines, wordMax = maxChars) {
|
|
84
|
+
if (!text || maxLines < 1) return [];
|
|
85
|
+
const cap = Math.max(maxChars, wordMax);
|
|
86
|
+
const words = text.split(" ").filter(Boolean);
|
|
87
|
+
const lines = [];
|
|
88
|
+
let current = "";
|
|
89
|
+
let cut = false;
|
|
90
|
+
for (let i = 0; i < words.length; i++) {
|
|
91
|
+
let word = words[i];
|
|
92
|
+
if (Array.from(word).length > cap) word = Array.from(word).slice(0, cap - 1).join("") + "\u2026";
|
|
93
|
+
const next = current ? `${current} ${word}` : word;
|
|
94
|
+
if (Array.from(next).length <= maxChars || !current) {
|
|
95
|
+
current = next;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
lines.push(current);
|
|
99
|
+
current = word;
|
|
100
|
+
if (lines.length === maxLines) {
|
|
101
|
+
cut = true;
|
|
102
|
+
current = "";
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (current) lines.push(current);
|
|
107
|
+
if (cut || lines.length > maxLines) {
|
|
108
|
+
const kept = lines.slice(0, maxLines);
|
|
109
|
+
const last = Array.from(kept[maxLines - 1] ?? "");
|
|
110
|
+
const limit = Math.max(maxChars, Math.min(cap, last.length));
|
|
111
|
+
kept[maxLines - 1] = (last.length >= limit ? last.slice(0, limit - 1).join("") : last.join("")).replace(/[\s.,;:…-]+$/, "") + "\u2026";
|
|
112
|
+
return kept;
|
|
113
|
+
}
|
|
114
|
+
return lines;
|
|
115
|
+
}
|
|
116
|
+
function mockupWordMax(width, min, ratio = 0.62) {
|
|
117
|
+
return Math.max(1, Math.floor(width / (min * ratio)));
|
|
118
|
+
}
|
|
119
|
+
function fitMockupFontSize(lines, width, max, min = 10, ratio = 0.62) {
|
|
120
|
+
const longest = Math.max(1, ...lines.map((l) => Array.from(l).length));
|
|
121
|
+
return Math.round(Math.max(min, Math.min(max, width / (longest * ratio))));
|
|
122
|
+
}
|
|
123
|
+
function MockupText({ lines, x, y, size, lineHeight = 1.12, tone: t = "ink", weight = 700, anchor = "middle", mono, style, ...rest }) {
|
|
124
|
+
if (!lines.length) return null;
|
|
125
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
126
|
+
"text",
|
|
127
|
+
{
|
|
128
|
+
x,
|
|
129
|
+
y,
|
|
130
|
+
fontSize: size,
|
|
131
|
+
fontWeight: weight,
|
|
132
|
+
textAnchor: anchor,
|
|
133
|
+
style: { ...tone(t), ...mono ? { fontFamily: MOCKUP_MONO } : null, ...style },
|
|
134
|
+
...rest,
|
|
135
|
+
children: lines.length === 1 ? lines[0] : lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("tspan", { x, dy: i === 0 ? 0 : size * lineHeight, children: line }, i))
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function MockupFrame({ label, decorative = false, kind, children, style, width = MOCKUP_SIZE, height = MOCKUP_SIZE, ...rest }) {
|
|
140
|
+
const a11y = decorative ? { "aria-hidden": true } : { role: "img", "aria-label": label };
|
|
141
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
142
|
+
"svg",
|
|
143
|
+
{
|
|
144
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
145
|
+
viewBox: `0 0 ${MOCKUP_SIZE} ${MOCKUP_SIZE}`,
|
|
146
|
+
width,
|
|
147
|
+
height,
|
|
148
|
+
focusable: "false",
|
|
149
|
+
"data-mockup": kind,
|
|
150
|
+
...a11y,
|
|
151
|
+
style: { ...ROOT_STYLE, ...style },
|
|
152
|
+
...rest,
|
|
153
|
+
children: [
|
|
154
|
+
decorative ? null : /* @__PURE__ */ jsxRuntime.jsx("title", { children: label }),
|
|
155
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: MOCKUP_SIZE, height: MOCKUP_SIZE, style: tone("bg") }),
|
|
156
|
+
children
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
function GenericMockup({ data, label, labels: _labels, ...rest }) {
|
|
162
|
+
const titleLines = wrapMockupText(data.title, 28, 2);
|
|
163
|
+
const titleFont = fitMockupFontSize(titleLines, 330, 22, 13);
|
|
164
|
+
const titleY = titleLines.length > 1 ? 340 : 352;
|
|
165
|
+
const eyebrow = data.category ? wrapMockupText(data.category, 30, 1) : [];
|
|
166
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MockupFrame, { kind: "generic", label, ...rest, children: [
|
|
167
|
+
/* @__PURE__ */ jsxRuntime.jsx("ellipse", { cx: 200, cy: 300, rx: 130, ry: 8, style: { ...tone("ink"), opacity: 0.08 } }),
|
|
168
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M90 82L114 64H330L306 82Z", style: tone("accent") }),
|
|
169
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M306 82L330 64V280L306 298Z", style: tone("surface") }),
|
|
170
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M306 82L330 64V280L306 298Z", style: { fill: "#000", opacity: 0.22 } }),
|
|
171
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: 90, y: 82, width: 216, height: 216, style: tone("surface") }),
|
|
172
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: 186, y: 82, width: 24, height: 46, style: { ...tone("accent"), opacity: 0.9 } }),
|
|
173
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: 198, cy: 186, r: 50, style: tone("label") }),
|
|
174
|
+
data.monogram ? /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: [data.monogram], x: 198, y: 202, size: 44, weight: 900, tone: "label-ink", letterSpacing: 1 }) : null,
|
|
175
|
+
data.brand ? /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: wrapMockupText(data.brand, 20, 1), x: 198, y: 272, size: 13, weight: 700, tone: "surface-ink", letterSpacing: 2 }) : null,
|
|
176
|
+
eyebrow.length > 0 && titleLines.length < 2 ? /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: eyebrow, x: 200, y: 326, size: 12, weight: 600, tone: "muted", letterSpacing: 1 }) : null,
|
|
177
|
+
/* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: titleLines, x: 200, y: titleY, size: titleFont, lineHeight: 1.2, weight: 700 })
|
|
178
|
+
] });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/mockups/labels.ts
|
|
182
|
+
var DEFAULT_MOCKUP_LABELS = {
|
|
183
|
+
illustration: "Ilustraci\xF3n de referencia",
|
|
184
|
+
size: "medida",
|
|
185
|
+
partNumber: "N.\xBA de parte",
|
|
186
|
+
oem: "OEM",
|
|
187
|
+
fits: "Compatible con",
|
|
188
|
+
flavor: "sabor",
|
|
189
|
+
servings: "porciones",
|
|
190
|
+
lightTruck: "LT",
|
|
191
|
+
runFlat: "Run flat"
|
|
192
|
+
};
|
|
193
|
+
var has = (haystack, needle) => haystack.toLowerCase().includes(needle.toLowerCase());
|
|
194
|
+
function describeMockup(data, labels = DEFAULT_MOCKUP_LABELS) {
|
|
195
|
+
const title = data.title ?? "";
|
|
196
|
+
const parts = [title];
|
|
197
|
+
switch (data.kind) {
|
|
198
|
+
case "tire":
|
|
199
|
+
if (data.size && !has(title, data.size)) parts.push(`${labels.size} ${data.size}`);
|
|
200
|
+
if (data.service && !has(title, data.service)) parts.push(data.service);
|
|
201
|
+
break;
|
|
202
|
+
case "part":
|
|
203
|
+
if (data.partNumber && !has(title, data.partNumber)) parts.push(`${labels.partNumber} ${data.partNumber}`);
|
|
204
|
+
break;
|
|
205
|
+
case "supplement":
|
|
206
|
+
if (data.flavor && !has(title, data.flavor)) parts.push(`${labels.flavor} ${data.flavor}`);
|
|
207
|
+
if (data.presentation && !has(title, data.presentation)) parts.push(data.presentation);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
const detail = parts.filter((p) => !!p).join(", ");
|
|
211
|
+
return detail ? `${labels.illustration}: ${detail}` : labels.illustration;
|
|
212
|
+
}
|
|
213
|
+
var X = 48;
|
|
214
|
+
var W = 304;
|
|
215
|
+
var PAD = 24;
|
|
216
|
+
var INNER = W - PAD * 2;
|
|
217
|
+
var SECONDARY = { opacity: 0.68 };
|
|
218
|
+
function PartMockup({ data, label, labels, ...rest }) {
|
|
219
|
+
const number = data.partNumber ?? data.oemNumber;
|
|
220
|
+
const numberCaption = data.partNumber ? labels.partNumber : labels.oem;
|
|
221
|
+
const bandLines = number ? [number] : wrapMockupText(data.title, 22, 2);
|
|
222
|
+
const bandFont = number ? fitMockupFontSize(bandLines, INNER, 34, 14, 0.62) : fitMockupFontSize(bandLines, INNER, 24, 14);
|
|
223
|
+
const header = data.brand ?? data.category;
|
|
224
|
+
const headerAside = data.brand ? data.position ?? data.category : data.position;
|
|
225
|
+
const body = [];
|
|
226
|
+
if (number) body.push({ lines: wrapMockupText(data.title, 26, 2), size: 17, weight: 700 });
|
|
227
|
+
if (data.fitment) body.push({ lines: wrapMockupText(`${labels.fits}: ${data.fitment}`, 34, 2), size: 13, secondary: true, weight: 500 });
|
|
228
|
+
if (data.partNumber && data.oemNumber) body.push({ lines: [`${labels.oem} ${data.oemNumber}`], size: 13, secondary: true, weight: 600, mono: true });
|
|
229
|
+
let y = 248;
|
|
230
|
+
const placed = [];
|
|
231
|
+
for (const b of body) {
|
|
232
|
+
const at = y + b.size;
|
|
233
|
+
const room = Math.floor((330 - at) / (b.size * 1.2)) + 1;
|
|
234
|
+
if (room < 1 || !b.lines.length) continue;
|
|
235
|
+
const lines = b.lines.length > room ? [...b.lines.slice(0, room - 1), b.lines[room - 1].replace(/[\s.,;:…-]*$/, "\u2026")] : b.lines;
|
|
236
|
+
placed.push({ ...b, lines, y: at });
|
|
237
|
+
y = at + (lines.length - 1) * b.size * 1.2 + 10;
|
|
238
|
+
}
|
|
239
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MockupFrame, { kind: "part", label, ...rest, children: [
|
|
240
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M78 98 C60 52 36 46 14 30", fill: "none", strokeWidth: 2, strokeLinecap: "round", style: tone("muted", "stroke") }),
|
|
241
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: X, y: 66, width: W, height: 276, rx: 18, style: tone("label") }),
|
|
242
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: X + 0.75, y: 66.75, width: W - 1.5, height: 274.5, rx: 17.25, fill: "none", strokeWidth: 1.5, style: { ...tone("muted", "stroke"), opacity: 0.3 } }),
|
|
243
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: 80, cy: 98, r: 9, style: tone("bg") }),
|
|
244
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: 80, cy: 98, r: 9, fill: "none", strokeWidth: 1.5, style: { ...tone("muted", "stroke"), opacity: 0.5 } }),
|
|
245
|
+
header ? /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: wrapMockupText(header, headerAside ? 14 : 22, 1), x: 104, y: 104, size: 16, weight: 800, tone: "label-ink", anchor: "start", letterSpacing: 0.5 }) : null,
|
|
246
|
+
headerAside ? /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: wrapMockupText(headerAside, header ? 14 : 22, 1), x: X + W - PAD, y: 104, size: 12, weight: 600, tone: "label-ink", anchor: "end", style: SECONDARY }) : null,
|
|
247
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: X, y: 128, width: W, height: 92, style: tone("accent") }),
|
|
248
|
+
number ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
249
|
+
/* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: [numberCaption], x: X + PAD, y: 154, size: 12, weight: 600, tone: "accent-ink", anchor: "start", letterSpacing: 1, style: { opacity: 0.85 } }),
|
|
250
|
+
/* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: bandLines, x: X + PAD, y: 198, size: bandFont, weight: 800, tone: "accent-ink", anchor: "start", mono: true })
|
|
251
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: bandLines, x: X + PAD, y: bandLines.length > 1 ? 168 : 182, size: bandFont, weight: 800, tone: "accent-ink", anchor: "start" }),
|
|
252
|
+
placed.map((b, i) => /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: b.lines, x: X + PAD, y: b.y, size: b.size, lineHeight: 1.2, weight: b.weight, tone: "label-ink", anchor: "start", mono: b.mono, style: b.secondary ? SECONDARY : void 0 }, i))
|
|
253
|
+
] });
|
|
254
|
+
}
|
|
255
|
+
var SHAPES = {
|
|
256
|
+
tub: {
|
|
257
|
+
label: { x: 131, y: 128, w: 138, h: 200 },
|
|
258
|
+
draw: () => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
259
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: 120, y: 44, width: 160, height: 54, rx: 8, style: tone("surface") }),
|
|
260
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: 128, y1: 71, x2: 272, y2: 71, strokeWidth: 42, strokeDasharray: "1.5 7.5", style: { ...tone("surface-ink", "stroke"), opacity: 0.18 } }),
|
|
261
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: 117, y: 95, width: 166, height: 9, rx: 2, style: tone("accent") }),
|
|
262
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M122 104H278L283 344Q283 362 265 362H135Q117 362 117 344Z", style: tone("surface") })
|
|
263
|
+
] })
|
|
264
|
+
},
|
|
265
|
+
bottle: {
|
|
266
|
+
label: { x: 131, y: 148, w: 138, h: 184 },
|
|
267
|
+
draw: () => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
268
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: 158, y: 46, width: 84, height: 40, rx: 7, style: tone("surface") }),
|
|
269
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: 164, y1: 66, x2: 236, y2: 66, strokeWidth: 30, strokeDasharray: "1.5 6.5", style: { ...tone("surface-ink", "stroke"), opacity: 0.18 } }),
|
|
270
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: 168, y: 84, width: 64, height: 12, style: tone("accent") }),
|
|
271
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M124 116Q124 96 148 96H252Q276 96 276 116L281 344Q281 362 263 362H137Q119 362 119 344Z", style: tone("surface") })
|
|
272
|
+
] })
|
|
273
|
+
},
|
|
274
|
+
bag: {
|
|
275
|
+
label: { x: 133, y: 118, w: 134, h: 196 },
|
|
276
|
+
draw: () => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
277
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M118 64Q200 50 282 64L297 344Q200 360 103 344Z", style: tone("surface") }),
|
|
278
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M118 64Q200 50 282 64L280 84Q200 70 120 84Z", style: tone("accent") }),
|
|
279
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M124 92Q200 79 276 92", fill: "none", strokeWidth: 2, strokeDasharray: "4 4", style: { ...tone("surface-ink", "stroke"), opacity: 0.4 } })
|
|
280
|
+
] })
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
function SupplementMockup({ data, label, labels, ...rest }) {
|
|
284
|
+
const shape = SHAPES[data.shape] ?? SHAPES.tub;
|
|
285
|
+
const box = shape.label;
|
|
286
|
+
const cx = box.x + box.w / 2;
|
|
287
|
+
const inner = box.w - 20;
|
|
288
|
+
const amount = data.presentation ?? (data.servings ? `${data.servings} ${labels.servings}` : null);
|
|
289
|
+
const nameLines = wrapMockupText(data.title, 12, 3, mockupWordMax(inner, 12, 0.64));
|
|
290
|
+
const nameFont = fitMockupFontSize(nameLines, inner, 26, 12, 0.64);
|
|
291
|
+
const flavor = data.flavor ? wrapMockupText(data.flavor, 18, 1)[0] : null;
|
|
292
|
+
const flavorW = flavor ? Math.min(inner, Array.from(flavor).length * 6.6 + 20) : 0;
|
|
293
|
+
const amountText = amount ? wrapMockupText(amount, 16, 1)[0] : null;
|
|
294
|
+
const amountW = amountText ? Math.min(inner, Array.from(amountText).length * 7.4 + 24) : 0;
|
|
295
|
+
const top = box.y + (data.brand ? 46 : 30);
|
|
296
|
+
const nameY = top + nameFont * 0.8;
|
|
297
|
+
const nameBottom = nameY + (nameLines.length - 1) * nameFont * 1.08;
|
|
298
|
+
const flavorY = Math.max(nameBottom + 14, box.y + box.h - 82);
|
|
299
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MockupFrame, { kind: "supplement", label, ...rest, children: [
|
|
300
|
+
/* @__PURE__ */ jsxRuntime.jsx("ellipse", { cx: 200, cy: 364, rx: 110, ry: 8, style: { ...tone("ink"), opacity: 0.1 } }),
|
|
301
|
+
shape.draw(),
|
|
302
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: box.x, y: box.y, width: box.w, height: box.h, rx: 6, style: tone("label") }),
|
|
303
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: box.x, y: box.y, width: box.w, height: 6, rx: 3, style: tone("accent") }),
|
|
304
|
+
data.brand ? /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: wrapMockupText(data.brand, 16, 1), x: cx, y: box.y + 28, size: 12, weight: 700, tone: "label-ink", letterSpacing: 1.5, style: { opacity: 0.68 } }) : null,
|
|
305
|
+
/* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: nameLines, x: cx, y: nameY, size: nameFont, lineHeight: 1.08, weight: 900, tone: "label-ink" }),
|
|
306
|
+
flavor ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
307
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: cx - flavorW / 2, y: flavorY, width: flavorW, height: 22, rx: 11, style: tone("accent") }),
|
|
308
|
+
/* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: [flavor], x: cx, y: flavorY + 15, size: 11.5, weight: 700, tone: "accent-ink" })
|
|
309
|
+
] }) : null,
|
|
310
|
+
amountText ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
311
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: cx - amountW / 2, y: box.y + box.h - 40, width: amountW, height: 26, rx: 13, style: tone("surface") }),
|
|
312
|
+
/* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: [amountText], x: cx, y: box.y + box.h - 22.5, size: 12.5, weight: 700, tone: "surface-ink", letterSpacing: 1 })
|
|
313
|
+
] }) : null
|
|
314
|
+
] });
|
|
315
|
+
}
|
|
316
|
+
var CX = 200;
|
|
317
|
+
var CY = 146;
|
|
318
|
+
function TireMockup({ data, label, labels, ...rest }) {
|
|
319
|
+
const split = data.width && data.aspect && data.rim;
|
|
320
|
+
const headline = split ? `${data.width}/${data.aspect} R${data.rim}` : data.size;
|
|
321
|
+
const sizeFont = headline ? fitMockupFontSize([headline], 330, 46, 22, 0.6) : 0;
|
|
322
|
+
const titleLines = headline ? [] : wrapMockupText(data.model ?? data.title, 24, 2);
|
|
323
|
+
const titleFont = fitMockupFontSize(titleLines, 320, 26, 14);
|
|
324
|
+
const extras = [data.lightTruck && !(headline ?? "").toUpperCase().startsWith(labels.lightTruck.toUpperCase()) ? labels.lightTruck : null, data.runFlat ? labels.runFlat : null].filter(Boolean).join(" \xB7 ");
|
|
325
|
+
const footer = [headline ? data.model : null, extras || null].filter(Boolean).join(" \xB7 ");
|
|
326
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MockupFrame, { kind: "tire", label, ...rest, children: [
|
|
327
|
+
/* @__PURE__ */ jsxRuntime.jsx("ellipse", { cx: CX, cy: CY + 128, rx: 120, ry: 8, style: { ...tone("ink"), opacity: 0.08 } }),
|
|
328
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: CX, cy: CY, r: 102, fill: "none", strokeWidth: 52, style: tone("surface", "stroke") }),
|
|
329
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: CX, cy: CY, r: 124, fill: "none", strokeWidth: 8, strokeDasharray: "8 9", style: tone("bg", "stroke") }),
|
|
330
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: CX, cy: CY, r: 111, fill: "none", strokeWidth: 1.5, strokeDasharray: "3 5", style: { ...tone("surface-ink", "stroke"), opacity: 0.35 } }),
|
|
331
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: CX, cy: CY, r: 86, fill: "none", strokeWidth: 1.5, style: { ...tone("surface-ink", "stroke"), opacity: 0.25 } }),
|
|
332
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: CX, cy: CY, r: 74, style: tone("muted") }),
|
|
333
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: CX, cy: CY, r: 45, fill: "none", strokeWidth: 36, strokeDasharray: "36 20.5", style: tone("surface", "stroke") }),
|
|
334
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: CX, cy: CY, r: 20, style: tone("label") }),
|
|
335
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: CX, cy: CY, r: 11, fill: "none", strokeWidth: 4, strokeDasharray: "2.5 11.3", style: tone("muted", "stroke") }),
|
|
336
|
+
data.brand ? /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: [data.brand], x: CX, y: 302, size: 15, weight: 700, tone: "muted", letterSpacing: 2 }) : null,
|
|
337
|
+
headline ? /* @__PURE__ */ jsxRuntime.jsx("text", { x: CX, y: 346, fontSize: sizeFont, fontWeight: 800, textAnchor: "middle", style: tone("ink"), children: split ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
338
|
+
`${data.width}/${data.aspect} `,
|
|
339
|
+
/* @__PURE__ */ jsxRuntime.jsx("tspan", { style: tone("accent"), children: `R${data.rim}` })
|
|
340
|
+
] }) : headline }) : /* @__PURE__ */ jsxRuntime.jsx(MockupText, { lines: titleLines, x: CX, y: titleLines.length > 1 ? 330 : 342, size: titleFont, weight: 700 }),
|
|
341
|
+
footer || data.service ? /* @__PURE__ */ jsxRuntime.jsxs("text", { x: CX, y: 380, fontSize: 15, fontWeight: 600, textAnchor: "middle", style: tone("muted"), children: [
|
|
342
|
+
data.service ? /* @__PURE__ */ jsxRuntime.jsx("tspan", { fontWeight: 800, style: tone("accent"), children: data.service }) : null,
|
|
343
|
+
data.service && footer ? " \xB7 " : null,
|
|
344
|
+
wrapMockupText(footer, data.service ? 34 : 40, 1)[0]
|
|
345
|
+
] }) : null
|
|
346
|
+
] });
|
|
347
|
+
}
|
|
348
|
+
var KIND_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
349
|
+
function normalize(kind, input) {
|
|
350
|
+
const r = typeof input === "function" ? { component: input } : input;
|
|
351
|
+
if (!r || typeof r.component !== "function") throw new TypeError(`Mockup "${kind}": component is required`);
|
|
352
|
+
return {
|
|
353
|
+
component: r.component,
|
|
354
|
+
fromProduct: r.fromProduct ?? ((product, variant) => mockup.mockupDataFromProduct(kind, product, variant)),
|
|
355
|
+
describe: r.describe ?? ((data, labels) => describeMockup(data, labels))
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function createMockupRegistry(initial = {}) {
|
|
359
|
+
const map = /* @__PURE__ */ new Map();
|
|
360
|
+
const registry = {
|
|
361
|
+
register(kind, renderer) {
|
|
362
|
+
if (!KIND_PATTERN.test(kind)) throw new TypeError(`Invalid mockup kind "${kind}" (use a-z, 0-9, - and _)`);
|
|
363
|
+
map.set(kind, normalize(kind, renderer));
|
|
364
|
+
return registry;
|
|
365
|
+
},
|
|
366
|
+
get: (kind) => map.get(kind),
|
|
367
|
+
has: (kind) => map.has(kind),
|
|
368
|
+
kinds: () => [...map.keys()],
|
|
369
|
+
extend(overrides = {}) {
|
|
370
|
+
const next = createMockupRegistry();
|
|
371
|
+
for (const [k, r] of map) next.register(k, r);
|
|
372
|
+
for (const [k, r] of Object.entries(overrides)) if (r) next.register(k, r);
|
|
373
|
+
return next;
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
for (const [k, r] of Object.entries(initial)) if (r) registry.register(k, r);
|
|
377
|
+
return registry;
|
|
378
|
+
}
|
|
379
|
+
var BUILTIN_MOCKUPS = {
|
|
380
|
+
tire: TireMockup,
|
|
381
|
+
part: PartMockup,
|
|
382
|
+
supplement: SupplementMockup,
|
|
383
|
+
generic: GenericMockup
|
|
384
|
+
};
|
|
385
|
+
var defaultMockupRegistry = createMockupRegistry(BUILTIN_MOCKUPS);
|
|
386
|
+
function registerMockup(kind, renderer) {
|
|
387
|
+
return defaultMockupRegistry.register(kind, renderer);
|
|
388
|
+
}
|
|
389
|
+
var MockupContext = react.createContext(null);
|
|
390
|
+
function MockupProvider({ registry, resolveKind, labels, children }) {
|
|
391
|
+
const parent = react.useContext(MockupContext);
|
|
392
|
+
const value = {
|
|
393
|
+
registry: registry ?? parent?.registry ?? defaultMockupRegistry,
|
|
394
|
+
resolveKind: resolveKind ?? parent?.resolveKind,
|
|
395
|
+
labels: { ...parent?.labels ?? DEFAULT_MOCKUP_LABELS, ...labels }
|
|
396
|
+
};
|
|
397
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MockupContext.Provider, { value, children });
|
|
398
|
+
}
|
|
399
|
+
function useMockupConfig() {
|
|
400
|
+
return react.useContext(MockupContext) ?? { registry: defaultMockupRegistry, labels: DEFAULT_MOCKUP_LABELS };
|
|
401
|
+
}
|
|
402
|
+
function ProductMockup({ kind, data, label, labels: labelOverrides, registry: registryProp, ...svg }) {
|
|
403
|
+
const config = useMockupConfig();
|
|
404
|
+
const registry = registryProp ?? config.registry;
|
|
405
|
+
const renderer = registry.get(kind) ?? registry.get("generic");
|
|
406
|
+
if (!renderer) return null;
|
|
407
|
+
const labels = labelOverrides ? { ...config.labels, ...labelOverrides } : config.labels;
|
|
408
|
+
const name = label ? `${labels.illustration}: ${label}` : renderer.describe(data, labels);
|
|
409
|
+
const Component = renderer.component;
|
|
410
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Component, { data, label: name, labels, ...svg });
|
|
411
|
+
}
|
|
412
|
+
function ProductImage({
|
|
413
|
+
product,
|
|
414
|
+
variant,
|
|
415
|
+
image,
|
|
416
|
+
kind: kindProp,
|
|
417
|
+
resolveKind: resolveProp,
|
|
418
|
+
registry: registryProp,
|
|
419
|
+
labels: labelOverrides,
|
|
420
|
+
alt,
|
|
421
|
+
className,
|
|
422
|
+
style,
|
|
423
|
+
id,
|
|
424
|
+
...imageProps
|
|
425
|
+
}) {
|
|
426
|
+
const config = useMockupConfig();
|
|
427
|
+
const img = image !== void 0 ? image : variant?.image ?? product.featured_image ?? product.images?.[0] ?? null;
|
|
428
|
+
if (img?.url) {
|
|
429
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Image, { data: img, alt: alt ?? img.alt ?? product.title, className, style, id, ...imageProps });
|
|
430
|
+
}
|
|
431
|
+
const registry = registryProp ?? config.registry;
|
|
432
|
+
let kind = kindProp;
|
|
433
|
+
if (!kind) {
|
|
434
|
+
const guess = mockup.resolveMockupKind(product, { kinds: registry.kinds() });
|
|
435
|
+
kind = (resolveProp ?? config.resolveKind)?.(product, guess) ?? guess;
|
|
436
|
+
}
|
|
437
|
+
const renderer = registry.get(kind) ?? registry.get("generic");
|
|
438
|
+
if (!renderer) return null;
|
|
439
|
+
const labels = labelOverrides ? { ...config.labels, ...labelOverrides } : config.labels;
|
|
440
|
+
const data = renderer.fromProduct(product, variant);
|
|
441
|
+
const decorative = alt === "";
|
|
442
|
+
const label = alt ? `${labels.illustration}: ${alt}` : renderer.describe(data, labels);
|
|
443
|
+
const Component = renderer.component;
|
|
444
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
445
|
+
Component,
|
|
446
|
+
{
|
|
447
|
+
data,
|
|
448
|
+
label,
|
|
449
|
+
labels,
|
|
450
|
+
decorative,
|
|
451
|
+
className,
|
|
452
|
+
style,
|
|
453
|
+
id,
|
|
454
|
+
"data-mockup-fallback": ""
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/mockups/define.tsx
|
|
460
|
+
var KIND_PATTERN2 = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
461
|
+
function defineMockup(definition) {
|
|
462
|
+
if (!definition || typeof definition.kind !== "string" || !KIND_PATTERN2.test(definition.kind)) {
|
|
463
|
+
throw new TypeError(`defineMockup: invalid kind "${String(definition?.kind)}" (use a-z, 0-9, - and _)`);
|
|
464
|
+
}
|
|
465
|
+
if (typeof definition.render !== "function") throw new TypeError(`defineMockup("${definition.kind}"): render is required`);
|
|
466
|
+
if (definition.match !== void 0 && typeof definition.match !== "function") throw new TypeError(`defineMockup("${definition.kind}"): match must be a function`);
|
|
467
|
+
return Object.freeze({ ...definition });
|
|
468
|
+
}
|
|
469
|
+
function createStoreMockups(definitions, options = {}) {
|
|
470
|
+
const base = options.base ?? "builtin";
|
|
471
|
+
const registry = base === "builtin" ? createMockupRegistry(BUILTIN_MOCKUPS) : base === "none" ? createMockupRegistry() : base.extend();
|
|
472
|
+
const seen = /* @__PURE__ */ new Set();
|
|
473
|
+
for (const d of definitions) {
|
|
474
|
+
const def = defineMockup(d);
|
|
475
|
+
if (seen.has(def.kind)) throw new TypeError(`createStoreMockups: kind "${def.kind}" is defined twice`);
|
|
476
|
+
seen.add(def.kind);
|
|
477
|
+
registry.register(def.kind, {
|
|
478
|
+
component: def.render,
|
|
479
|
+
...def.data ? { fromProduct: def.data } : null,
|
|
480
|
+
...def.describe ? { describe: def.describe } : null
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
const matchers = definitions.filter((d) => typeof d.match === "function");
|
|
484
|
+
const resolveKind = (product, guess) => {
|
|
485
|
+
for (const d of matchers) if (d.match(product, guess)) return d.kind;
|
|
486
|
+
return guess;
|
|
487
|
+
};
|
|
488
|
+
const out = { registry, resolveKind, kinds: [...seen] };
|
|
489
|
+
if (options.labels) out.labels = options.labels;
|
|
490
|
+
return out;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
Object.defineProperty(exports, "BUILTIN_MOCKUP_KINDS", {
|
|
494
|
+
enumerable: true,
|
|
495
|
+
get: function () { return mockup.BUILTIN_MOCKUP_KINDS; }
|
|
496
|
+
});
|
|
497
|
+
Object.defineProperty(exports, "DEFAULT_MOCKUP_KIND_RULES", {
|
|
498
|
+
enumerable: true,
|
|
499
|
+
get: function () { return mockup.DEFAULT_MOCKUP_KIND_RULES; }
|
|
500
|
+
});
|
|
501
|
+
Object.defineProperty(exports, "MOCKUP_KIND_ATTRIBUTES", {
|
|
502
|
+
enumerable: true,
|
|
503
|
+
get: function () { return mockup.MOCKUP_KIND_ATTRIBUTES; }
|
|
504
|
+
});
|
|
505
|
+
Object.defineProperty(exports, "MOCKUP_TEXT_LIMITS", {
|
|
506
|
+
enumerable: true,
|
|
507
|
+
get: function () { return mockup.MOCKUP_TEXT_LIMITS; }
|
|
508
|
+
});
|
|
509
|
+
Object.defineProperty(exports, "cleanMockupText", {
|
|
510
|
+
enumerable: true,
|
|
511
|
+
get: function () { return mockup.cleanMockupText; }
|
|
512
|
+
});
|
|
513
|
+
Object.defineProperty(exports, "genericMockupData", {
|
|
514
|
+
enumerable: true,
|
|
515
|
+
get: function () { return mockup.genericMockupData; }
|
|
516
|
+
});
|
|
517
|
+
Object.defineProperty(exports, "mockupDataFromProduct", {
|
|
518
|
+
enumerable: true,
|
|
519
|
+
get: function () { return mockup.mockupDataFromProduct; }
|
|
520
|
+
});
|
|
521
|
+
Object.defineProperty(exports, "mockupMonogram", {
|
|
522
|
+
enumerable: true,
|
|
523
|
+
get: function () { return mockup.mockupMonogram; }
|
|
524
|
+
});
|
|
525
|
+
Object.defineProperty(exports, "partMockupData", {
|
|
526
|
+
enumerable: true,
|
|
527
|
+
get: function () { return mockup.partMockupData; }
|
|
528
|
+
});
|
|
529
|
+
Object.defineProperty(exports, "resolveMockupKind", {
|
|
530
|
+
enumerable: true,
|
|
531
|
+
get: function () { return mockup.resolveMockupKind; }
|
|
532
|
+
});
|
|
533
|
+
Object.defineProperty(exports, "supplementMockupData", {
|
|
534
|
+
enumerable: true,
|
|
535
|
+
get: function () { return mockup.supplementMockupData; }
|
|
536
|
+
});
|
|
537
|
+
Object.defineProperty(exports, "tireMockupData", {
|
|
538
|
+
enumerable: true,
|
|
539
|
+
get: function () { return mockup.tireMockupData; }
|
|
540
|
+
});
|
|
541
|
+
exports.BUILTIN_MOCKUPS = BUILTIN_MOCKUPS;
|
|
542
|
+
exports.DEFAULT_MOCKUP_LABELS = DEFAULT_MOCKUP_LABELS;
|
|
543
|
+
exports.GenericMockup = GenericMockup;
|
|
544
|
+
exports.MOCKUP_MONO = MOCKUP_MONO;
|
|
545
|
+
exports.MOCKUP_SIZE = MOCKUP_SIZE;
|
|
546
|
+
exports.MOCKUP_TOKENS = MOCKUP_TOKENS;
|
|
547
|
+
exports.MockupFrame = MockupFrame;
|
|
548
|
+
exports.MockupProvider = MockupProvider;
|
|
549
|
+
exports.MockupText = MockupText;
|
|
550
|
+
exports.PartMockup = PartMockup;
|
|
551
|
+
exports.ProductImage = ProductImage;
|
|
552
|
+
exports.ProductMockup = ProductMockup;
|
|
553
|
+
exports.SupplementMockup = SupplementMockup;
|
|
554
|
+
exports.TireMockup = TireMockup;
|
|
555
|
+
exports.createMockupRegistry = createMockupRegistry;
|
|
556
|
+
exports.createStoreMockups = createStoreMockups;
|
|
557
|
+
exports.defaultMockupRegistry = defaultMockupRegistry;
|
|
558
|
+
exports.defineMockup = defineMockup;
|
|
559
|
+
exports.describeMockup = describeMockup;
|
|
560
|
+
exports.fitMockupFontSize = fitMockupFontSize;
|
|
561
|
+
exports.mockupWordMax = mockupWordMax;
|
|
562
|
+
exports.registerMockup = registerMockup;
|
|
563
|
+
exports.tone = tone;
|
|
564
|
+
exports.useMockupConfig = useMockupConfig;
|
|
565
|
+
exports.wrapMockupText = wrapMockupText;
|
|
566
|
+
//# sourceMappingURL=mockups.cjs.map
|
|
567
|
+
//# sourceMappingURL=mockups.cjs.map
|