@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.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
var storefront = require('@asteby/storefront');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var geo = require('@asteby/storefront/geo');
|
|
6
7
|
|
|
7
8
|
// src/context.tsx
|
|
8
9
|
var StorefrontContext = react.createContext(null);
|
|
@@ -552,8 +553,6 @@ function AddToCartButton({
|
|
|
552
553
|
}
|
|
553
554
|
);
|
|
554
555
|
}
|
|
555
|
-
|
|
556
|
-
// src/seo.ts
|
|
557
556
|
var clean = (s, max) => {
|
|
558
557
|
if (!s) return void 0;
|
|
559
558
|
const text = s.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -687,6 +686,16 @@ function organizationJsonLd(shop, url) {
|
|
|
687
686
|
contactPoint
|
|
688
687
|
});
|
|
689
688
|
}
|
|
689
|
+
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})?$/;
|
|
690
|
+
function openingHoursJsonLd(hours) {
|
|
691
|
+
if (!hours) return void 0;
|
|
692
|
+
if (typeof hours !== "string") {
|
|
693
|
+
const list = hours.weekly ? geo.toSchemaOpeningHours(hours) : [];
|
|
694
|
+
return list.length ? list : void 0;
|
|
695
|
+
}
|
|
696
|
+
const tokens = hours.split(",").map((t) => t.trim());
|
|
697
|
+
return tokens.length && tokens.every((t) => SCHEMA_HOURS.test(t)) ? tokens : void 0;
|
|
698
|
+
}
|
|
690
699
|
function localBusinessJsonLd(shop, options = {}) {
|
|
691
700
|
const site = shopUrl(shop, options.url);
|
|
692
701
|
return shop.branches.map(
|
|
@@ -700,8 +709,8 @@ function localBusinessJsonLd(shop, options = {}) {
|
|
|
700
709
|
telephone: b.phone ?? shop.contact.phone ?? void 0,
|
|
701
710
|
priceRange: options.priceRange,
|
|
702
711
|
address: compact({ "@type": "PostalAddress", streetAddress: b.address, addressLocality: b.city, addressCountry: shop.country_code }),
|
|
703
|
-
geo:
|
|
704
|
-
openingHours: b.
|
|
712
|
+
geo: geo.isValidPoint(b) ? { "@type": "GeoCoordinates", latitude: b.lat, longitude: b.lng } : void 0,
|
|
713
|
+
openingHours: openingHoursJsonLd(b.opening_hours ?? b.hours),
|
|
705
714
|
parentOrganization: site ? { "@id": `${site}#organization` } : { "@type": "Organization", name: shop.name }
|
|
706
715
|
})
|
|
707
716
|
);
|
|
@@ -770,7 +779,7 @@ function resolveSectionProps(schema, raw, path = "") {
|
|
|
770
779
|
for (const [name, spec] of Object.entries(schema)) {
|
|
771
780
|
const key = path ? `${path}.${name}` : name;
|
|
772
781
|
let value = input[name];
|
|
773
|
-
const
|
|
782
|
+
const fail2 = (message) => {
|
|
774
783
|
errors.push({ prop: key, message });
|
|
775
784
|
if ("default" in spec) props[name] = cloneDefault(spec.default);
|
|
776
785
|
};
|
|
@@ -781,37 +790,37 @@ function resolveSectionProps(schema, raw, path = "") {
|
|
|
781
790
|
}
|
|
782
791
|
if (STRING_KINDS.has(spec.kind)) {
|
|
783
792
|
if (typeof value !== "string") {
|
|
784
|
-
|
|
793
|
+
fail2("must be a string");
|
|
785
794
|
continue;
|
|
786
795
|
}
|
|
787
796
|
} else if (spec.kind === "number") {
|
|
788
797
|
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) value = Number(value);
|
|
789
798
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
790
|
-
|
|
799
|
+
fail2("must be a number");
|
|
791
800
|
continue;
|
|
792
801
|
}
|
|
793
802
|
if (spec.min !== void 0 && value < spec.min || spec.max !== void 0 && value > spec.max) {
|
|
794
|
-
|
|
803
|
+
fail2(`must be between ${spec.min ?? "-\u221E"} and ${spec.max ?? "\u221E"}`);
|
|
795
804
|
continue;
|
|
796
805
|
}
|
|
797
806
|
} else if (spec.kind === "boolean") {
|
|
798
807
|
if (typeof value !== "boolean") {
|
|
799
|
-
|
|
808
|
+
fail2("must be a boolean");
|
|
800
809
|
continue;
|
|
801
810
|
}
|
|
802
811
|
} else if (spec.kind === "select") {
|
|
803
812
|
if (typeof value !== "string" || spec.options && !spec.options.some((o) => o.value === value)) {
|
|
804
|
-
|
|
813
|
+
fail2(`must be one of ${(spec.options ?? []).map((o) => o.value).join(", ")}`);
|
|
805
814
|
continue;
|
|
806
815
|
}
|
|
807
816
|
} else if (spec.kind === "products") {
|
|
808
817
|
if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
|
|
809
|
-
|
|
818
|
+
fail2("must be a list of product handles");
|
|
810
819
|
continue;
|
|
811
820
|
}
|
|
812
821
|
} else if (spec.kind === "list") {
|
|
813
822
|
if (!Array.isArray(value)) {
|
|
814
|
-
|
|
823
|
+
fail2("must be a list");
|
|
815
824
|
continue;
|
|
816
825
|
}
|
|
817
826
|
const items = [];
|
|
@@ -982,15 +991,369 @@ function ThemeStyle({ theme, nonce, id = "storefront-theme", ...options }) {
|
|
|
982
991
|
if (!css) return null;
|
|
983
992
|
return /* @__PURE__ */ jsxRuntime.jsx("style", { id, nonce, "data-storefront-theme": "", dangerouslySetInnerHTML: { __html: css } });
|
|
984
993
|
}
|
|
994
|
+
var MESSAGES = {
|
|
995
|
+
unsupported: "Tu navegador no permite obtener tu ubicaci\xF3n.",
|
|
996
|
+
insecure: "La ubicaci\xF3n solo est\xE1 disponible en conexiones seguras (HTTPS).",
|
|
997
|
+
denied: "No tenemos permiso para usar tu ubicaci\xF3n. Puedes activarlo en el navegador o escribir tu c\xF3digo postal.",
|
|
998
|
+
unavailable: "No pudimos determinar tu ubicaci\xF3n. Escribe tu c\xF3digo postal.",
|
|
999
|
+
timeout: "Tard\xF3 demasiado en obtener tu ubicaci\xF3n. Intenta de nuevo o escribe tu c\xF3digo postal."
|
|
1000
|
+
};
|
|
1001
|
+
var fail = (code) => ({ code, message: MESSAGES[code] });
|
|
1002
|
+
var hasGeolocation = () => typeof navigator !== "undefined" && !!navigator.geolocation;
|
|
1003
|
+
function useGeolocation(options = {}) {
|
|
1004
|
+
const { enableHighAccuracy = false, timeoutMs = 1e4, maximumAge = 3e5 } = options;
|
|
1005
|
+
const [supported, setSupported] = react.useState(false);
|
|
1006
|
+
const [permission, setPermission] = react.useState("unknown");
|
|
1007
|
+
const [status, setStatus] = react.useState("idle");
|
|
1008
|
+
const [position, setPosition] = react.useState(null);
|
|
1009
|
+
const [error, setError] = react.useState(null);
|
|
1010
|
+
const alive = react.useRef(true);
|
|
1011
|
+
react.useEffect(() => {
|
|
1012
|
+
alive.current = true;
|
|
1013
|
+
setSupported(hasGeolocation());
|
|
1014
|
+
let off = () => {
|
|
1015
|
+
};
|
|
1016
|
+
try {
|
|
1017
|
+
const perms = typeof navigator !== "undefined" ? navigator.permissions : void 0;
|
|
1018
|
+
void perms?.query({ name: "geolocation" }).then((p) => {
|
|
1019
|
+
if (!alive.current) return;
|
|
1020
|
+
const sync = () => setPermission(p.state);
|
|
1021
|
+
sync();
|
|
1022
|
+
p.addEventListener?.("change", sync);
|
|
1023
|
+
off = () => p.removeEventListener?.("change", sync);
|
|
1024
|
+
}, () => {
|
|
1025
|
+
});
|
|
1026
|
+
} catch {
|
|
1027
|
+
}
|
|
1028
|
+
return () => {
|
|
1029
|
+
alive.current = false;
|
|
1030
|
+
off();
|
|
1031
|
+
};
|
|
1032
|
+
}, []);
|
|
1033
|
+
const request = react.useCallback(() => {
|
|
1034
|
+
const failWith = (code) => {
|
|
1035
|
+
if (alive.current) {
|
|
1036
|
+
setStatus("error");
|
|
1037
|
+
setError(fail(code));
|
|
1038
|
+
}
|
|
1039
|
+
return null;
|
|
1040
|
+
};
|
|
1041
|
+
if (!hasGeolocation()) return Promise.resolve(failWith("unsupported"));
|
|
1042
|
+
if (typeof window !== "undefined" && window.isSecureContext === false) return Promise.resolve(failWith("insecure"));
|
|
1043
|
+
setStatus("locating");
|
|
1044
|
+
setError(null);
|
|
1045
|
+
return new Promise((resolve) => {
|
|
1046
|
+
navigator.geolocation.getCurrentPosition(
|
|
1047
|
+
(pos) => {
|
|
1048
|
+
const point = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
|
1049
|
+
if (alive.current) {
|
|
1050
|
+
setPosition(point);
|
|
1051
|
+
setStatus("success");
|
|
1052
|
+
setPermission("granted");
|
|
1053
|
+
}
|
|
1054
|
+
resolve(point);
|
|
1055
|
+
},
|
|
1056
|
+
(err) => resolve(failWith(err.code === 1 ? "denied" : err.code === 3 ? "timeout" : "unavailable")),
|
|
1057
|
+
{ enableHighAccuracy, timeout: timeoutMs, maximumAge }
|
|
1058
|
+
);
|
|
1059
|
+
});
|
|
1060
|
+
}, [enableHighAccuracy, timeoutMs, maximumAge]);
|
|
1061
|
+
const reset = react.useCallback(() => {
|
|
1062
|
+
setStatus("idle");
|
|
1063
|
+
setPosition(null);
|
|
1064
|
+
setError(null);
|
|
1065
|
+
}, []);
|
|
1066
|
+
return { supported, permission, status, position, error, request, reset };
|
|
1067
|
+
}
|
|
1068
|
+
var EMPTY = [];
|
|
1069
|
+
var IDLE = { locations: [], status: "idle", error: null, source: null };
|
|
1070
|
+
function useNearestLocations(options = {}) {
|
|
1071
|
+
const { origin = null, postalCode = null, limit, radiusKm, remote = false } = options;
|
|
1072
|
+
const sf = useOptionalStorefront();
|
|
1073
|
+
const client = options.client ?? sf?.client ?? null;
|
|
1074
|
+
const branches = options.locations ?? sf?.shop?.branches ?? EMPTY;
|
|
1075
|
+
const lat = origin?.lat;
|
|
1076
|
+
const lng = origin?.lng;
|
|
1077
|
+
const hasOrigin = lat !== void 0 && lng !== void 0;
|
|
1078
|
+
const cp = postalCode?.trim() || null;
|
|
1079
|
+
const mode = react.useMemo(() => {
|
|
1080
|
+
if (cp) return client ? "remote" : null;
|
|
1081
|
+
if (!hasOrigin) return null;
|
|
1082
|
+
if (remote) return client ? "remote" : null;
|
|
1083
|
+
return branches.length ? "local" : null;
|
|
1084
|
+
}, [cp, hasOrigin, remote, client, branches.length]);
|
|
1085
|
+
const local = react.useMemo(() => {
|
|
1086
|
+
if (mode !== "local" || !hasOrigin) return [];
|
|
1087
|
+
return geo.rankByDistance(branches, { lat, lng }, { ...radiusKm !== void 0 ? { radiusKm } : {}, ...limit !== void 0 ? { limit } : {} }).map(({ item, distanceKm }) => ({ ...item, distance_km: distanceKm }));
|
|
1088
|
+
}, [mode, branches, lat, lng, radiusKm, limit, hasOrigin]);
|
|
1089
|
+
const [remoteState, setRemoteState] = react.useState(null);
|
|
1090
|
+
const key = mode === "remote" ? cp ? `cp:${cp}` : `ll:${lat},${lng}` : "";
|
|
1091
|
+
react.useEffect(() => {
|
|
1092
|
+
if (mode !== "remote" || !client) return;
|
|
1093
|
+
const ctrl = new AbortController();
|
|
1094
|
+
const req = cp ? client.locations.list({ postal_code: cp }, { signal: ctrl.signal }) : client.locations.nearest({ lat, lng }, { signal: ctrl.signal });
|
|
1095
|
+
req.then(
|
|
1096
|
+
(res) => !ctrl.signal.aborted && setRemoteState({ key, nodes: res.nodes }),
|
|
1097
|
+
(err) => !ctrl.signal.aborted && setRemoteState({ key, error: err instanceof Error ? err : new Error(String(err)) })
|
|
1098
|
+
);
|
|
1099
|
+
return () => ctrl.abort();
|
|
1100
|
+
}, [mode, client, key, cp, lat, lng]);
|
|
1101
|
+
return react.useMemo(() => {
|
|
1102
|
+
if (mode === "local") return { locations: local, status: "success", error: null, source: "local" };
|
|
1103
|
+
if (mode !== "remote") return IDLE;
|
|
1104
|
+
if (!remoteState || remoteState.key !== key) return { locations: [], status: "loading", error: null, source: "remote" };
|
|
1105
|
+
if ("error" in remoteState) return { locations: [], status: "error", error: remoteState.error, source: "remote" };
|
|
1106
|
+
let nodes = remoteState.nodes;
|
|
1107
|
+
if (radiusKm !== void 0) nodes = nodes.filter((n) => n.distance_km == null || n.distance_km <= radiusKm);
|
|
1108
|
+
if (limit !== void 0) nodes = nodes.slice(0, Math.max(0, limit));
|
|
1109
|
+
return { locations: nodes, status: "success", error: null, source: "remote" };
|
|
1110
|
+
}, [mode, local, remoteState, key, radiusKm, limit]);
|
|
1111
|
+
}
|
|
1112
|
+
var SELECTED_LOCATION_PARAM = "sucursal";
|
|
1113
|
+
var SELECTED_LOCATION_STORAGE_KEY = "sf_sucursal";
|
|
1114
|
+
var CHANGE_EVENT = "storefront:selected-location";
|
|
1115
|
+
var storage = () => {
|
|
1116
|
+
try {
|
|
1117
|
+
return typeof localStorage === "undefined" ? null : localStorage;
|
|
1118
|
+
} catch {
|
|
1119
|
+
return null;
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
function readSelectedLocationId({ search = typeof location === "undefined" ? "" : location.search, store = storage(), param = SELECTED_LOCATION_PARAM, storageKey = SELECTED_LOCATION_STORAGE_KEY } = {}) {
|
|
1123
|
+
const fromUrl = new URLSearchParams(search).get(param);
|
|
1124
|
+
if (fromUrl) return fromUrl;
|
|
1125
|
+
try {
|
|
1126
|
+
return store?.getItem(storageKey) || null;
|
|
1127
|
+
} catch {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
var EMPTY2 = [];
|
|
1132
|
+
function useSelectedLocation(options = {}) {
|
|
1133
|
+
const { initialId = null, param = SELECTED_LOCATION_PARAM, storageKey = SELECTED_LOCATION_STORAGE_KEY, syncUrl = true } = options;
|
|
1134
|
+
const sf = useOptionalStorefront();
|
|
1135
|
+
const locations = options.locations ?? sf?.shop?.branches ?? EMPTY2;
|
|
1136
|
+
const [raw, setRaw] = react.useState(initialId);
|
|
1137
|
+
const [ready, setReady] = react.useState(false);
|
|
1138
|
+
react.useEffect(() => {
|
|
1139
|
+
const read = () => setRaw(readSelectedLocationId({ param, storageKey }));
|
|
1140
|
+
read();
|
|
1141
|
+
setReady(true);
|
|
1142
|
+
window.addEventListener(CHANGE_EVENT, read);
|
|
1143
|
+
window.addEventListener("storage", read);
|
|
1144
|
+
window.addEventListener("popstate", read);
|
|
1145
|
+
return () => {
|
|
1146
|
+
window.removeEventListener(CHANGE_EVENT, read);
|
|
1147
|
+
window.removeEventListener("storage", read);
|
|
1148
|
+
window.removeEventListener("popstate", read);
|
|
1149
|
+
};
|
|
1150
|
+
}, [param, storageKey]);
|
|
1151
|
+
const location2 = react.useMemo(() => raw ? locations.find((l) => l.id === raw) ?? null : null, [raw, locations]);
|
|
1152
|
+
const id = location2?.id ?? null;
|
|
1153
|
+
react.useEffect(() => {
|
|
1154
|
+
if (!ready || !raw || !locations.length) return;
|
|
1155
|
+
try {
|
|
1156
|
+
if (location2) storage()?.setItem(storageKey, raw);
|
|
1157
|
+
else storage()?.removeItem(storageKey);
|
|
1158
|
+
} catch {
|
|
1159
|
+
}
|
|
1160
|
+
}, [ready, raw, locations, location2, storageKey]);
|
|
1161
|
+
const write = react.useCallback(
|
|
1162
|
+
(next) => {
|
|
1163
|
+
try {
|
|
1164
|
+
if (next) storage()?.setItem(storageKey, next);
|
|
1165
|
+
else storage()?.removeItem(storageKey);
|
|
1166
|
+
} catch {
|
|
1167
|
+
}
|
|
1168
|
+
if (syncUrl && typeof history !== "undefined") {
|
|
1169
|
+
const url = new URL(window.location.href);
|
|
1170
|
+
if (next) url.searchParams.set(param, next);
|
|
1171
|
+
else url.searchParams.delete(param);
|
|
1172
|
+
history.replaceState(history.state, "", url);
|
|
1173
|
+
}
|
|
1174
|
+
setRaw(next);
|
|
1175
|
+
window.dispatchEvent(new Event(CHANGE_EVENT));
|
|
1176
|
+
},
|
|
1177
|
+
[param, storageKey, syncUrl]
|
|
1178
|
+
);
|
|
1179
|
+
const select = react.useCallback((next) => write(next), [write]);
|
|
1180
|
+
const clear = react.useCallback(() => write(null), [write]);
|
|
1181
|
+
return { id, location: location2, ready, select, clear };
|
|
1182
|
+
}
|
|
1183
|
+
var EMPTY3 = [];
|
|
1184
|
+
function useLocationsOrShop(locations) {
|
|
1185
|
+
const sf = useOptionalStorefront();
|
|
1186
|
+
return locations ?? (sf?.shop?.branches ?? EMPTY3);
|
|
1187
|
+
}
|
|
1188
|
+
function LocationList({ locations, selectedId, children, ...rest }) {
|
|
1189
|
+
const list = useLocationsOrShop(locations);
|
|
1190
|
+
if (!list.length) return null;
|
|
1191
|
+
return /* @__PURE__ */ jsxRuntime.jsx("ul", { ...rest, children: list.map((loc, index) => {
|
|
1192
|
+
const selected = loc.id === selectedId;
|
|
1193
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { "data-selected": selected || void 0, "aria-current": selected || void 0, children: children ? children(loc, { index, selected }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1194
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: loc.name }),
|
|
1195
|
+
" ",
|
|
1196
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: loc.address })
|
|
1197
|
+
] }) }, loc.id);
|
|
1198
|
+
}) });
|
|
1199
|
+
}
|
|
1200
|
+
function LocationPicker({
|
|
1201
|
+
legend,
|
|
1202
|
+
locations,
|
|
1203
|
+
value,
|
|
1204
|
+
defaultValue = null,
|
|
1205
|
+
onChange,
|
|
1206
|
+
name = SELECTED_LOCATION_PARAM,
|
|
1207
|
+
children,
|
|
1208
|
+
...rest
|
|
1209
|
+
}) {
|
|
1210
|
+
const list = useLocationsOrShop(locations);
|
|
1211
|
+
const [inner, setInner] = react.useState(defaultValue);
|
|
1212
|
+
if (!list.length) return null;
|
|
1213
|
+
const current = value !== void 0 ? value : inner;
|
|
1214
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { ...rest, children: [
|
|
1215
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: legend }),
|
|
1216
|
+
list.map((loc) => {
|
|
1217
|
+
const checked = loc.id === current;
|
|
1218
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { "data-checked": checked || void 0, children: [
|
|
1219
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1220
|
+
"input",
|
|
1221
|
+
{
|
|
1222
|
+
type: "radio",
|
|
1223
|
+
name,
|
|
1224
|
+
value: loc.id,
|
|
1225
|
+
checked,
|
|
1226
|
+
onChange: () => {
|
|
1227
|
+
if (value === void 0) setInner(loc.id);
|
|
1228
|
+
onChange?.(loc.id, loc);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
),
|
|
1232
|
+
" ",
|
|
1233
|
+
children ? children(loc, { checked }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1234
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: loc.name }),
|
|
1235
|
+
" ",
|
|
1236
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: loc.address })
|
|
1237
|
+
] })
|
|
1238
|
+
] }, loc.id);
|
|
1239
|
+
})
|
|
1240
|
+
] });
|
|
1241
|
+
}
|
|
1242
|
+
var DEFAULT_TZ = "America/Mexico_City";
|
|
1243
|
+
function OpenStatus({ location: location2, hours, timezone, now, labels, children, ...rest }) {
|
|
1244
|
+
const h = hours ?? location2?.opening_hours;
|
|
1245
|
+
const tz = timezone ?? location2?.timezone ?? DEFAULT_TZ;
|
|
1246
|
+
const [tick, setTick] = react.useState(null);
|
|
1247
|
+
react.useEffect(() => {
|
|
1248
|
+
if (now) return;
|
|
1249
|
+
setTick(/* @__PURE__ */ new Date());
|
|
1250
|
+
const t = setInterval(() => setTick(/* @__PURE__ */ new Date()), 6e4);
|
|
1251
|
+
return () => clearInterval(t);
|
|
1252
|
+
}, [now]);
|
|
1253
|
+
const at = now ?? tick;
|
|
1254
|
+
if (!h || !at) return null;
|
|
1255
|
+
let status;
|
|
1256
|
+
try {
|
|
1257
|
+
status = geo.openStatus(h, tz, at);
|
|
1258
|
+
} catch {
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
const text = { open: "Abierto", closed: "Cerrado", ...labels };
|
|
1262
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-state": status.open ? "open" : "closed", ...rest, children: children ? children(status) : status.open ? text.open : text.closed });
|
|
1263
|
+
}
|
|
1264
|
+
function DirectionsLink({ location: location2, provider, children = "C\xF3mo llegar", ...rest }) {
|
|
1265
|
+
const href = geo.directionsUrl(location2, provider ? { provider } : {});
|
|
1266
|
+
if (!href) return null;
|
|
1267
|
+
return /* @__PURE__ */ jsxRuntime.jsx("a", { target: "_blank", rel: "noopener noreferrer", ...rest, href, children });
|
|
1268
|
+
}
|
|
1269
|
+
var readyText = (days) => days <= 0 ? "listo hoy" : days === 1 ? "listo en 1 d\xEDa" : `listo en ${days} d\xEDas`;
|
|
1270
|
+
function PickupOption({ location: location2, locale, children, ...rest }) {
|
|
1271
|
+
const sf = useOptionalStorefront();
|
|
1272
|
+
const p = location2.pickup;
|
|
1273
|
+
if (!p?.enabled) return null;
|
|
1274
|
+
const free = !p.fee || Number(p.fee.amount) === 0;
|
|
1275
|
+
const feeText = free ? null : storefront.formatMoney(p.fee, locale ?? sf?.locale);
|
|
1276
|
+
const info = { location: location2, fee: free ? null : p.fee, free, feeText, prepDays: p.prep_days };
|
|
1277
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { ...rest, children: children ? children(info) : `Recoge en sucursal \xB7 ${free ? "gratis" : feeText} \xB7 ${readyText(p.prep_days)}` });
|
|
1278
|
+
}
|
|
1279
|
+
function NearestLocationForm({
|
|
1280
|
+
locations,
|
|
1281
|
+
action,
|
|
1282
|
+
name = "cp",
|
|
1283
|
+
defaultValue,
|
|
1284
|
+
onSubmitPostalCode,
|
|
1285
|
+
onLocate,
|
|
1286
|
+
labels,
|
|
1287
|
+
children,
|
|
1288
|
+
...rest
|
|
1289
|
+
}) {
|
|
1290
|
+
const list = useLocationsOrShop(locations);
|
|
1291
|
+
const id = react.useId();
|
|
1292
|
+
const geo = useGeolocation();
|
|
1293
|
+
if (!list.length) return null;
|
|
1294
|
+
const text = { postalCode: "C\xF3digo postal", submit: "Buscar sucursales", locate: "Usar mi ubicaci\xF3n", locating: "Buscando\u2026", ...labels };
|
|
1295
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1296
|
+
"form",
|
|
1297
|
+
{
|
|
1298
|
+
method: "get",
|
|
1299
|
+
...action !== void 0 ? { action } : {},
|
|
1300
|
+
...rest,
|
|
1301
|
+
onSubmit: onSubmitPostalCode ? (e) => {
|
|
1302
|
+
e.preventDefault();
|
|
1303
|
+
const value = new FormData(e.currentTarget).get(name);
|
|
1304
|
+
onSubmitPostalCode(typeof value === "string" ? value.trim() : "", e);
|
|
1305
|
+
} : void 0,
|
|
1306
|
+
children: [
|
|
1307
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, children: text.postalCode }),
|
|
1308
|
+
" ",
|
|
1309
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1310
|
+
"input",
|
|
1311
|
+
{
|
|
1312
|
+
id,
|
|
1313
|
+
name,
|
|
1314
|
+
type: "text",
|
|
1315
|
+
inputMode: "numeric",
|
|
1316
|
+
autoComplete: "postal-code",
|
|
1317
|
+
pattern: "[0-9]{5}",
|
|
1318
|
+
maxLength: 5,
|
|
1319
|
+
required: true,
|
|
1320
|
+
...defaultValue !== void 0 ? { defaultValue } : {}
|
|
1321
|
+
}
|
|
1322
|
+
),
|
|
1323
|
+
" ",
|
|
1324
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", children: text.submit }),
|
|
1325
|
+
onLocate && geo.supported && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1326
|
+
"button",
|
|
1327
|
+
{
|
|
1328
|
+
type: "button",
|
|
1329
|
+
disabled: geo.status === "locating",
|
|
1330
|
+
onClick: () => void geo.request().then((p) => p && onLocate(p)),
|
|
1331
|
+
children: geo.status === "locating" ? text.locating : text.locate
|
|
1332
|
+
}
|
|
1333
|
+
),
|
|
1334
|
+
geo.error && /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", children: geo.error.message }),
|
|
1335
|
+
children
|
|
1336
|
+
]
|
|
1337
|
+
}
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
985
1340
|
|
|
986
1341
|
exports.AddToCartButton = AddToCartButton;
|
|
987
1342
|
exports.CART_COOKIE_NAME = CART_COOKIE_NAME;
|
|
988
1343
|
exports.CartProvider = CartProvider;
|
|
989
1344
|
exports.DEFAULT_IMAGE_WIDTHS = DEFAULT_IMAGE_WIDTHS;
|
|
1345
|
+
exports.DirectionsLink = DirectionsLink;
|
|
990
1346
|
exports.Image = Image;
|
|
991
1347
|
exports.JsonLd = JsonLd;
|
|
1348
|
+
exports.LocationList = LocationList;
|
|
1349
|
+
exports.LocationPicker = LocationPicker;
|
|
992
1350
|
exports.Money = Money;
|
|
1351
|
+
exports.NearestLocationForm = NearestLocationForm;
|
|
1352
|
+
exports.OpenStatus = OpenStatus;
|
|
1353
|
+
exports.PickupOption = PickupOption;
|
|
993
1354
|
exports.ProductPrice = ProductPrice;
|
|
1355
|
+
exports.SELECTED_LOCATION_PARAM = SELECTED_LOCATION_PARAM;
|
|
1356
|
+
exports.SELECTED_LOCATION_STORAGE_KEY = SELECTED_LOCATION_STORAGE_KEY;
|
|
994
1357
|
exports.SectionRegistry = SectionRegistry;
|
|
995
1358
|
exports.Sections = Sections;
|
|
996
1359
|
exports.StorefrontProvider = StorefrontProvider;
|
|
@@ -1006,11 +1369,13 @@ exports.faqJsonLd = faqJsonLd;
|
|
|
1006
1369
|
exports.getCartIdFromCookie = getCartIdFromCookie;
|
|
1007
1370
|
exports.isSafeCssValue = isSafeCssValue;
|
|
1008
1371
|
exports.localBusinessJsonLd = localBusinessJsonLd;
|
|
1372
|
+
exports.openingHoursJsonLd = openingHoursJsonLd;
|
|
1009
1373
|
exports.optimisticCart = optimisticCart;
|
|
1010
1374
|
exports.organizationJsonLd = organizationJsonLd;
|
|
1011
1375
|
exports.productJsonLd = productJsonLd;
|
|
1012
1376
|
exports.productMeta = productMeta;
|
|
1013
1377
|
exports.readCartCookie = readCartCookie;
|
|
1378
|
+
exports.readSelectedLocationId = readSelectedLocationId;
|
|
1014
1379
|
exports.resolveSectionProps = resolveSectionProps;
|
|
1015
1380
|
exports.sectionJsonSchema = sectionJsonSchema;
|
|
1016
1381
|
exports.seoMeta = seoMeta;
|
|
@@ -1022,9 +1387,12 @@ exports.themeToCssVariables = themeToCssVariables;
|
|
|
1022
1387
|
exports.toMerchandise = toMerchandise;
|
|
1023
1388
|
exports.tokenName = tokenName;
|
|
1024
1389
|
exports.useCart = useCart;
|
|
1390
|
+
exports.useGeolocation = useGeolocation;
|
|
1391
|
+
exports.useNearestLocations = useNearestLocations;
|
|
1025
1392
|
exports.useOptionalCart = useOptionalCart;
|
|
1026
1393
|
exports.useOptionalStorefront = useOptionalStorefront;
|
|
1027
1394
|
exports.useProductOptions = useProductOptions;
|
|
1395
|
+
exports.useSelectedLocation = useSelectedLocation;
|
|
1028
1396
|
exports.useStorefront = useStorefront;
|
|
1029
1397
|
exports.useStorefrontClient = useStorefrontClient;
|
|
1030
1398
|
exports.writeCartCookie = writeCartCookie;
|