tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
a tool for shared writing and social publishing
284
fork
atom
overview
issues
27
pulls
pipelines
updated postListing design
cozylittle.house
4 days ago
55bed223
acbb9604
+274
-93
8 changed files
expand all
collapse all
unified
split
app
(home-pages)
reader
InboxContent.tsx
page.tsx
components
ActionBar
NavigationButtons.tsx
Icons
ShareTiny.tsx
InteractionsPreview.tsx
Pages
useHasBackgroundImage.ts
PostListing.tsx
ThemeManager
ThemeProvider.tsx
+63
-15
app/(home-pages)/reader/InboxContent.tsx
···
4
import type { Cursor, Post } from "./getReaderFeed";
5
import useSWRInfinite from "swr/infinite";
6
import { getReaderFeed } from "./getReaderFeed";
7
-
import { useEffect, useRef } from "react";
8
import Link from "next/link";
9
import { PostListing } from "components/PostListing";
0
0
0
0
10
11
export const InboxContent = (props: {
12
posts: Post[];
···
60
61
return () => observer.disconnect();
62
}, [data, size, setSize, isValidating]);
0
0
63
64
const allPosts = data ? data.flatMap((page) => page.posts) : [];
0
0
0
0
0
0
0
0
0
0
0
0
65
66
if (allPosts.length === 0 && !isValidating) return <ReaderEmpty />;
67
0
0
68
return (
69
-
<div className="flex flex-col gap-3 relative">
70
-
{allPosts.map((p) => (
71
-
<PostListing {...p} key={p.documents.uri} />
72
-
))}
73
-
{/* Trigger element for loading more posts */}
74
-
<div
75
-
ref={loadMoreRef}
76
-
className="absolute bottom-96 left-0 w-full h-px pointer-events-none"
77
-
aria-hidden="true"
78
-
/>
79
-
{isValidating && (
80
-
<div className="text-center text-tertiary py-4">
81
-
Loading more posts...
0
0
0
0
0
0
0
0
0
0
0
0
0
0
82
</div>
83
-
)}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
84
</div>
85
);
86
};
···
4
import type { Cursor, Post } from "./getReaderFeed";
5
import useSWRInfinite from "swr/infinite";
6
import { getReaderFeed } from "./getReaderFeed";
7
+
import { useEffect, useRef, useState } from "react";
8
import Link from "next/link";
9
import { PostListing } from "components/PostListing";
10
+
import { SortSmall } from "components/Icons/SortSmall";
11
+
import { Input } from "components/Input";
12
+
import { useHasBackgroundImage } from "components/Pages/useHasBackgroundImage";
13
+
import { InteractionDrawer } from "app/lish/[did]/[publication]/[rkey]/Interactions/InteractionDrawer";
14
15
export const InboxContent = (props: {
16
posts: Post[];
···
64
65
return () => observer.disconnect();
66
}, [data, size, setSize, isValidating]);
67
+
let [searchValue, setSearchValue] = useState("");
68
+
let [sort, setSort] = useState<"recent" | "popular">("popular");
69
70
const allPosts = data ? data.flatMap((page) => page.posts) : [];
71
+
const postTitles = allPosts.map((p) => {
72
+
p.documents.data?.title;
73
+
});
74
+
const filteredPosts = allPosts
75
+
.filter((p) =>
76
+
p.documents.data?.title.toLowerCase().includes(searchValue.toLowerCase()),
77
+
)
78
+
.sort(
79
+
(a, b) =>
80
+
new Date(b.documents.data?.publishedAt || 0).getTime() -
81
+
new Date(a.documents.data?.publishedAt || 0).getTime(),
82
+
);
83
84
if (allPosts.length === 0 && !isValidating) return <ReaderEmpty />;
85
86
+
let hasBackgroundImage = useHasBackgroundImage();
87
+
88
return (
89
+
<div className="flex flex-row gap-6">
90
+
<div className="flex flex-col gap-6 relative">
91
+
<div className="flex justify-between gap-4 text-tertiary">
92
+
<Input
93
+
className={`inboxSearchInput
94
+
appearance-none! outline-hidden!
95
+
w-full min-w-0 text-primary relative px-1
96
+
border rounded-md border-border-light focus-within:border-border
97
+
bg-transparent ${hasBackgroundImage ? "focus-within:bg-bg-page" : "focus-within:bg-bg-leaflet"} `}
98
+
type="text"
99
+
id="inbox-search"
100
+
size={1}
101
+
placeholder="search posts..."
102
+
value={searchValue}
103
+
onChange={(e) => {
104
+
setSearchValue(e.currentTarget.value);
105
+
}}
106
+
/>
107
+
<button
108
+
className="flex gap-1"
109
+
onClick={() => {
110
+
setSort(sort === "popular" ? "recent" : "popular");
111
+
}}
112
+
>
113
+
{sort === "popular" ? "Popular" : "Recent"}
114
+
<SortSmall />
115
+
</button>
116
</div>
117
+
{filteredPosts.map((p) => (
118
+
<PostListing {...p} key={p.documents.uri} />
119
+
))}
120
+
{/* Trigger element for loading more posts */}
121
+
<div
122
+
ref={loadMoreRef}
123
+
className="absolute bottom-96 left-0 w-full h-px pointer-events-none"
124
+
aria-hidden="true"
125
+
/>
126
+
{isValidating && (
127
+
<div className="text-center text-tertiary py-4">
128
+
Loading more posts...
129
+
</div>
130
+
)}
131
+
</div>
132
</div>
133
);
134
};
+1
app/(home-pages)/reader/page.tsx
···
6
7
export default async function Reader(props: {}) {
8
let posts = await getReaderFeed();
0
9
return (
10
<DashboardLayout
11
id="reader"
···
6
7
export default async function Reader(props: {}) {
8
let posts = await getReaderFeed();
9
+
10
return (
11
<DashboardLayout
12
id="reader"
+1
-1
components/ActionBar/NavigationButtons.tsx
···
53
labelOnMobile={!props.compactOnMobile}
54
icon={<WriterSmall />}
55
label="Write"
56
-
className={` w-fit! ${current ? "bg-bg-page! border-border-light!" : ""}`}
57
/>
58
</SpeedyLink>
59
);
···
53
labelOnMobile={!props.compactOnMobile}
54
icon={<WriterSmall />}
55
label="Write"
56
+
className={`${current ? "bg-bg-page! border-border-light!" : ""}`}
57
/>
58
</SpeedyLink>
59
);
+19
components/Icons/ShareTiny.tsx
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
import { Props } from "./Props";
2
+
3
+
export const ShareTiny = (props: Props) => {
4
+
return (
5
+
<svg
6
+
width="16"
7
+
height="16"
8
+
viewBox="0 0 16 16"
9
+
fill="none"
10
+
xmlns="http://www.w3.org/2000/svg"
11
+
{...props}
12
+
>
13
+
<path
14
+
d="M14.294 2.09457C14.4677 2.02691 14.6645 2.06158 14.8048 2.18441C14.9451 2.30734 15.0054 2.4983 14.961 2.67953L12.8145 11.4481C12.7536 11.6967 12.5144 11.8588 12.2608 11.8241L7.56942 11.1766L5.33211 13.7664C5.20836 13.9096 5.01456 13.9711 4.83114 13.9246C4.6477 13.8781 4.50644 13.7316 4.4659 13.5467L3.68368 9.98324L1.212 8.00863C1.07265 7.89707 1.00353 7.71931 1.03035 7.54281C1.05731 7.36628 1.1765 7.21715 1.34285 7.15218L14.294 2.09457ZM4.70028 9.94417L5.12118 11.867L5.8409 10.2899L5.88094 10.2176C5.89632 10.1948 5.9137 10.1732 5.9327 10.1532L8.08407 7.8807L4.70028 9.94417ZM2.51375 7.76742L4.17391 9.09457L10.7677 5.07503C10.9816 4.9446 11.2595 4.99249 11.4171 5.18734C11.5746 5.38222 11.5631 5.66361 11.3907 5.84554L7.32723 10.1346L11.9493 10.7713L13.7598 3.37582L2.51375 7.76742Z"
15
+
fill="currentColor"
16
+
/>
17
+
</svg>
18
+
);
19
+
};
+1
-1
components/InteractionsPreview.tsx
···
90
);
91
};
92
93
-
const TagPopover = (props: { tags: string[] }) => {
94
return (
95
<Popover
96
className="p-2! max-w-xs"
···
90
);
91
};
92
93
+
export const TagPopover = (props: { tags: string[] }) => {
94
return (
95
<Popover
96
className="p-2! max-w-xs"
+5
components/Pages/useHasBackgroundImage.ts
···
0
0
0
0
0
···
1
+
import { useHasBackgroundImageContext } from "components/ThemeManager/ThemeProvider";
2
+
3
+
export function useHasBackgroundImage(entityID?: string | null) {
4
+
return useHasBackgroundImageContext();
5
+
}
+159
-60
components/PostListing.tsx
···
1
"use client";
2
import { AtUri } from "@atproto/api";
3
import { PubIcon } from "components/ActionBar/Publications";
4
-
import { CommentTiny } from "components/Icons/CommentTiny";
5
-
import { QuoteTiny } from "components/Icons/QuoteTiny";
6
-
import { Separator } from "components/Layout";
7
import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider";
8
import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider";
9
-
import { useSmoker } from "components/Toast";
10
import { blobRefToSrc } from "src/utils/blobRefToSrc";
11
import type {
12
NormalizedDocument,
···
15
import type { Post } from "app/(home-pages)/reader/getReaderFeed";
16
17
import Link from "next/link";
18
-
import { InteractionPreview } from "./InteractionsPreview";
19
import { useLocalizedDate } from "src/hooks/useLocalizedDate";
0
0
0
0
0
0
20
21
export const PostListing = (props: Post) => {
22
let pubRecord = props.publication?.pubRecord as
···
36
let isStandalone = !pubRecord;
37
let theme = usePubTheme(pubRecord?.theme || postRecord?.theme, isStandalone);
38
let themeRecord = pubRecord?.theme || postRecord?.theme;
0
0
0
0
0
0
0
0
39
let backgroundImage =
40
themeRecord?.backgroundImage?.image?.ref && uri
41
? blobRefToSrc(themeRecord.backgroundImage.image.ref, new AtUri(uri).host)
···
56
let tags = (postRecord?.tags as string[] | undefined) || [];
57
58
// For standalone posts, link directly to the document
59
-
let postHref = props.publication
60
? `${props.publication.href}/${postUri.rkey}`
61
: `/p/${postUri.host}/${postUri.rkey}`;
62
63
return (
64
-
<BaseThemeProvider {...theme} local>
65
-
<div
66
-
style={{
67
-
backgroundImage: backgroundImage
68
-
? `url(${backgroundImage})`
69
-
: undefined,
70
-
backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat",
71
-
backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`,
72
-
}}
73
-
className={`no-underline! flex flex-row gap-2 w-full relative
74
-
bg-bg-leaflet
75
-
border border-border-light rounded-lg
76
-
sm:p-2 p-2 selected-outline
77
-
hover:outline-accent-contrast hover:border-accent-contrast
78
-
`}
79
-
>
80
-
<Link className="h-full w-full absolute top-0 left-0" href={postHref} />
81
<div
82
-
className={`${showPageBackground ? "bg-bg-page " : "bg-transparent"} rounded-md w-full px-[10px] pt-2 pb-2`}
83
-
style={{
84
-
backgroundColor: showPageBackground
85
-
? "rgba(var(--bg-page), var(--bg-page-alpha))"
86
-
: "transparent",
87
-
}}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
88
>
89
-
<h3 className="text-primary truncate">{postRecord.title}</h3>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
90
91
-
<p className="text-secondary italic">{postRecord.description}</p>
92
-
<div className="flex flex-col-reverse md:flex-row md gap-2 text-sm text-tertiary items-center justify-start pt-1.5 md:pt-3 w-full">
93
-
{props.publication && pubRecord && (
94
-
<PubInfo
95
-
href={props.publication.href}
96
-
pubRecord={pubRecord}
97
-
uri={props.publication.uri}
98
-
/>
99
-
)}
100
-
<div className="flex flex-row justify-between gap-2 items-center w-full">
101
-
<PostInfo publishedAt={postRecord.publishedAt} />
102
-
<InteractionPreview
103
-
postUrl={postHref}
104
-
quotesCount={quotes}
105
-
commentsCount={comments}
106
-
tags={tags}
107
-
showComments={pubRecord?.preferences?.showComments !== false}
108
-
showMentions={pubRecord?.preferences?.showMentions !== false}
109
-
share
110
-
/>
111
</div>
112
</div>
113
</div>
0
0
0
0
0
0
0
0
0
0
0
114
</div>
115
-
</BaseThemeProvider>
116
);
117
};
118
···
123
}) => {
124
return (
125
<div className="flex flex-col md:w-auto shrink-0 w-full">
126
-
<hr className="md:hidden block border-border-light mb-2" />
127
<Link
128
href={props.href}
129
-
className="text-accent-contrast font-bold no-underline text-sm flex gap-1 items-center md:w-fit relative shrink-0"
130
>
131
<PubIcon tiny record={props.pubRecord} uri={props.uri} />
132
{props.pubRecord.name}
···
135
);
136
};
137
138
-
const PostInfo = (props: { publishedAt: string | undefined }) => {
139
let localizedDate = useLocalizedDate(props.publishedAt || "", {
140
year: "numeric",
141
month: "short",
142
day: "numeric",
143
});
0
0
0
0
0
0
0
0
0
0
0
0
0
144
return (
145
-
<div className="flex gap-2 items-center shrink-0 self-start">
146
-
{props.publishedAt && (
147
-
<>
148
-
<div className="shrink-0">{localizedDate}</div>
149
-
</>
150
-
)}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
151
</div>
152
);
153
};
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
"use client";
2
import { AtUri } from "@atproto/api";
3
import { PubIcon } from "components/ActionBar/Publications";
0
0
0
4
import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider";
5
import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider";
0
6
import { blobRefToSrc } from "src/utils/blobRefToSrc";
7
import type {
8
NormalizedDocument,
···
11
import type { Post } from "app/(home-pages)/reader/getReaderFeed";
12
13
import Link from "next/link";
14
+
import { InteractionPreview, TagPopover } from "./InteractionsPreview";
15
import { useLocalizedDate } from "src/hooks/useLocalizedDate";
16
+
import { useSmoker } from "./Toast";
17
+
import { Separator } from "./Layout";
18
+
import { SpeedyLink } from "./SpeedyLink";
19
+
import { CommentTiny } from "./Icons/CommentTiny";
20
+
import { QuoteTiny } from "./Icons/QuoteTiny";
21
+
import { ShareTiny } from "./Icons/ShareTiny";
22
23
export const PostListing = (props: Post) => {
24
let pubRecord = props.publication?.pubRecord as
···
38
let isStandalone = !pubRecord;
39
let theme = usePubTheme(pubRecord?.theme || postRecord?.theme, isStandalone);
40
let themeRecord = pubRecord?.theme || postRecord?.theme;
41
+
let el = document?.getElementById(`post-listing-${postUri}`);
42
+
43
+
let hasBackgroundImage =
44
+
!!themeRecord?.backgroundImage?.image &&
45
+
el &&
46
+
Number(window.getComputedStyle(el).getPropertyValue("--bg-page-alpha")) <
47
+
0.7;
48
+
49
let backgroundImage =
50
themeRecord?.backgroundImage?.image?.ref && uri
51
? blobRefToSrc(themeRecord.backgroundImage.image.ref, new AtUri(uri).host)
···
66
let tags = (postRecord?.tags as string[] | undefined) || [];
67
68
// For standalone posts, link directly to the document
69
+
let postUrl = props.publication
70
? `${props.publication.href}/${postUri.rkey}`
71
: `/p/${postUri.host}/${postUri.rkey}`;
72
73
return (
74
+
<div className="postListing flex flex-col gap-1">
75
+
<div className="text-sm text-tertiary flex gap-1 items-center px-1 ">
76
+
<div className="flex ">
77
+
<div className="sm:w-4 w-4 sm:h-4 h-4 rounded-full bg-test border border-border-light first:ml-0 -ml-2" />
78
+
<div className="sm:w-4 w-4 sm:h-4 h-4 rounded-full bg-test border border-border-light first:ml-0 -ml-2" />
79
+
</div>
80
+
others recommend
81
+
</div>
82
+
<BaseThemeProvider {...theme} local>
0
0
0
0
0
0
0
0
83
<div
84
+
id={`post-listing-${postUri}`}
85
+
className={`
86
+
relative
87
+
flex flex-col overflow-hidden
88
+
selected-outline border-border-light rounded-lg w-full hover:outline-accent-contrast
89
+
hover:border-accent-contrast
90
+
${showPageBackground ? "bg-bg-page " : "bg-bg-leaflet"} `}
91
+
style={
92
+
hasBackgroundImage
93
+
? {
94
+
backgroundImage: backgroundImage
95
+
? `url(${backgroundImage})`
96
+
: undefined,
97
+
backgroundRepeat: backgroundImageRepeat
98
+
? "repeat"
99
+
: "no-repeat",
100
+
backgroundSize: backgroundImageRepeat
101
+
? `${backgroundImageSize}px`
102
+
: "cover",
103
+
}
104
+
: {}
105
+
}
106
>
107
+
<Link
108
+
className="h-full w-full absolute top-0 left-0"
109
+
href={postUrl}
110
+
/>
111
+
{postRecord.coverImage && (
112
+
<div className="postListingImage">
113
+
<img
114
+
src={blobRefToSrc(postRecord.coverImage.ref, postUri.host)}
115
+
alt={postRecord.title || ""}
116
+
className="w-full h-auto aspect-video rounded"
117
+
/>
118
+
</div>
119
+
)}
120
+
<div className="postListingInfo px-3 py-2">
121
+
<h3 className="postListingTitle text-primary line-clamp-2 sm:text-lg text-base">
122
+
{postRecord.title}
123
+
</h3>
124
125
+
<p className="postListingDescription text-secondary line-clamp-3 sm:text-base text-sm">
126
+
{postRecord.description}
127
+
</p>
128
+
<div className="flex flex-col-reverse md:flex-row md gap-2 text-sm text-tertiary items-center justify-start pt-1.5 md:pt-3 w-full">
129
+
{props.publication && pubRecord && (
130
+
<PubInfo
131
+
href={props.publication.href}
132
+
pubRecord={pubRecord}
133
+
uri={props.publication.uri}
134
+
/>
135
+
)}
136
+
<div className="flex flex-row justify-between gap-2 text-xs items-center w-full">
137
+
<PostDate publishedAt={postRecord.publishedAt} />
138
+
{tags.length === 0 ? null : <TagPopover tags={tags!} />}
139
+
</div>
0
0
0
0
0
140
</div>
141
</div>
142
</div>
143
+
</BaseThemeProvider>
144
+
<div className="text-sm flex justify-between text-tertiary">
145
+
<Interactions
146
+
postUrl={postUrl}
147
+
quotesCount={quotes}
148
+
commentsCount={comments}
149
+
tags={tags}
150
+
showComments={pubRecord?.preferences?.showComments !== false}
151
+
showMentions={pubRecord?.preferences?.showMentions !== false}
152
+
/>{" "}
153
+
<Share postUrl={postUrl} />
154
</div>
155
+
</div>
156
);
157
};
158
···
163
}) => {
164
return (
165
<div className="flex flex-col md:w-auto shrink-0 w-full">
166
+
<hr className="md:hidden block border-border-light mb-1" />
167
<Link
168
href={props.href}
169
+
className="text-accent-contrast font-bold no-underline text-sm flex gap-[6px] items-center md:w-fit relative shrink-0"
170
>
171
<PubIcon tiny record={props.pubRecord} uri={props.uri} />
172
{props.pubRecord.name}
···
175
);
176
};
177
178
+
const PostDate = (props: { publishedAt: string | undefined }) => {
179
let localizedDate = useLocalizedDate(props.publishedAt || "", {
180
year: "numeric",
181
month: "short",
182
day: "numeric",
183
});
184
+
if (props.publishedAt) {
185
+
return <div className="shrink-0 sm:text-sm text-xs">{localizedDate}</div>;
186
+
} else return null;
187
+
};
188
+
189
+
const Interactions = (props: {
190
+
quotesCount: number;
191
+
commentsCount: number;
192
+
tags?: string[];
193
+
postUrl: string;
194
+
showComments: boolean;
195
+
showMentions: boolean;
196
+
}) => {
197
return (
198
+
<div
199
+
className={`flex gap-2 text-tertiary text-sm items-center justify-between px-1`}
200
+
>
201
+
<div className="postListingsInteractions flex gap-3">
202
+
{!props.showMentions || props.quotesCount === 0 ? null : (
203
+
<SpeedyLink
204
+
aria-label="Post quotes"
205
+
href={`${props.postUrl}?interactionDrawer=quotes`}
206
+
className="relative flex flex-row gap-1 text-sm items-center hover:text-accent-contrast hover:no-underline! text-tertiary"
207
+
>
208
+
<QuoteTiny /> {props.quotesCount}
209
+
</SpeedyLink>
210
+
)}
211
+
{!props.showComments || props.commentsCount === 0 ? null : (
212
+
<SpeedyLink
213
+
aria-label="Post comments"
214
+
href={`${props.postUrl}?interactionDrawer=comments`}
215
+
className="relative flex flex-row gap-1 text-sm items-center hover:text-accent-contrast hover:no-underline! text-tertiary"
216
+
>
217
+
<CommentTiny /> {props.commentsCount}
218
+
</SpeedyLink>
219
+
)}
220
+
</div>
221
</div>
222
);
223
};
224
+
225
+
const Share = (props: { postUrl: string }) => {
226
+
let smoker = useSmoker();
227
+
return (
228
+
<button
229
+
id={`copy-post-link-${props.postUrl}`}
230
+
className="flex gap-1 items-center hover:text-accent-contrast relative font-bold"
231
+
onClick={(e) => {
232
+
e.stopPropagation();
233
+
e.preventDefault();
234
+
let mouseX = e.clientX;
235
+
let mouseY = e.clientY;
236
+
237
+
if (!props.postUrl) return;
238
+
navigator.clipboard.writeText(`leaflet.pub${props.postUrl}`);
239
+
240
+
smoker({
241
+
text: <strong>Copied Link!</strong>,
242
+
position: {
243
+
y: mouseY,
244
+
x: mouseX,
245
+
},
246
+
});
247
+
}}
248
+
>
249
+
Share <ShareTiny />
250
+
</button>
251
+
);
252
+
};
+25
-16
components/ThemeManager/ThemeProvider.tsx
···
8
export function useCardBorderHiddenContext() {
9
return useContext(CardBorderHiddenContext);
10
}
0
0
0
0
0
0
0
11
import {
12
colorToString,
13
useColorAttribute,
···
79
80
return (
81
<CardBorderHiddenContext.Provider value={!!cardBorderHiddenValue}>
82
-
<BaseThemeProvider
83
-
local={props.local}
84
-
bgLeaflet={bgLeaflet}
85
-
bgPage={bgPage}
86
-
primary={primary}
87
-
highlight2={highlight2}
88
-
highlight3={highlight3}
89
-
highlight1={highlight1?.data.value}
90
-
accent1={accent1}
91
-
accent2={accent2}
92
-
showPageBackground={showPageBackground}
93
-
pageWidth={pageWidth?.data.value}
94
-
hasBackgroundImage={hasBackgroundImage}
95
-
>
96
-
{props.children}
97
-
</BaseThemeProvider>
0
0
98
</CardBorderHiddenContext.Provider>
99
);
100
}
···
8
export function useCardBorderHiddenContext() {
9
return useContext(CardBorderHiddenContext);
10
}
11
+
12
+
// Context for hasBackgroundImage
13
+
export const HasBackgroundImageContext = createContext<boolean>(false);
14
+
15
+
export function useHasBackgroundImageContext() {
16
+
return useContext(HasBackgroundImageContext);
17
+
}
18
import {
19
colorToString,
20
useColorAttribute,
···
86
87
return (
88
<CardBorderHiddenContext.Provider value={!!cardBorderHiddenValue}>
89
+
<HasBackgroundImageContext.Provider value={hasBackgroundImage}>
90
+
<BaseThemeProvider
91
+
local={props.local}
92
+
bgLeaflet={bgLeaflet}
93
+
bgPage={bgPage}
94
+
primary={primary}
95
+
highlight2={highlight2}
96
+
highlight3={highlight3}
97
+
highlight1={highlight1?.data.value}
98
+
accent1={accent1}
99
+
accent2={accent2}
100
+
showPageBackground={showPageBackground}
101
+
pageWidth={pageWidth?.data.value}
102
+
hasBackgroundImage={hasBackgroundImage}
103
+
>
104
+
{props.children}
105
+
</BaseThemeProvider>
106
+
</HasBackgroundImageContext.Provider>
107
</CardBorderHiddenContext.Provider>
108
);
109
}