Skip to content

Commit a0ed77d

Browse files
authored
Merge pull request #27 from deco-sites/jonasjesus/deco-5282-device-visibility
feat(sections): per-device visibility gate (DECO-5282)
2 parents c163790 + 4595c19 commit a0ed77d

3 files changed

Lines changed: 61 additions & 10 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { type ReactNode } from "react";
2+
import { useDevice } from "@decocms/start/sdk/useDevice";
3+
4+
export type DeviceVisibility = "all" | "mobile-only" | "desktop-only";
5+
6+
export interface VisibilityConfig {
7+
/**
8+
* @title Exibir em
9+
* @description Em quais dispositivos esta seção aparece. Útil para mostrar
10+
* conteúdo diferente no celular e no computador.
11+
* @default "all"
12+
*/
13+
visibility?: DeviceVisibility;
14+
}
15+
16+
export function isVisibleOnDevice(
17+
device: string,
18+
visibility: DeviceVisibility = "all",
19+
): boolean {
20+
if (visibility === "all") return true;
21+
const isDesktop = device === "desktop";
22+
return visibility === "desktop-only" ? isDesktop : !isDesktop;
23+
}
24+
25+
/**
26+
* Server-side device gate. Renders children only on the configured device(s).
27+
*
28+
* Safe for SSR + caching: device is resolved server-side via `useDevice`
29+
* (from the request user-agent) and the edge cache is segmented by device
30+
* (see `buildSegment` in worker-entry), so the gated-out markup is never sent
31+
* to — nor cached for — the wrong device, and there's no hydration mismatch.
32+
*/
33+
export default function DeviceVisible(
34+
{ visibility = "all", children }: VisibilityConfig & { children: ReactNode },
35+
) {
36+
const device = useDevice();
37+
if (!isVisibleOnDevice(device, visibility)) return null;
38+
return <>{children}</>;
39+
}

src/sections/Images/Banner.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { type HTMLWidget, type ImageWidget } from "~/types/widgets";
22
import { Picture, Source } from "~/components/ui/Picture";
33
import Section from "../../components/ui/Section";
4+
import DeviceVisible, {
5+
type VisibilityConfig,
6+
} from "../../components/ui/DeviceVisible";
47
import { clx } from "~/sdk/clx";
58

6-
export interface Props {
9+
export interface Props extends VisibilityConfig {
710
title: string;
811
description?: HTMLWidget;
912

@@ -18,9 +21,10 @@ export interface Props {
1821
};
1922
}
2023

21-
function Banner({ title, description, images, cta }: Props) {
24+
function Banner({ title, description, images, cta, visibility }: Props) {
2225
return (
23-
<Section.Container>
26+
<DeviceVisible visibility={visibility}>
27+
<Section.Container>
2428
<div className="relative bg-base-200 mx-5 sm:mx-0">
2529
<Picture>
2630
<Source
@@ -66,7 +70,8 @@ function Banner({ title, description, images, cta }: Props) {
6670
</div>
6771
</div>
6872
</div>
69-
</Section.Container>
73+
</Section.Container>
74+
</DeviceVisible>
7075
);
7176
}
7277

src/sections/Product/ProductShelf.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ import Section, {
66
} from "../../components/ui/Section";
77
import { useOffer } from "@decocms/apps/commerce/sdk/useOffer";
88
import { useSendEvent } from "../../sdk/useSendEvent";
9+
import DeviceVisible, {
10+
type VisibilityConfig,
11+
} from "../../components/ui/DeviceVisible";
912
import { type LoadingFallbackProps } from "~/types/deco";
10-
export interface Props extends SectionHeaderProps {
13+
export interface Props extends SectionHeaderProps, VisibilityConfig {
1114
products: Product[] | null;
1215
}
13-
export default function ProductShelf({ products, title, cta }: Props) {
16+
export default function ProductShelf(
17+
{ products, title, cta, visibility }: Props,
18+
) {
1419
if (!products || products.length === 0) {
1520
return null;
1621
}
@@ -31,11 +36,13 @@ export default function ProductShelf({ products, title, cta }: Props) {
3136
},
3237
});
3338
return (
34-
<Section.Container {...viewItemListEvent}>
35-
<Section.Header title={title} cta={cta} />
39+
<DeviceVisible visibility={visibility}>
40+
<Section.Container {...viewItemListEvent}>
41+
<Section.Header title={title} cta={cta} />
3642

37-
<ProductSlider products={products} itemListName={title} />
38-
</Section.Container>
43+
<ProductSlider products={products} itemListName={title} />
44+
</Section.Container>
45+
</DeviceVisible>
3946
);
4047
}
4148
export const LoadingFallback = (

0 commit comments

Comments
 (0)