@asteby/storefront-react 0.1.2 → 0.3.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 +171 -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.cjs +380 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +228 -54
- package/dist/index.d.ts +228 -54
- package/dist/index.js +373 -171
- package/dist/index.js.map +1 -1
- package/dist/mockups.cjs +525 -0
- package/dist/mockups.cjs.map +1 -0
- package/dist/mockups.d.cts +203 -0
- package/dist/mockups.d.ts +203 -0
- package/dist/mockups.js +401 -0
- package/dist/mockups.js.map +1 -0
- package/package.json +12 -2
package/dist/index.js
CHANGED
|
@@ -1,39 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function StorefrontProvider({ client, config, shop = null, locale, currency, children }) {
|
|
8
|
-
const configKey = config ? `${config.baseUrl}|${config.token}|${config.locale ?? ""}|${config.customerToken ?? ""}` : "";
|
|
9
|
-
const resolvedClient = useMemo(
|
|
10
|
-
() => client ?? (config ? createStorefrontClient(config) : null),
|
|
11
|
-
[client, configKey]
|
|
12
|
-
);
|
|
13
|
-
const value = useMemo(
|
|
14
|
-
() => ({
|
|
15
|
-
client: resolvedClient,
|
|
16
|
-
shop,
|
|
17
|
-
locale: locale ?? config?.locale ?? shop?.locale ?? "es-MX",
|
|
18
|
-
currency: currency ?? shop?.currency_code ?? "MXN"
|
|
19
|
-
}),
|
|
20
|
-
[resolvedClient, shop, locale, currency, config?.locale]
|
|
21
|
-
);
|
|
22
|
-
return /* @__PURE__ */ jsx(StorefrontContext.Provider, { value, children });
|
|
23
|
-
}
|
|
24
|
-
function useStorefront() {
|
|
25
|
-
const ctx = useContext(StorefrontContext);
|
|
26
|
-
if (!ctx) throw new Error("useStorefront must be used inside <StorefrontProvider>");
|
|
27
|
-
return ctx;
|
|
28
|
-
}
|
|
29
|
-
function useOptionalStorefront() {
|
|
30
|
-
return useContext(StorefrontContext);
|
|
31
|
-
}
|
|
32
|
-
function useStorefrontClient() {
|
|
33
|
-
const { client } = useStorefront();
|
|
34
|
-
if (!client) throw new Error("<StorefrontProvider> needs a `client` or `config` for browser-side calls");
|
|
35
|
-
return client;
|
|
36
|
-
}
|
|
1
|
+
import { useOptionalStorefront } from './chunk-C6QZNKRE.js';
|
|
2
|
+
export { DEFAULT_IMAGE_WIDTHS, Image, Money, ProductPrice, StorefrontProvider, defaultImageLoader, useOptionalStorefront, useStorefront, useStorefrontClient } from './chunk-C6QZNKRE.js';
|
|
3
|
+
import { createContext, useRef, useState, useCallback, useEffect, useMemo, useContext, Fragment as Fragment$1, useId } from 'react';
|
|
4
|
+
import { formatMoney, isStorefrontError } from '@asteby/storefront';
|
|
5
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
import { toSchemaOpeningHours, isValidPoint, rankByDistance, openStatus, directionsUrl } from '@asteby/storefront/geo';
|
|
37
7
|
|
|
38
8
|
// src/cookie.ts
|
|
39
9
|
var CART_COOKIE_NAME = "sf_cart";
|
|
@@ -313,128 +283,6 @@ function useCart() {
|
|
|
313
283
|
function useOptionalCart() {
|
|
314
284
|
return useContext(CartContext);
|
|
315
285
|
}
|
|
316
|
-
function Money({
|
|
317
|
-
data,
|
|
318
|
-
as,
|
|
319
|
-
locale,
|
|
320
|
-
withoutTrailingZeros,
|
|
321
|
-
withoutCurrency,
|
|
322
|
-
currencyDisplay,
|
|
323
|
-
...rest
|
|
324
|
-
}) {
|
|
325
|
-
const sf = useOptionalStorefront();
|
|
326
|
-
const text = formatMoney(data, locale ?? sf?.locale, {
|
|
327
|
-
...withoutTrailingZeros !== void 0 ? { withoutTrailingZeros } : {},
|
|
328
|
-
...withoutCurrency !== void 0 ? { withoutCurrency } : {},
|
|
329
|
-
...currencyDisplay !== void 0 ? { currencyDisplay } : {}
|
|
330
|
-
});
|
|
331
|
-
return createElement(as ?? "span", rest, text);
|
|
332
|
-
}
|
|
333
|
-
var DEFAULT_IMAGE_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600];
|
|
334
|
-
var defaultImageLoader = (src, width, height) => {
|
|
335
|
-
try {
|
|
336
|
-
const isAbsolute = /^[a-z][a-z0-9+.-]*:/i.test(src);
|
|
337
|
-
const url = new URL(src, "http://relative.invalid");
|
|
338
|
-
url.searchParams.set("width", String(width));
|
|
339
|
-
if (height) url.searchParams.set("height", String(height));
|
|
340
|
-
return isAbsolute ? url.toString() : url.pathname + url.search + url.hash;
|
|
341
|
-
} catch {
|
|
342
|
-
return src;
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
function parseRatio(ratio) {
|
|
346
|
-
if (!ratio) return null;
|
|
347
|
-
const [w, h] = ratio.split(/[/:]/).map((n) => Number.parseFloat(n));
|
|
348
|
-
return w && h ? w / h : null;
|
|
349
|
-
}
|
|
350
|
-
function Image({
|
|
351
|
-
data,
|
|
352
|
-
sizes = "100vw",
|
|
353
|
-
widths = DEFAULT_IMAGE_WIDTHS,
|
|
354
|
-
loader = defaultImageLoader,
|
|
355
|
-
priority = false,
|
|
356
|
-
aspectRatio,
|
|
357
|
-
alt,
|
|
358
|
-
loading,
|
|
359
|
-
decoding,
|
|
360
|
-
...rest
|
|
361
|
-
}) {
|
|
362
|
-
const ratio = parseRatio(aspectRatio) ?? (data.width && data.height ? data.width / data.height : null);
|
|
363
|
-
let candidates = [...widths].sort((a, b) => a - b);
|
|
364
|
-
if (data.width) {
|
|
365
|
-
const capped = candidates.filter((w) => w <= data.width);
|
|
366
|
-
candidates = capped.length ? capped : [data.width];
|
|
367
|
-
}
|
|
368
|
-
const srcSet = loader ? candidates.map((w) => `${loader(data.url, w, ratio ? Math.round(w / ratio) : void 0)} ${w}w`).join(", ") : void 0;
|
|
369
|
-
const width = data.width ?? candidates[candidates.length - 1];
|
|
370
|
-
const height = data.height ?? (ratio && width ? Math.round(width / ratio) : void 0);
|
|
371
|
-
return /* @__PURE__ */ jsx(
|
|
372
|
-
"img",
|
|
373
|
-
{
|
|
374
|
-
src: data.url,
|
|
375
|
-
alt: alt ?? data.alt ?? "",
|
|
376
|
-
...srcSet ? { srcSet, sizes } : {},
|
|
377
|
-
...width ? { width } : {},
|
|
378
|
-
...height ? { height } : {},
|
|
379
|
-
loading: loading ?? (priority ? "eager" : "lazy"),
|
|
380
|
-
decoding: decoding ?? (priority ? "auto" : "async"),
|
|
381
|
-
...priority ? { fetchPriority: "high" } : {},
|
|
382
|
-
...rest
|
|
383
|
-
}
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
var VISUALLY_HIDDEN = {
|
|
387
|
-
position: "absolute",
|
|
388
|
-
width: 1,
|
|
389
|
-
height: 1,
|
|
390
|
-
padding: 0,
|
|
391
|
-
margin: -1,
|
|
392
|
-
overflow: "hidden",
|
|
393
|
-
clip: "rect(0, 0, 0, 0)",
|
|
394
|
-
whiteSpace: "nowrap",
|
|
395
|
-
border: 0
|
|
396
|
-
};
|
|
397
|
-
function ProductPrice({
|
|
398
|
-
product,
|
|
399
|
-
variant,
|
|
400
|
-
fromLabel = "Desde",
|
|
401
|
-
compareAtLabel = "Precio anterior",
|
|
402
|
-
locale,
|
|
403
|
-
withoutTrailingZeros,
|
|
404
|
-
...rest
|
|
405
|
-
}) {
|
|
406
|
-
let price = null;
|
|
407
|
-
let compareAt = null;
|
|
408
|
-
let isRange = false;
|
|
409
|
-
if (variant) {
|
|
410
|
-
price = variant.price;
|
|
411
|
-
compareAt = variant.compare_at_price;
|
|
412
|
-
} else if (product) {
|
|
413
|
-
price = product.price_range.min;
|
|
414
|
-
compareAt = product.compare_at_price_range?.min ?? null;
|
|
415
|
-
isRange = product.price_range.min.amount !== product.price_range.max.amount;
|
|
416
|
-
}
|
|
417
|
-
if (!price) return null;
|
|
418
|
-
const onSale = compareAt !== null && parseMoney(compareAt) > parseMoney(price);
|
|
419
|
-
const opts = { ...locale ? { locale } : {}, ...withoutTrailingZeros !== void 0 ? { withoutTrailingZeros } : {} };
|
|
420
|
-
return /* @__PURE__ */ jsxs("div", { "data-on-sale": onSale || void 0, ...rest, children: [
|
|
421
|
-
isRange ? /* @__PURE__ */ jsxs("span", { "data-price-prefix": "", children: [
|
|
422
|
-
fromLabel,
|
|
423
|
-
" "
|
|
424
|
-
] }) : null,
|
|
425
|
-
/* @__PURE__ */ jsx(Money, { data: price, "data-price": "", ...opts }),
|
|
426
|
-
onSale && compareAt ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
427
|
-
" ",
|
|
428
|
-
/* @__PURE__ */ jsxs("s", { "data-compare-at-price": "", children: [
|
|
429
|
-
/* @__PURE__ */ jsxs("span", { style: VISUALLY_HIDDEN, children: [
|
|
430
|
-
compareAtLabel,
|
|
431
|
-
": "
|
|
432
|
-
] }),
|
|
433
|
-
/* @__PURE__ */ jsx(Money, { data: compareAt, ...opts })
|
|
434
|
-
] })
|
|
435
|
-
] }) : null
|
|
436
|
-
] });
|
|
437
|
-
}
|
|
438
286
|
function toParams(input) {
|
|
439
287
|
if (!input) return new URLSearchParams();
|
|
440
288
|
return input instanceof URLSearchParams ? new URLSearchParams(input) : new URLSearchParams(input);
|
|
@@ -550,8 +398,6 @@ function AddToCartButton({
|
|
|
550
398
|
}
|
|
551
399
|
);
|
|
552
400
|
}
|
|
553
|
-
|
|
554
|
-
// src/seo.ts
|
|
555
401
|
var clean = (s, max) => {
|
|
556
402
|
if (!s) return void 0;
|
|
557
403
|
const text = s.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -685,6 +531,16 @@ function organizationJsonLd(shop, url) {
|
|
|
685
531
|
contactPoint
|
|
686
532
|
});
|
|
687
533
|
}
|
|
534
|
+
var SCHEMA_HOURS = /^(Mo|Tu|We|Th|Fr|Sa|Su)(-(Mo|Tu|We|Th|Fr|Sa|Su))?( \d{2}:\d{2}-\d{2}:\d{2})?$/;
|
|
535
|
+
function openingHoursJsonLd(hours) {
|
|
536
|
+
if (!hours) return void 0;
|
|
537
|
+
if (typeof hours !== "string") {
|
|
538
|
+
const list = hours.weekly ? toSchemaOpeningHours(hours) : [];
|
|
539
|
+
return list.length ? list : void 0;
|
|
540
|
+
}
|
|
541
|
+
const tokens = hours.split(",").map((t) => t.trim());
|
|
542
|
+
return tokens.length && tokens.every((t) => SCHEMA_HOURS.test(t)) ? tokens : void 0;
|
|
543
|
+
}
|
|
688
544
|
function localBusinessJsonLd(shop, options = {}) {
|
|
689
545
|
const site = shopUrl(shop, options.url);
|
|
690
546
|
return shop.branches.map(
|
|
@@ -698,8 +554,8 @@ function localBusinessJsonLd(shop, options = {}) {
|
|
|
698
554
|
telephone: b.phone ?? shop.contact.phone ?? void 0,
|
|
699
555
|
priceRange: options.priceRange,
|
|
700
556
|
address: compact({ "@type": "PostalAddress", streetAddress: b.address, addressLocality: b.city, addressCountry: shop.country_code }),
|
|
701
|
-
geo: b
|
|
702
|
-
openingHours: b.
|
|
557
|
+
geo: isValidPoint(b) ? { "@type": "GeoCoordinates", latitude: b.lat, longitude: b.lng } : void 0,
|
|
558
|
+
openingHours: openingHoursJsonLd(b.opening_hours ?? b.hours),
|
|
703
559
|
parentOrganization: site ? { "@id": `${site}#organization` } : { "@type": "Organization", name: shop.name }
|
|
704
560
|
})
|
|
705
561
|
);
|
|
@@ -768,7 +624,7 @@ function resolveSectionProps(schema, raw, path = "") {
|
|
|
768
624
|
for (const [name, spec] of Object.entries(schema)) {
|
|
769
625
|
const key = path ? `${path}.${name}` : name;
|
|
770
626
|
let value = input[name];
|
|
771
|
-
const
|
|
627
|
+
const fail2 = (message) => {
|
|
772
628
|
errors.push({ prop: key, message });
|
|
773
629
|
if ("default" in spec) props[name] = cloneDefault(spec.default);
|
|
774
630
|
};
|
|
@@ -779,37 +635,37 @@ function resolveSectionProps(schema, raw, path = "") {
|
|
|
779
635
|
}
|
|
780
636
|
if (STRING_KINDS.has(spec.kind)) {
|
|
781
637
|
if (typeof value !== "string") {
|
|
782
|
-
|
|
638
|
+
fail2("must be a string");
|
|
783
639
|
continue;
|
|
784
640
|
}
|
|
785
641
|
} else if (spec.kind === "number") {
|
|
786
642
|
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) value = Number(value);
|
|
787
643
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
788
|
-
|
|
644
|
+
fail2("must be a number");
|
|
789
645
|
continue;
|
|
790
646
|
}
|
|
791
647
|
if (spec.min !== void 0 && value < spec.min || spec.max !== void 0 && value > spec.max) {
|
|
792
|
-
|
|
648
|
+
fail2(`must be between ${spec.min ?? "-\u221E"} and ${spec.max ?? "\u221E"}`);
|
|
793
649
|
continue;
|
|
794
650
|
}
|
|
795
651
|
} else if (spec.kind === "boolean") {
|
|
796
652
|
if (typeof value !== "boolean") {
|
|
797
|
-
|
|
653
|
+
fail2("must be a boolean");
|
|
798
654
|
continue;
|
|
799
655
|
}
|
|
800
656
|
} else if (spec.kind === "select") {
|
|
801
657
|
if (typeof value !== "string" || spec.options && !spec.options.some((o) => o.value === value)) {
|
|
802
|
-
|
|
658
|
+
fail2(`must be one of ${(spec.options ?? []).map((o) => o.value).join(", ")}`);
|
|
803
659
|
continue;
|
|
804
660
|
}
|
|
805
661
|
} else if (spec.kind === "products") {
|
|
806
662
|
if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
|
|
807
|
-
|
|
663
|
+
fail2("must be a list of product handles");
|
|
808
664
|
continue;
|
|
809
665
|
}
|
|
810
666
|
} else if (spec.kind === "list") {
|
|
811
667
|
if (!Array.isArray(value)) {
|
|
812
|
-
|
|
668
|
+
fail2("must be a list");
|
|
813
669
|
continue;
|
|
814
670
|
}
|
|
815
671
|
const items = [];
|
|
@@ -980,7 +836,353 @@ function ThemeStyle({ theme, nonce, id = "storefront-theme", ...options }) {
|
|
|
980
836
|
if (!css) return null;
|
|
981
837
|
return /* @__PURE__ */ jsx("style", { id, nonce, "data-storefront-theme": "", dangerouslySetInnerHTML: { __html: css } });
|
|
982
838
|
}
|
|
839
|
+
var MESSAGES = {
|
|
840
|
+
unsupported: "Tu navegador no permite obtener tu ubicaci\xF3n.",
|
|
841
|
+
insecure: "La ubicaci\xF3n solo est\xE1 disponible en conexiones seguras (HTTPS).",
|
|
842
|
+
denied: "No tenemos permiso para usar tu ubicaci\xF3n. Puedes activarlo en el navegador o escribir tu c\xF3digo postal.",
|
|
843
|
+
unavailable: "No pudimos determinar tu ubicaci\xF3n. Escribe tu c\xF3digo postal.",
|
|
844
|
+
timeout: "Tard\xF3 demasiado en obtener tu ubicaci\xF3n. Intenta de nuevo o escribe tu c\xF3digo postal."
|
|
845
|
+
};
|
|
846
|
+
var fail = (code) => ({ code, message: MESSAGES[code] });
|
|
847
|
+
var hasGeolocation = () => typeof navigator !== "undefined" && !!navigator.geolocation;
|
|
848
|
+
function useGeolocation(options = {}) {
|
|
849
|
+
const { enableHighAccuracy = false, timeoutMs = 1e4, maximumAge = 3e5 } = options;
|
|
850
|
+
const [supported, setSupported] = useState(false);
|
|
851
|
+
const [permission, setPermission] = useState("unknown");
|
|
852
|
+
const [status, setStatus] = useState("idle");
|
|
853
|
+
const [position, setPosition] = useState(null);
|
|
854
|
+
const [error, setError] = useState(null);
|
|
855
|
+
const alive = useRef(true);
|
|
856
|
+
useEffect(() => {
|
|
857
|
+
alive.current = true;
|
|
858
|
+
setSupported(hasGeolocation());
|
|
859
|
+
let off = () => {
|
|
860
|
+
};
|
|
861
|
+
try {
|
|
862
|
+
const perms = typeof navigator !== "undefined" ? navigator.permissions : void 0;
|
|
863
|
+
void perms?.query({ name: "geolocation" }).then((p) => {
|
|
864
|
+
if (!alive.current) return;
|
|
865
|
+
const sync = () => setPermission(p.state);
|
|
866
|
+
sync();
|
|
867
|
+
p.addEventListener?.("change", sync);
|
|
868
|
+
off = () => p.removeEventListener?.("change", sync);
|
|
869
|
+
}, () => {
|
|
870
|
+
});
|
|
871
|
+
} catch {
|
|
872
|
+
}
|
|
873
|
+
return () => {
|
|
874
|
+
alive.current = false;
|
|
875
|
+
off();
|
|
876
|
+
};
|
|
877
|
+
}, []);
|
|
878
|
+
const request = useCallback(() => {
|
|
879
|
+
const failWith = (code) => {
|
|
880
|
+
if (alive.current) {
|
|
881
|
+
setStatus("error");
|
|
882
|
+
setError(fail(code));
|
|
883
|
+
}
|
|
884
|
+
return null;
|
|
885
|
+
};
|
|
886
|
+
if (!hasGeolocation()) return Promise.resolve(failWith("unsupported"));
|
|
887
|
+
if (typeof window !== "undefined" && window.isSecureContext === false) return Promise.resolve(failWith("insecure"));
|
|
888
|
+
setStatus("locating");
|
|
889
|
+
setError(null);
|
|
890
|
+
return new Promise((resolve) => {
|
|
891
|
+
navigator.geolocation.getCurrentPosition(
|
|
892
|
+
(pos) => {
|
|
893
|
+
const point = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
|
894
|
+
if (alive.current) {
|
|
895
|
+
setPosition(point);
|
|
896
|
+
setStatus("success");
|
|
897
|
+
setPermission("granted");
|
|
898
|
+
}
|
|
899
|
+
resolve(point);
|
|
900
|
+
},
|
|
901
|
+
(err) => resolve(failWith(err.code === 1 ? "denied" : err.code === 3 ? "timeout" : "unavailable")),
|
|
902
|
+
{ enableHighAccuracy, timeout: timeoutMs, maximumAge }
|
|
903
|
+
);
|
|
904
|
+
});
|
|
905
|
+
}, [enableHighAccuracy, timeoutMs, maximumAge]);
|
|
906
|
+
const reset = useCallback(() => {
|
|
907
|
+
setStatus("idle");
|
|
908
|
+
setPosition(null);
|
|
909
|
+
setError(null);
|
|
910
|
+
}, []);
|
|
911
|
+
return { supported, permission, status, position, error, request, reset };
|
|
912
|
+
}
|
|
913
|
+
var EMPTY = [];
|
|
914
|
+
var IDLE = { locations: [], status: "idle", error: null, source: null };
|
|
915
|
+
function useNearestLocations(options = {}) {
|
|
916
|
+
const { origin = null, postalCode = null, limit, radiusKm, remote = false } = options;
|
|
917
|
+
const sf = useOptionalStorefront();
|
|
918
|
+
const client = options.client ?? sf?.client ?? null;
|
|
919
|
+
const branches = options.locations ?? sf?.shop?.branches ?? EMPTY;
|
|
920
|
+
const lat = origin?.lat;
|
|
921
|
+
const lng = origin?.lng;
|
|
922
|
+
const hasOrigin = lat !== void 0 && lng !== void 0;
|
|
923
|
+
const cp = postalCode?.trim() || null;
|
|
924
|
+
const mode = useMemo(() => {
|
|
925
|
+
if (cp) return client ? "remote" : null;
|
|
926
|
+
if (!hasOrigin) return null;
|
|
927
|
+
if (remote) return client ? "remote" : null;
|
|
928
|
+
return branches.length ? "local" : null;
|
|
929
|
+
}, [cp, hasOrigin, remote, client, branches.length]);
|
|
930
|
+
const local = useMemo(() => {
|
|
931
|
+
if (mode !== "local" || !hasOrigin) return [];
|
|
932
|
+
return rankByDistance(branches, { lat, lng }, { ...radiusKm !== void 0 ? { radiusKm } : {}, ...limit !== void 0 ? { limit } : {} }).map(({ item, distanceKm }) => ({ ...item, distance_km: distanceKm }));
|
|
933
|
+
}, [mode, branches, lat, lng, radiusKm, limit, hasOrigin]);
|
|
934
|
+
const [remoteState, setRemoteState] = useState(null);
|
|
935
|
+
const key = mode === "remote" ? cp ? `cp:${cp}` : `ll:${lat},${lng}` : "";
|
|
936
|
+
useEffect(() => {
|
|
937
|
+
if (mode !== "remote" || !client) return;
|
|
938
|
+
const ctrl = new AbortController();
|
|
939
|
+
const req = cp ? client.locations.list({ postal_code: cp }, { signal: ctrl.signal }) : client.locations.nearest({ lat, lng }, { signal: ctrl.signal });
|
|
940
|
+
req.then(
|
|
941
|
+
(res) => !ctrl.signal.aborted && setRemoteState({ key, nodes: res.nodes }),
|
|
942
|
+
(err) => !ctrl.signal.aborted && setRemoteState({ key, error: err instanceof Error ? err : new Error(String(err)) })
|
|
943
|
+
);
|
|
944
|
+
return () => ctrl.abort();
|
|
945
|
+
}, [mode, client, key, cp, lat, lng]);
|
|
946
|
+
return useMemo(() => {
|
|
947
|
+
if (mode === "local") return { locations: local, status: "success", error: null, source: "local" };
|
|
948
|
+
if (mode !== "remote") return IDLE;
|
|
949
|
+
if (!remoteState || remoteState.key !== key) return { locations: [], status: "loading", error: null, source: "remote" };
|
|
950
|
+
if ("error" in remoteState) return { locations: [], status: "error", error: remoteState.error, source: "remote" };
|
|
951
|
+
let nodes = remoteState.nodes;
|
|
952
|
+
if (radiusKm !== void 0) nodes = nodes.filter((n) => n.distance_km == null || n.distance_km <= radiusKm);
|
|
953
|
+
if (limit !== void 0) nodes = nodes.slice(0, Math.max(0, limit));
|
|
954
|
+
return { locations: nodes, status: "success", error: null, source: "remote" };
|
|
955
|
+
}, [mode, local, remoteState, key, radiusKm, limit]);
|
|
956
|
+
}
|
|
957
|
+
var SELECTED_LOCATION_PARAM = "sucursal";
|
|
958
|
+
var SELECTED_LOCATION_STORAGE_KEY = "sf_sucursal";
|
|
959
|
+
var CHANGE_EVENT = "storefront:selected-location";
|
|
960
|
+
var storage = () => {
|
|
961
|
+
try {
|
|
962
|
+
return typeof localStorage === "undefined" ? null : localStorage;
|
|
963
|
+
} catch {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
function readSelectedLocationId({ search = typeof location === "undefined" ? "" : location.search, store = storage(), param = SELECTED_LOCATION_PARAM, storageKey = SELECTED_LOCATION_STORAGE_KEY } = {}) {
|
|
968
|
+
const fromUrl = new URLSearchParams(search).get(param);
|
|
969
|
+
if (fromUrl) return fromUrl;
|
|
970
|
+
try {
|
|
971
|
+
return store?.getItem(storageKey) || null;
|
|
972
|
+
} catch {
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
var EMPTY2 = [];
|
|
977
|
+
function useSelectedLocation(options = {}) {
|
|
978
|
+
const { initialId = null, param = SELECTED_LOCATION_PARAM, storageKey = SELECTED_LOCATION_STORAGE_KEY, syncUrl = true } = options;
|
|
979
|
+
const sf = useOptionalStorefront();
|
|
980
|
+
const locations = options.locations ?? sf?.shop?.branches ?? EMPTY2;
|
|
981
|
+
const [raw, setRaw] = useState(initialId);
|
|
982
|
+
const [ready, setReady] = useState(false);
|
|
983
|
+
useEffect(() => {
|
|
984
|
+
const read = () => setRaw(readSelectedLocationId({ param, storageKey }));
|
|
985
|
+
read();
|
|
986
|
+
setReady(true);
|
|
987
|
+
window.addEventListener(CHANGE_EVENT, read);
|
|
988
|
+
window.addEventListener("storage", read);
|
|
989
|
+
window.addEventListener("popstate", read);
|
|
990
|
+
return () => {
|
|
991
|
+
window.removeEventListener(CHANGE_EVENT, read);
|
|
992
|
+
window.removeEventListener("storage", read);
|
|
993
|
+
window.removeEventListener("popstate", read);
|
|
994
|
+
};
|
|
995
|
+
}, [param, storageKey]);
|
|
996
|
+
const location2 = useMemo(() => raw ? locations.find((l) => l.id === raw) ?? null : null, [raw, locations]);
|
|
997
|
+
const id = location2?.id ?? null;
|
|
998
|
+
useEffect(() => {
|
|
999
|
+
if (!ready || !raw || !locations.length) return;
|
|
1000
|
+
try {
|
|
1001
|
+
if (location2) storage()?.setItem(storageKey, raw);
|
|
1002
|
+
else storage()?.removeItem(storageKey);
|
|
1003
|
+
} catch {
|
|
1004
|
+
}
|
|
1005
|
+
}, [ready, raw, locations, location2, storageKey]);
|
|
1006
|
+
const write = useCallback(
|
|
1007
|
+
(next) => {
|
|
1008
|
+
try {
|
|
1009
|
+
if (next) storage()?.setItem(storageKey, next);
|
|
1010
|
+
else storage()?.removeItem(storageKey);
|
|
1011
|
+
} catch {
|
|
1012
|
+
}
|
|
1013
|
+
if (syncUrl && typeof history !== "undefined") {
|
|
1014
|
+
const url = new URL(window.location.href);
|
|
1015
|
+
if (next) url.searchParams.set(param, next);
|
|
1016
|
+
else url.searchParams.delete(param);
|
|
1017
|
+
history.replaceState(history.state, "", url);
|
|
1018
|
+
}
|
|
1019
|
+
setRaw(next);
|
|
1020
|
+
window.dispatchEvent(new Event(CHANGE_EVENT));
|
|
1021
|
+
},
|
|
1022
|
+
[param, storageKey, syncUrl]
|
|
1023
|
+
);
|
|
1024
|
+
const select = useCallback((next) => write(next), [write]);
|
|
1025
|
+
const clear = useCallback(() => write(null), [write]);
|
|
1026
|
+
return { id, location: location2, ready, select, clear };
|
|
1027
|
+
}
|
|
1028
|
+
var EMPTY3 = [];
|
|
1029
|
+
function useLocationsOrShop(locations) {
|
|
1030
|
+
const sf = useOptionalStorefront();
|
|
1031
|
+
return locations ?? (sf?.shop?.branches ?? EMPTY3);
|
|
1032
|
+
}
|
|
1033
|
+
function LocationList({ locations, selectedId, children, ...rest }) {
|
|
1034
|
+
const list = useLocationsOrShop(locations);
|
|
1035
|
+
if (!list.length) return null;
|
|
1036
|
+
return /* @__PURE__ */ jsx("ul", { ...rest, children: list.map((loc, index) => {
|
|
1037
|
+
const selected = loc.id === selectedId;
|
|
1038
|
+
return /* @__PURE__ */ jsx("li", { "data-selected": selected || void 0, "aria-current": selected || void 0, children: children ? children(loc, { index, selected }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1039
|
+
/* @__PURE__ */ jsx("span", { children: loc.name }),
|
|
1040
|
+
" ",
|
|
1041
|
+
/* @__PURE__ */ jsx("span", { children: loc.address })
|
|
1042
|
+
] }) }, loc.id);
|
|
1043
|
+
}) });
|
|
1044
|
+
}
|
|
1045
|
+
function LocationPicker({
|
|
1046
|
+
legend,
|
|
1047
|
+
locations,
|
|
1048
|
+
value,
|
|
1049
|
+
defaultValue = null,
|
|
1050
|
+
onChange,
|
|
1051
|
+
name = SELECTED_LOCATION_PARAM,
|
|
1052
|
+
children,
|
|
1053
|
+
...rest
|
|
1054
|
+
}) {
|
|
1055
|
+
const list = useLocationsOrShop(locations);
|
|
1056
|
+
const [inner, setInner] = useState(defaultValue);
|
|
1057
|
+
if (!list.length) return null;
|
|
1058
|
+
const current = value !== void 0 ? value : inner;
|
|
1059
|
+
return /* @__PURE__ */ jsxs("fieldset", { ...rest, children: [
|
|
1060
|
+
/* @__PURE__ */ jsx("legend", { children: legend }),
|
|
1061
|
+
list.map((loc) => {
|
|
1062
|
+
const checked = loc.id === current;
|
|
1063
|
+
return /* @__PURE__ */ jsxs("label", { "data-checked": checked || void 0, children: [
|
|
1064
|
+
/* @__PURE__ */ jsx(
|
|
1065
|
+
"input",
|
|
1066
|
+
{
|
|
1067
|
+
type: "radio",
|
|
1068
|
+
name,
|
|
1069
|
+
value: loc.id,
|
|
1070
|
+
checked,
|
|
1071
|
+
onChange: () => {
|
|
1072
|
+
if (value === void 0) setInner(loc.id);
|
|
1073
|
+
onChange?.(loc.id, loc);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
),
|
|
1077
|
+
" ",
|
|
1078
|
+
children ? children(loc, { checked }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1079
|
+
/* @__PURE__ */ jsx("span", { children: loc.name }),
|
|
1080
|
+
" ",
|
|
1081
|
+
/* @__PURE__ */ jsx("span", { children: loc.address })
|
|
1082
|
+
] })
|
|
1083
|
+
] }, loc.id);
|
|
1084
|
+
})
|
|
1085
|
+
] });
|
|
1086
|
+
}
|
|
1087
|
+
var DEFAULT_TZ = "America/Mexico_City";
|
|
1088
|
+
function OpenStatus({ location: location2, hours, timezone, now, labels, children, ...rest }) {
|
|
1089
|
+
const h = hours ?? location2?.opening_hours;
|
|
1090
|
+
const tz = timezone ?? location2?.timezone ?? DEFAULT_TZ;
|
|
1091
|
+
const [tick, setTick] = useState(null);
|
|
1092
|
+
useEffect(() => {
|
|
1093
|
+
if (now) return;
|
|
1094
|
+
setTick(/* @__PURE__ */ new Date());
|
|
1095
|
+
const t = setInterval(() => setTick(/* @__PURE__ */ new Date()), 6e4);
|
|
1096
|
+
return () => clearInterval(t);
|
|
1097
|
+
}, [now]);
|
|
1098
|
+
const at = now ?? tick;
|
|
1099
|
+
if (!h || !at) return null;
|
|
1100
|
+
let status;
|
|
1101
|
+
try {
|
|
1102
|
+
status = openStatus(h, tz, at);
|
|
1103
|
+
} catch {
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
const text = { open: "Abierto", closed: "Cerrado", ...labels };
|
|
1107
|
+
return /* @__PURE__ */ jsx("span", { "data-state": status.open ? "open" : "closed", ...rest, children: children ? children(status) : status.open ? text.open : text.closed });
|
|
1108
|
+
}
|
|
1109
|
+
function DirectionsLink({ location: location2, provider, children = "C\xF3mo llegar", ...rest }) {
|
|
1110
|
+
const href = directionsUrl(location2, provider ? { provider } : {});
|
|
1111
|
+
if (!href) return null;
|
|
1112
|
+
return /* @__PURE__ */ jsx("a", { target: "_blank", rel: "noopener noreferrer", ...rest, href, children });
|
|
1113
|
+
}
|
|
1114
|
+
var readyText = (days) => days <= 0 ? "listo hoy" : days === 1 ? "listo en 1 d\xEDa" : `listo en ${days} d\xEDas`;
|
|
1115
|
+
function PickupOption({ location: location2, locale, children, ...rest }) {
|
|
1116
|
+
const sf = useOptionalStorefront();
|
|
1117
|
+
const p = location2.pickup;
|
|
1118
|
+
if (!p?.enabled) return null;
|
|
1119
|
+
const free = !p.fee || Number(p.fee.amount) === 0;
|
|
1120
|
+
const feeText = free ? null : formatMoney(p.fee, locale ?? sf?.locale);
|
|
1121
|
+
const info = { location: location2, fee: free ? null : p.fee, free, feeText, prepDays: p.prep_days };
|
|
1122
|
+
return /* @__PURE__ */ jsx("span", { ...rest, children: children ? children(info) : `Recoge en sucursal \xB7 ${free ? "gratis" : feeText} \xB7 ${readyText(p.prep_days)}` });
|
|
1123
|
+
}
|
|
1124
|
+
function NearestLocationForm({
|
|
1125
|
+
locations,
|
|
1126
|
+
action,
|
|
1127
|
+
name = "cp",
|
|
1128
|
+
defaultValue,
|
|
1129
|
+
onSubmitPostalCode,
|
|
1130
|
+
onLocate,
|
|
1131
|
+
labels,
|
|
1132
|
+
children,
|
|
1133
|
+
...rest
|
|
1134
|
+
}) {
|
|
1135
|
+
const list = useLocationsOrShop(locations);
|
|
1136
|
+
const id = useId();
|
|
1137
|
+
const geo = useGeolocation();
|
|
1138
|
+
if (!list.length) return null;
|
|
1139
|
+
const text = { postalCode: "C\xF3digo postal", submit: "Buscar sucursales", locate: "Usar mi ubicaci\xF3n", locating: "Buscando\u2026", ...labels };
|
|
1140
|
+
return /* @__PURE__ */ jsxs(
|
|
1141
|
+
"form",
|
|
1142
|
+
{
|
|
1143
|
+
method: "get",
|
|
1144
|
+
...action !== void 0 ? { action } : {},
|
|
1145
|
+
...rest,
|
|
1146
|
+
onSubmit: onSubmitPostalCode ? (e) => {
|
|
1147
|
+
e.preventDefault();
|
|
1148
|
+
const value = new FormData(e.currentTarget).get(name);
|
|
1149
|
+
onSubmitPostalCode(typeof value === "string" ? value.trim() : "", e);
|
|
1150
|
+
} : void 0,
|
|
1151
|
+
children: [
|
|
1152
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, children: text.postalCode }),
|
|
1153
|
+
" ",
|
|
1154
|
+
/* @__PURE__ */ jsx(
|
|
1155
|
+
"input",
|
|
1156
|
+
{
|
|
1157
|
+
id,
|
|
1158
|
+
name,
|
|
1159
|
+
type: "text",
|
|
1160
|
+
inputMode: "numeric",
|
|
1161
|
+
autoComplete: "postal-code",
|
|
1162
|
+
pattern: "[0-9]{5}",
|
|
1163
|
+
maxLength: 5,
|
|
1164
|
+
required: true,
|
|
1165
|
+
...defaultValue !== void 0 ? { defaultValue } : {}
|
|
1166
|
+
}
|
|
1167
|
+
),
|
|
1168
|
+
" ",
|
|
1169
|
+
/* @__PURE__ */ jsx("button", { type: "submit", children: text.submit }),
|
|
1170
|
+
onLocate && geo.supported && /* @__PURE__ */ jsx(
|
|
1171
|
+
"button",
|
|
1172
|
+
{
|
|
1173
|
+
type: "button",
|
|
1174
|
+
disabled: geo.status === "locating",
|
|
1175
|
+
onClick: () => void geo.request().then((p) => p && onLocate(p)),
|
|
1176
|
+
children: geo.status === "locating" ? text.locating : text.locate
|
|
1177
|
+
}
|
|
1178
|
+
),
|
|
1179
|
+
geo.error && /* @__PURE__ */ jsx("span", { role: "alert", children: geo.error.message }),
|
|
1180
|
+
children
|
|
1181
|
+
]
|
|
1182
|
+
}
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
983
1185
|
|
|
984
|
-
export { AddToCartButton, CART_COOKIE_NAME, CartProvider,
|
|
1186
|
+
export { AddToCartButton, CART_COOKIE_NAME, CartProvider, DirectionsLink, JsonLd, LocationList, LocationPicker, NearestLocationForm, OpenStatus, PickupOption, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, SectionRegistry, Sections, ThemeStyle, VariantSelector, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, seoMeta, serializeCartCookie, serializeJsonLd, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useCart, useGeolocation, useNearestLocations, useOptionalCart, useProductOptions, useSelectedLocation, writeCartCookie };
|
|
985
1187
|
//# sourceMappingURL=index.js.map
|
|
986
1188
|
//# sourceMappingURL=index.js.map
|