dune-react 0.0.16 → 0.0.19

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.
Files changed (30) hide show
  1. package/dist/components/puck-base/core/fields.d.ts +23 -0
  2. package/dist/components/puck-base/core/fields.js +24 -15
  3. package/dist/components/puck-base/core/styles.d.ts +1 -5
  4. package/dist/components/puck-base/core/styles.js +1 -5
  5. package/dist/components/puck-base/core/with-editable.js +2 -2
  6. package/dist/components/puck-base/editor-context.d.ts +2 -0
  7. package/dist/components/puck-base/fields/index.d.ts +1 -0
  8. package/dist/components/puck-base/fields/location-field.d.ts +44 -0
  9. package/dist/components/puck-base/fields/location-field.js +207 -0
  10. package/dist/components/puck-block/hero-sections/fullscreen-hero-1/fullscreen-hero.d.ts +3 -3
  11. package/dist/components/puck-block/hero-sections/fullscreen-hero-1/fullscreen-hero.js +57 -11
  12. package/dist/components/puck-block/hero-sections/fullscreen-hero-1/index.js +4 -4
  13. package/dist/components/puck-block/hero-sections/image-hero-1/image-hero.d.ts +3 -3
  14. package/dist/components/puck-block/hero-sections/image-hero-1/image-hero.js +4 -3
  15. package/dist/components/puck-block/hero-sections/image-hero-1/index.js +3 -10
  16. package/dist/components/puck-block/hero-sections/props.d.ts +1 -3
  17. package/dist/components/puck-block/hero-sections/video-hero-1/index.js +3 -3
  18. package/dist/components/puck-block/hero-sections/video-hero-1/video-hero.d.ts +4 -4
  19. package/dist/components/puck-block/hero-sections/video-hero-1/video-hero.js +32 -29
  20. package/dist/components/puck-block/location-sections/location-1/index.js +17 -22
  21. package/dist/components/puck-block/location-sections/location-1/location.d.ts +5 -7
  22. package/dist/components/puck-block/location-sections/location-1/location.js +15 -12
  23. package/dist/components/puck-block/location-sections/location-2/index.js +28 -24
  24. package/dist/components/puck-block/location-sections/location-2/location.d.ts +6 -8
  25. package/dist/components/puck-block/location-sections/location-2/location.js +18 -15
  26. package/dist/components/puck-block/location-sections/location-3/index.js +43 -20
  27. package/dist/components/puck-block/location-sections/location-3/location.d.ts +5 -6
  28. package/dist/components/puck-block/location-sections/location-3/location.js +96 -86
  29. package/dist/components/puck-block/location-sections/props.d.ts +9 -10
  30. package/package.json +3 -2
@@ -801,4 +801,27 @@ export declare const form: {
801
801
  };
802
802
  };
803
803
  export declare const formDefaults: CompoundFormProps;
804
+ /** 地图定位的核心数据:地址 + 坐标 */
805
+ export interface MapLocation {
806
+ /** 用户可读的完整地址(显示 & 搜索用) */
807
+ address: string;
808
+ /** 纬度 */
809
+ lat: number;
810
+ /** 经度 */
811
+ lng: number;
812
+ /** 地图缩放级别,默认 14 */
813
+ zoom?: number;
814
+ /** Google Place ID(可选,便于精确定位) */
815
+ placeId?: string;
816
+ }
817
+ /** 从 MapLocation 生成 Google Maps embed URL */
818
+ export declare function getMapEmbedUrl(loc: MapLocation): string;
819
+ /** 从 MapLocation 生成 Google Maps 导航 URL */
820
+ export declare function getDirectionsUrl(loc: MapLocation): string;
821
+ export declare const locationDefaults: MapLocation;
822
+ export declare const location: {
823
+ type: "custom";
824
+ label: string;
825
+ render: any;
826
+ };
804
827
  export * from "./styles";
@@ -1,7 +1,7 @@
1
1
  import { fieldTypes } from "../field.js";
2
2
  import { ActionField } from "../fields/action-field.js";
3
3
  import { IconPickerField } from "../icon-picker-field.js";
4
- import { backgroundColor, backgroundImage, bannerStylesDefaults, bannerStylesField, createStylesDefaults, createStylesField, heroStylesDefaults, padding, paddingDefaults, paddingLevel, sectionBaseStyleDefaults, sectionBaseStyleFields, sectionOverlay, sectionStyle } from "./styles.js";
4
+ import { LocationField } from "../fields/location-field.js";
5
5
  const formMethods = ["get", "post", "put", "patch", "delete"];
6
6
  const icon = {
7
7
  type: "custom",
@@ -241,42 +241,51 @@ const formDefaults = {
241
241
  icon: "move-right"
242
242
  }
243
243
  };
244
+ function getMapEmbedUrl(loc) {
245
+ const { lat, lng, zoom = 14 } = loc;
246
+ return `https://maps.google.com/maps?q=${lat},${lng}&z=${zoom}&output=embed`;
247
+ }
248
+ function getDirectionsUrl(loc) {
249
+ return `https://www.google.com/maps/dir/?api=1&destination=${loc.lat},${loc.lng}`;
250
+ }
251
+ const locationDefaults = {
252
+ address: "San Francisco, CA, USA",
253
+ lat: 37.7749,
254
+ lng: -122.4194,
255
+ zoom: 13
256
+ };
257
+ const location = {
258
+ type: "custom",
259
+ label: "Location",
260
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
261
+ render: LocationField
262
+ };
244
263
  export {
245
264
  action,
246
265
  actionDefaults,
247
- backgroundColor,
248
- backgroundImage,
249
266
  badge,
250
- bannerStylesDefaults,
251
- bannerStylesField,
252
267
  button,
253
268
  buttons,
254
269
  card,
255
270
  cards,
256
271
  contentFields,
257
272
  contentFieldsWithFeatures,
258
- createStylesDefaults,
259
- createStylesField,
260
273
  description,
261
274
  features,
262
275
  field,
263
276
  fieldDefaults,
264
277
  form,
265
278
  formDefaults,
279
+ getDirectionsUrl,
280
+ getMapEmbedUrl,
266
281
  getPlaceholderImageUrl,
267
282
  heading,
268
- heroStylesDefaults,
269
283
  icon,
270
284
  image,
271
285
  image16x9Placeholder,
272
286
  image1x1Placeholder,
273
287
  image9x16Placeholder,
274
288
  images,
275
- padding,
276
- paddingDefaults,
277
- paddingLevel,
278
- sectionBaseStyleDefaults,
279
- sectionBaseStyleFields,
280
- sectionOverlay,
281
- sectionStyle
289
+ location,
290
+ locationDefaults
282
291
  };
@@ -1394,11 +1394,7 @@ export declare const contactStylesDefaults: {
1394
1394
  readonly bottom: "medium";
1395
1395
  };
1396
1396
  };
1397
- export interface LocationSectionStyles extends SectionBaseStyles {
1398
- mapHeight?: number;
1399
- mapFilter?: string;
1400
- mapTintColor?: string;
1401
- }
1397
+ export type LocationSectionStyles = SectionBaseStyles;
1402
1398
  export declare const locationStylesField: {
1403
1399
  type: "object";
1404
1400
  label: string;
@@ -216,11 +216,7 @@ createStylesField({
216
216
  }
217
217
  });
218
218
  createStylesDefaults();
219
- createStylesField({
220
- mapHeight: { type: "number", label: "Map Height", min: 200, max: 800 },
221
- mapFilter: { type: "text", label: "Map Filter (CSS)" },
222
- mapTintColor: { type: "text", label: "Map Tint Color" }
223
- });
219
+ createStylesField();
224
220
  createStylesDefaults();
225
221
  createStylesField();
226
222
  createStylesDefaults();
@@ -70,7 +70,7 @@ function withEditable(Component, config) {
70
70
  if (isBlock) {
71
71
  return /* @__PURE__ */ jsxs("span", { ref: wrapperRef, className: "group relative block h-full w-full", children: [
72
72
  /* @__PURE__ */ jsx(Component, { ...props }),
73
- /* @__PURE__ */ jsx("span", { className: "absolute inset-0 z-40 flex items-center justify-center", children: /* @__PURE__ */ jsx(
73
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-0 z-60 flex items-center justify-center", children: /* @__PURE__ */ jsx(
74
74
  "span",
75
75
  {
76
76
  ref: portalRef,
@@ -84,7 +84,7 @@ function withEditable(Component, config) {
84
84
  }
85
85
  return /* @__PURE__ */ jsxs("span", { ref: wrapperRef, className: "group relative inline-flex", children: [
86
86
  /* @__PURE__ */ jsx(Component, { ...props }),
87
- /* @__PURE__ */ jsx("span", { className: "absolute inset-0 z-40 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100", children: /* @__PURE__ */ jsx(
87
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-0 z-60 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100", children: /* @__PURE__ */ jsx(
88
88
  "span",
89
89
  {
90
90
  onClick: handleEdit,
@@ -17,6 +17,8 @@ type EditorContextValue = {
17
17
  domain?: string;
18
18
  /** 站内可选页面列表,用于 url 字段快捷选择 */
19
19
  sitePages?: SitePage[];
20
+ /** Google Maps API Key,用于 LocationField 地址搜索 */
21
+ googleMapsApiKey?: string;
20
22
  };
21
23
  export declare const EditorContextProvider: import("react").Provider<EditorContextValue>;
22
24
  export declare const useEditorContext: () => EditorContextValue;
@@ -4,4 +4,5 @@ export { ColorField } from "./color-field";
4
4
  export { RadioToggleField } from "./radio-toggle-field";
5
5
  export { ObjectField } from "./object-field";
6
6
  export { VirtualizedSelectField, LARGE_SELECT_THRESHOLD, } from "./virtualized-select-field";
7
+ export { LocationField } from "./location-field";
7
8
  export { ActionField, ACTION_TYPE_FIELD_MAP, ACTION_TYPE_OPTIONS, PageActionFields, ExternalActionFields, EmailActionFields, PhoneActionFields, SectionActionFields, DownloadActionFields, type ActionTypeFieldProps, } from "./action-field";
@@ -0,0 +1,44 @@
1
+ import type { MapLocation } from "../core/fields";
2
+ interface GLatLng {
3
+ lat(): number;
4
+ lng(): number;
5
+ }
6
+ interface GGeometry {
7
+ location?: GLatLng;
8
+ }
9
+ interface GPlaceResult {
10
+ formatted_address?: string;
11
+ geometry?: GGeometry;
12
+ place_id?: string;
13
+ address_components?: unknown[];
14
+ }
15
+ interface GAutocomplete {
16
+ getPlace(): GPlaceResult;
17
+ addListener(event: string, handler: () => void): void;
18
+ }
19
+ interface GAutocompleteConstructor {
20
+ new (input: HTMLInputElement, opts?: Record<string, unknown>): GAutocomplete;
21
+ }
22
+ interface GGoogleMaps {
23
+ places: {
24
+ Autocomplete: GAutocompleteConstructor;
25
+ };
26
+ event: {
27
+ clearInstanceListeners(instance: unknown): void;
28
+ };
29
+ }
30
+ interface GGoogle {
31
+ maps: GGoogleMaps;
32
+ }
33
+ declare global {
34
+ interface Window {
35
+ google?: GGoogle;
36
+ }
37
+ }
38
+ export interface LocationFieldProps {
39
+ name: string;
40
+ value: MapLocation | undefined;
41
+ onChange: (value: MapLocation) => void;
42
+ }
43
+ export declare const LocationField: import("react").NamedExoticComponent<LocationFieldProps>;
44
+ export {};
@@ -0,0 +1,207 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { memo, useState, useRef, useEffect, useCallback } from "react";
4
+ import { Input } from "../../shadcn/input.js";
5
+ import { Label } from "../../shadcn/label.js";
6
+ import { Loader2, Search, X, MapPin } from "lucide-react";
7
+ import { useEditorContext } from "../editor-context.js";
8
+ let googleLoadPromise = null;
9
+ function loadGooglePlaces(apiKey) {
10
+ var _a, _b;
11
+ if (googleLoadPromise) return googleLoadPromise;
12
+ if (typeof window !== "undefined" && ((_b = (_a = window.google) == null ? void 0 : _a.maps) == null ? void 0 : _b.places)) {
13
+ return Promise.resolve();
14
+ }
15
+ googleLoadPromise = new Promise((resolve, reject) => {
16
+ const script = document.createElement("script");
17
+ script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`;
18
+ script.async = true;
19
+ script.onload = () => resolve();
20
+ script.onerror = () => {
21
+ googleLoadPromise = null;
22
+ reject(new Error("Failed to load Google Maps API"));
23
+ };
24
+ document.head.appendChild(script);
25
+ });
26
+ return googleLoadPromise;
27
+ }
28
+ const LocationField = memo(function LocationField2({
29
+ value,
30
+ onChange
31
+ }) {
32
+ const { googleMapsApiKey } = useEditorContext();
33
+ const apiKey = googleMapsApiKey || "";
34
+ const [inputValue, setInputValue] = useState((value == null ? void 0 : value.address) || "");
35
+ const [isLoaded, setIsLoaded] = useState(false);
36
+ const [loadError, setLoadError] = useState(false);
37
+ const inputRef = useRef(null);
38
+ const autocompleteRef = useRef(null);
39
+ useEffect(() => {
40
+ if ((value == null ? void 0 : value.address) && value.address !== inputValue) {
41
+ setInputValue(value.address);
42
+ }
43
+ }, [value == null ? void 0 : value.address]);
44
+ useEffect(() => {
45
+ if (!apiKey) {
46
+ setLoadError(true);
47
+ return;
48
+ }
49
+ loadGooglePlaces(apiKey).then(() => setIsLoaded(true)).catch(() => setLoadError(true));
50
+ }, [apiKey]);
51
+ useEffect(() => {
52
+ if (!isLoaded || !inputRef.current || autocompleteRef.current) return;
53
+ const G = window.google;
54
+ const autocomplete = new G.maps.places.Autocomplete(inputRef.current, {
55
+ fields: [
56
+ "formatted_address",
57
+ "geometry",
58
+ "place_id",
59
+ "address_components"
60
+ ]
61
+ });
62
+ autocomplete.addListener("place_changed", () => {
63
+ var _a;
64
+ const place = autocomplete.getPlace();
65
+ if (!((_a = place.geometry) == null ? void 0 : _a.location)) return;
66
+ const lat = place.geometry.location.lat();
67
+ const lng = place.geometry.location.lng();
68
+ const address = place.formatted_address || "";
69
+ const newLocation = {
70
+ address,
71
+ lat,
72
+ lng,
73
+ zoom: (value == null ? void 0 : value.zoom) ?? 14,
74
+ placeId: place.place_id || ""
75
+ };
76
+ setInputValue(address);
77
+ onChange(newLocation);
78
+ });
79
+ autocompleteRef.current = autocomplete;
80
+ return () => {
81
+ var _a;
82
+ (_a = window.google) == null ? void 0 : _a.maps.event.clearInstanceListeners(autocomplete);
83
+ autocompleteRef.current = null;
84
+ };
85
+ }, [isLoaded]);
86
+ const handleClear = useCallback(() => {
87
+ var _a;
88
+ setInputValue("");
89
+ onChange({
90
+ address: "",
91
+ lat: 0,
92
+ lng: 0,
93
+ zoom: 14,
94
+ placeId: ""
95
+ });
96
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
97
+ }, [onChange]);
98
+ const handleZoomChange = useCallback(
99
+ (e) => {
100
+ if (!value) return;
101
+ onChange({ ...value, zoom: Number(e.target.value) });
102
+ },
103
+ [value, onChange]
104
+ );
105
+ const embedUrl = value && value.lat !== 0 ? `https://maps.google.com/maps?q=${value.lat},${value.lng}&z=${value.zoom ?? 14}&output=embed` : null;
106
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-3", children: [
107
+ /* @__PURE__ */ jsx(Label, { children: "Location" }),
108
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
109
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3", children: !isLoaded && !loadError ? /* @__PURE__ */ jsx(Loader2, { className: "text-muted-foreground size-4 animate-spin" }) : /* @__PURE__ */ jsx(Search, { className: "text-muted-foreground size-4" }) }),
110
+ /* @__PURE__ */ jsx(
111
+ Input,
112
+ {
113
+ ref: inputRef,
114
+ value: inputValue,
115
+ onChange: (e) => setInputValue(e.target.value),
116
+ placeholder: loadError ? "Enter address manually..." : "Search address or place...",
117
+ className: "pl-9 pr-8"
118
+ }
119
+ ),
120
+ inputValue && /* @__PURE__ */ jsx(
121
+ "button",
122
+ {
123
+ type: "button",
124
+ onClick: handleClear,
125
+ className: "text-muted-foreground hover:text-foreground absolute inset-y-0 right-0 flex items-center pr-3",
126
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
127
+ }
128
+ )
129
+ ] }),
130
+ loadError && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
131
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
132
+ /* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Latitude" }),
133
+ /* @__PURE__ */ jsx(
134
+ Input,
135
+ {
136
+ type: "number",
137
+ step: "any",
138
+ value: (value == null ? void 0 : value.lat) ?? "",
139
+ onChange: (e) => onChange({
140
+ ...value || { address: "", lng: 0, zoom: 14 },
141
+ lat: Number(e.target.value)
142
+ }),
143
+ placeholder: "37.7749",
144
+ className: "h-8 text-xs"
145
+ }
146
+ )
147
+ ] }),
148
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
149
+ /* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Longitude" }),
150
+ /* @__PURE__ */ jsx(
151
+ Input,
152
+ {
153
+ type: "number",
154
+ step: "any",
155
+ value: (value == null ? void 0 : value.lng) ?? "",
156
+ onChange: (e) => onChange({
157
+ ...value || { address: "", lat: 0, zoom: 14 },
158
+ lng: Number(e.target.value)
159
+ }),
160
+ placeholder: "-122.4194",
161
+ className: "h-8 text-xs"
162
+ }
163
+ )
164
+ ] })
165
+ ] }),
166
+ value && value.lat !== 0 && /* @__PURE__ */ jsxs("div", { className: "border-border bg-muted/50 space-y-2 rounded-lg border p-3", children: [
167
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
168
+ /* @__PURE__ */ jsx(MapPin, { className: "text-primary mt-0.5 size-3.5 shrink-0" }),
169
+ /* @__PURE__ */ jsx("span", { className: "text-foreground text-sm font-medium leading-tight", children: value.address })
170
+ ] }),
171
+ /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground pl-5.5 text-xs", children: [
172
+ value.lat.toFixed(4),
173
+ ", ",
174
+ value.lng.toFixed(4)
175
+ ] })
176
+ ] }),
177
+ embedUrl && /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsx(
178
+ "iframe",
179
+ {
180
+ src: embedUrl,
181
+ title: "Location preview",
182
+ className: "h-[160px] w-full border-0",
183
+ loading: "lazy",
184
+ referrerPolicy: "no-referrer-when-downgrade"
185
+ }
186
+ ) }),
187
+ value && value.lat !== 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
188
+ /* @__PURE__ */ jsx(Label, { className: "shrink-0 text-xs", children: "Zoom" }),
189
+ /* @__PURE__ */ jsx(
190
+ "input",
191
+ {
192
+ type: "range",
193
+ min: 1,
194
+ max: 20,
195
+ step: 1,
196
+ value: value.zoom ?? 14,
197
+ onChange: handleZoomChange,
198
+ className: "h-1.5 flex-1 cursor-pointer appearance-none rounded-full bg-gray-200 accent-current"
199
+ }
200
+ ),
201
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground w-6 text-right text-xs tabular-nums", children: value.zoom ?? 14 })
202
+ ] })
203
+ ] });
204
+ });
205
+ export {
206
+ LocationField
207
+ };
@@ -9,10 +9,10 @@ export interface FullscreenHeroProps {
9
9
  variant: string;
10
10
  };
11
11
  buttons?: CompoundButtonProps[];
12
- backgroundImage?: {
12
+ images?: {
13
13
  src: string;
14
14
  alt?: string;
15
- } | string;
15
+ }[];
16
16
  styles?: {
17
17
  padding?: CompoundContainerProps["padding"];
18
18
  sectionStyle?: CompoundContainerProps["sectionStyle"];
@@ -20,4 +20,4 @@ export interface FullscreenHeroProps {
20
20
  overlay?: "none" | "dark" | "gradient";
21
21
  };
22
22
  }
23
- export declare const FullscreenHero: ({ heading, description, badge, buttons, backgroundImage, styles, }: FullscreenHeroProps) => import("react/jsx-runtime").JSX.Element;
23
+ export declare const FullscreenHero: ({ heading, description, badge, buttons, images, styles, }: FullscreenHeroProps) => import("react/jsx-runtime").JSX.Element;
@@ -12,15 +12,16 @@ const FullscreenHero = ({
12
12
  description,
13
13
  badge,
14
14
  buttons,
15
- backgroundImage,
15
+ images,
16
16
  styles
17
17
  }) => {
18
18
  const sectionStyle = styles == null ? void 0 : styles.sectionStyle;
19
19
  const overlay = (styles == null ? void 0 : styles.overlay) ?? "dark";
20
20
  const backgroundColor = styles == null ? void 0 : styles.backgroundColor;
21
21
  const padding = styles == null ? void 0 : styles.padding;
22
- const bgSrc = typeof backgroundImage === "string" ? backgroundImage : backgroundImage == null ? void 0 : backgroundImage.src;
23
- const bgAlt = typeof backgroundImage === "string" ? "" : (backgroundImage == null ? void 0 : backgroundImage.alt) ?? "";
22
+ const bgImage = images == null ? void 0 : images[0];
23
+ const bgSrc = bgImage == null ? void 0 : bgImage.src;
24
+ const bgAlt = (bgImage == null ? void 0 : bgImage.alt) ?? "";
24
25
  if (!bgSrc) {
25
26
  return /* @__PURE__ */ jsx(
26
27
  CompoundContainer,
@@ -29,7 +30,15 @@ const FullscreenHero = ({
29
30
  sectionStyle,
30
31
  backgroundColor,
31
32
  className: "min-h-[100vh] flex flex-col justify-end",
32
- children: /* @__PURE__ */ jsx(Content, { badge, heading, description, buttons })
33
+ children: /* @__PURE__ */ jsx(
34
+ Content,
35
+ {
36
+ badge,
37
+ heading,
38
+ description,
39
+ buttons
40
+ }
41
+ )
33
42
  }
34
43
  );
35
44
  }
@@ -40,20 +49,57 @@ const FullscreenHero = ({
40
49
  {
41
50
  className: cn(
42
51
  "relative w-full min-h-[100vh] flex flex-col justify-end overflow-hidden bg-background text-foreground",
43
- { "pt-20 lg:pt-40": top === "large", "pb-20 lg:pb-40": bottom === "large" },
44
- { "pt-15 lg:pt-30": top === "medium", "pb-15 lg:pb-30": bottom === "medium" },
45
- { "pt-10 lg:pt-20": top === "small", "pb-10 lg:pb-20": bottom === "small" }
52
+ {
53
+ "pt-20 lg:pt-40": top === "large",
54
+ "pb-20 lg:pb-40": bottom === "large"
55
+ },
56
+ {
57
+ "pt-15 lg:pt-30": top === "medium",
58
+ "pb-15 lg:pb-30": bottom === "medium"
59
+ },
60
+ {
61
+ "pt-10 lg:pt-20": top === "small",
62
+ "pb-10 lg:pb-20": bottom === "small"
63
+ }
46
64
  ),
47
65
  style: { ...styleVars, ...backgroundColor ? { backgroundColor } : {} },
48
66
  children: [
49
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 [&>span]:h-full [&>span]:w-full", children: /* @__PURE__ */ jsx(CompoundImage, { src: bgSrc, alt: bgAlt, className: "h-full w-full object-cover" }) }),
50
- overlay !== "none" && /* @__PURE__ */ jsx("div", { className: cn("absolute inset-0", overlayStyles[overlay] ?? overlayStyles.dark) }),
51
- /* @__PURE__ */ jsx("div", { className: "container mx-auto relative z-50", children: /* @__PURE__ */ jsx(Content, { badge, heading, description, buttons }) })
67
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 [&>span]:h-full [&>span]:w-full", children: /* @__PURE__ */ jsx(
68
+ CompoundImage,
69
+ {
70
+ src: bgSrc,
71
+ alt: bgAlt,
72
+ className: "h-full w-full object-cover"
73
+ }
74
+ ) }),
75
+ overlay !== "none" && /* @__PURE__ */ jsx(
76
+ "div",
77
+ {
78
+ className: cn(
79
+ "absolute inset-0",
80
+ overlayStyles[overlay] ?? overlayStyles.dark
81
+ )
82
+ }
83
+ ),
84
+ /* @__PURE__ */ jsx("div", { className: "container mx-auto relative z-50", children: /* @__PURE__ */ jsx(
85
+ Content,
86
+ {
87
+ badge,
88
+ heading,
89
+ description,
90
+ buttons
91
+ }
92
+ ) })
52
93
  ]
53
94
  }
54
95
  );
55
96
  };
56
- function Content({ badge, heading, description, buttons }) {
97
+ function Content({
98
+ badge,
99
+ heading,
100
+ description,
101
+ buttons
102
+ }) {
57
103
  return /* @__PURE__ */ jsxs(Fragment, { children: [
58
104
  badge && /* @__PURE__ */ jsx("span", { className: "bg-primary/10 text-primary mb-6 inline-block rounded-full px-3 py-1 text-xs font-medium", children: badge.label }),
59
105
  /* @__PURE__ */ jsx("h1", { className: "font-serif text-5xl md:text-7xl lg:text-8xl font-medium leading-[0.95] mb-8 max-w-[800px] text-foreground", children: heading }),
@@ -1,6 +1,6 @@
1
- import { contentFields } from "../../../puck-base/core/fields.js";
1
+ import { images, contentFields } from "../../../puck-base/core/fields.js";
2
2
  import { FullscreenHero } from "./fullscreen-hero.js";
3
- import { backgroundImage, createStylesDefaults, createStylesField } from "../../../puck-base/core/styles.js";
3
+ import { createStylesDefaults, createStylesField } from "../../../puck-base/core/styles.js";
4
4
  const conf = {
5
5
  fields: {
6
6
  badge: contentFields.badge,
@@ -13,7 +13,7 @@ const conf = {
13
13
  contentEditable: true
14
14
  },
15
15
  buttons: contentFields.buttons,
16
- backgroundImage,
16
+ images: { ...images, max: 1 },
17
17
  styles: createStylesField({
18
18
  overlay: {
19
19
  type: "select",
@@ -37,7 +37,7 @@ const conf = {
37
37
  icon: "none"
38
38
  }
39
39
  ],
40
- backgroundImage: "https://images.pexels.com/photos/924824/pexels-photo-924824.jpeg?auto=compress&cs=tinysrgb&w=1920",
40
+ images: [{ src: "https://images.pexels.com/photos/924824/pexels-photo-924824.jpeg?auto=compress&cs=tinysrgb&w=1920", alt: "Hero background" }],
41
41
  styles: createStylesDefaults({
42
42
  sectionStyle: "dark",
43
43
  overlay: "dark"
@@ -6,10 +6,10 @@ export interface ImageHeroProps {
6
6
  description?: string;
7
7
  badge?: CompoundBadgeProps;
8
8
  buttons?: CompoundButtonProps[];
9
- backgroundImage?: {
9
+ images?: {
10
10
  src: string;
11
11
  alt?: string;
12
- } | string;
12
+ }[];
13
13
  styles?: {
14
14
  padding?: CompoundContainerProps["padding"];
15
15
  sectionStyle?: CompoundContainerProps["sectionStyle"];
@@ -19,4 +19,4 @@ export interface ImageHeroProps {
19
19
  minHeight?: "medium" | "large" | "full";
20
20
  };
21
21
  }
22
- export declare const ImageHero: ({ heading, description, badge, buttons, backgroundImage, styles, }: ImageHeroProps) => import("react/jsx-runtime").JSX.Element;
22
+ export declare const ImageHero: ({ heading, description, badge, buttons, images, styles, }: ImageHeroProps) => import("react/jsx-runtime").JSX.Element;
@@ -18,14 +18,15 @@ const ImageHero = ({
18
18
  description,
19
19
  badge,
20
20
  buttons,
21
- backgroundImage,
21
+ images,
22
22
  styles
23
23
  }) => {
24
24
  const overlay = (styles == null ? void 0 : styles.overlay) ?? "dark";
25
25
  const align = (styles == null ? void 0 : styles.align) ?? "center";
26
26
  const minHeight = (styles == null ? void 0 : styles.minHeight) ?? "large";
27
- const bgSrc = typeof backgroundImage === "string" ? backgroundImage : backgroundImage == null ? void 0 : backgroundImage.src;
28
- const bgAlt = typeof backgroundImage === "string" ? "" : (backgroundImage == null ? void 0 : backgroundImage.alt) ?? "";
27
+ const bgImage = images == null ? void 0 : images[0];
28
+ const bgSrc = bgImage == null ? void 0 : bgImage.src;
29
+ const bgAlt = (bgImage == null ? void 0 : bgImage.alt) ?? "";
29
30
  return /* @__PURE__ */ jsx(
30
31
  CompoundContainer,
31
32
  {
@@ -1,4 +1,4 @@
1
- import { buttons, badge } from "../../../puck-base/core/fields.js";
1
+ import { images, buttons, badge } from "../../../puck-base/core/fields.js";
2
2
  import { heroStylesDefaults, createStylesField, sectionOverlay } from "../../../puck-base/core/styles.js";
3
3
  import { ImageHero } from "./image-hero.js";
4
4
  const imageHeroStylesField = createStylesField({
@@ -26,20 +26,13 @@ const conf = {
26
26
  description: { type: "textarea", contentEditable: true },
27
27
  badge,
28
28
  buttons,
29
- backgroundImage: {
30
- type: "text",
31
- label: "Background Image URL",
32
- ai: {
33
- instructions: "URL for a full-width background image behind the section.",
34
- stream: false
35
- }
36
- },
29
+ images: { ...images, max: 1 },
37
30
  styles: imageHeroStylesField
38
31
  },
39
32
  defaultProps: {
40
33
  heading: "Build something people want",
41
34
  description: "A modern platform for teams who care about speed, quality, and customer experience.",
42
- backgroundImage: "https://images.pexels.com/photos/3184291/pexels-photo-3184291.jpeg?auto=compress&cs=tinysrgb&w=1920",
35
+ images: [{ src: "https://images.pexels.com/photos/3184291/pexels-photo-3184291.jpeg?auto=compress&cs=tinysrgb&w=1920", alt: "Hero background" }],
43
36
  buttons: [
44
37
  {
45
38
  label: "Get Started",
@@ -37,9 +37,7 @@ type HeroSectionProps = {
37
37
  spacing?: "tight" | "normal" | "relaxed";
38
38
  align?: "left" | "center";
39
39
  displaySize?: "default" | "display";
40
- images?: HeroImage | HeroImage[];
41
- backgroundImage?: HeroImage | string;
42
- posterImage?: HeroImage;
40
+ images?: HeroImage[];
43
41
  videoUrl?: string;
44
42
  adjectives?: {
45
43
  adjective: string;