tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
28
pulls
pipelines
unify background color for publications
cozylittle.house
10 months ago
4ff9ac2e
de451c11
+181
-80
5 changed files
expand all
collapse all
unified
split
app
[leaflet_id]
page.tsx
lish
[handle]
[publication]
[rkey]
page.tsx
dashboard
PublicationDashboard.tsx
page.tsx
components
ThemeManager
ThemeProvider.tsx
+1
-1
app/[leaflet_id]/page.tsx
···
31
31
if (!rootEntity || !res.data || res.data.blocked_by_admin)
32
32
return (
33
33
<div className="w-screen h-screen flex place-items-center bg-bg-leaflet">
34
34
-
<div className="bg-bg-page mx-auto p-4 border border-border rounded-md flex flex-col text-center justify-centergap-1 w-fit">
34
34
+
<div className="bg-bg-page mx-auto p-4 border border-border rounded-md flex flex-col text-center justify-center gap-1 w-fit">
35
35
<div className="font-bold">
36
36
Hmmm…we couldn't find that Leaflet.
37
37
</div>
+1
-2
app/lish/[handle]/[publication]/[rkey]/page.tsx
···
1
1
import Link from "next/link";
2
2
-
import { Footer } from "../../../Footer";
3
2
import { getPds, IdResolver } from "@atproto/identity";
4
3
import { supabaseServerClient } from "supabase/serverClient";
5
4
import { AtUri } from "@atproto/syntax";
···
59
58
blocks = firstPage.blocks || [];
60
59
}
61
60
return (
62
62
-
<div className="postPage w-full h-screen bg-bg-leaflet flex items-stretch">
61
61
+
<div className="postPage w-full h-screen bg-[#FDFCFA] flex items-stretch">
63
62
<div className="pubWrapper flex flex-col w-full ">
64
63
<div className="pubContent flex flex-col px-3 sm:px-4 py-3 sm:py-9 mx-auto max-w-prose h-full w-full overflow-auto">
65
64
<div className="flex flex-col pb-8">
+1
-1
app/lish/[handle]/[publication]/dashboard/PublicationDashboard.tsx
···
32
32
function Tab(props: { name: string; selected: boolean; onSelect: () => void }) {
33
33
return (
34
34
<div
35
35
-
className={`pubTabs border bg-bg-page border-b-0 px-2 pt-1 pb-0.5 rounded-t-md border-border hover:cursor-pointer ${props.selected ? "text-accent-1 font-bold -mb-[1px]" : ""}`}
35
35
+
className={`pubTabs border bg-[#FDFCFA] border-b-0 px-2 pt-1 pb-0.5 rounded-t-md border-border hover:cursor-pointer ${props.selected ? "text-accent-1 font-bold -mb-[1px]" : ""}`}
36
36
onClick={() => props.onSelect()}
37
37
>
38
38
{props.name}
+76
-70
app/lish/[handle]/[publication]/dashboard/page.tsx
···
61
61
try {
62
62
return (
63
63
<ThemeProvider entityID={null}>
64
64
-
<div className="relative max-w-prose w-full h-full mx-auto flex sm:flex-row flex-col sm:items-stretch sm:px-6">
65
65
-
<div className="w-12 relative">
66
66
-
<Sidebar className="mt-6 p-2">
67
67
-
<Actions publication={publication.uri} />
68
68
-
</Sidebar>
69
69
-
</div>
70
70
-
<div
71
71
-
className={`h-full overflow-y-scroll pt-4 sm:pl-5 sm:pt-9 w-full`}
72
72
-
>
73
73
-
<PublicationDashboard
74
74
-
name={publication.name}
75
75
-
tabs={{
76
76
-
Drafts: (
77
77
-
<DraftList
78
78
-
publication={publication.uri}
79
79
-
drafts={publication.leaflets_in_publications.filter(
80
80
-
(p) => !p.doc,
81
81
-
)}
82
82
-
/>
83
83
-
),
84
84
-
Published:
85
85
-
publication.documents_in_publications.length === 0 ? (
86
86
-
<div className="italic text-tertiary w-full container text-center place-items-center flex flex-col gap-3 p-3">
87
87
-
Nothing's been published yet...
88
88
-
</div>
89
89
-
) : (
90
90
-
<div className="publishedList w-full flex flex-col gap-4 pb-6">
91
91
-
{publication.documents_in_publications.map((doc) => {
92
92
-
if (!doc.documents) return null;
93
93
-
let leaflet = publication.leaflets_in_publications.find(
94
94
-
(l) => doc.documents && l.doc === doc.documents.uri,
95
95
-
);
96
96
-
let uri = new AtUri(doc.documents.uri);
97
97
-
let record = doc.documents
98
98
-
.data as PubLeafletDocument.Record;
64
64
+
<div className="w-screen h-screen flex place-items-center bg-[#FDFCFA]">
65
65
+
<div className="relative max-w-prose w-full h-full mx-auto flex sm:flex-row flex-col sm:items-stretch sm:px-6">
66
66
+
<div className="w-12 relative">
67
67
+
<Sidebar className="mt-6 p-2">
68
68
+
<Actions publication={publication.uri} />
69
69
+
</Sidebar>
70
70
+
</div>
71
71
+
<div
72
72
+
className={`h-full overflow-y-scroll pt-4 sm:pl-5 sm:pt-9 w-full`}
73
73
+
>
74
74
+
<PublicationDashboard
75
75
+
name={publication.name}
76
76
+
tabs={{
77
77
+
Drafts: (
78
78
+
<DraftList
79
79
+
publication={publication.uri}
80
80
+
drafts={publication.leaflets_in_publications.filter(
81
81
+
(p) => !p.doc,
82
82
+
)}
83
83
+
/>
84
84
+
),
85
85
+
Published:
86
86
+
publication.documents_in_publications.length === 0 ? (
87
87
+
<div className="italic text-tertiary w-full container text-center place-items-center flex flex-col gap-3 p-3">
88
88
+
Nothing's been published yet...
89
89
+
</div>
90
90
+
) : (
91
91
+
<div className="publishedList w-full flex flex-col gap-4 pb-6">
92
92
+
{publication.documents_in_publications.map((doc) => {
93
93
+
if (!doc.documents) return null;
94
94
+
let leaflet =
95
95
+
publication.leaflets_in_publications.find(
96
96
+
(l) =>
97
97
+
doc.documents && l.doc === doc.documents.uri,
98
98
+
);
99
99
+
let uri = new AtUri(doc.documents.uri);
100
100
+
let record = doc.documents
101
101
+
.data as PubLeafletDocument.Record;
99
102
100
100
-
return (
101
101
-
<React.Fragment key={doc.documents?.uri}>
102
102
-
<div className="flex w-full ">
103
103
-
<Link
104
104
-
href={`/lish/${params.handle}/${params.publication}/${uri.rkey}`}
105
105
-
className="publishedPost grow flex flex-col gap-2 hover:!no-underline"
106
106
-
>
107
107
-
<h3 className="text-primary">{record.title}</h3>
108
108
-
<p className="italic text-secondary">
109
109
-
This is a placeholder for description
110
110
-
</p>
111
111
-
<p className="text-sm text-tertiary pt-2">
112
112
-
{record.publishedAt} PlaceholderDate
113
113
-
</p>
114
114
-
</Link>
115
115
-
{leaflet && (
103
103
+
return (
104
104
+
<React.Fragment key={doc.documents?.uri}>
105
105
+
<div className="flex w-full ">
116
106
<Link
117
117
-
className="pt-[6px]"
118
118
-
href={`/${leaflet.leaflet}`}
107
107
+
href={`/lish/${params.handle}/${params.publication}/${uri.rkey}`}
108
108
+
className="publishedPost grow flex flex-col hover:!no-underline"
119
109
>
120
120
-
<EditTiny />
110
110
+
<h3 className="text-primary">
111
111
+
{record.title}
112
112
+
</h3>
113
113
+
<p className="italic text-secondary">
114
114
+
This is a placeholder for description
115
115
+
</p>
116
116
+
<p className="text-sm text-tertiary pt-2">
117
117
+
{record.publishedAt} PlaceholderDate
118
118
+
</p>
121
119
</Link>
122
122
-
)}
123
123
-
</div>
124
124
-
<hr className="last:hidden border-border-light" />
125
125
-
</React.Fragment>
126
126
-
);
127
127
-
})}
128
128
-
</div>
129
129
-
),
130
130
-
}}
131
131
-
defaultTab={"Drafts"}
132
132
-
/>
120
120
+
{leaflet && (
121
121
+
<Link
122
122
+
className="pt-[6px]"
123
123
+
href={`/${leaflet.leaflet}`}
124
124
+
>
125
125
+
<EditTiny />
126
126
+
</Link>
127
127
+
)}
128
128
+
</div>
129
129
+
<hr className="last:hidden border-border-light" />
130
130
+
</React.Fragment>
131
131
+
);
132
132
+
})}
133
133
+
</div>
134
134
+
),
135
135
+
}}
136
136
+
defaultTab={"Drafts"}
137
137
+
/>
138
138
+
</div>
139
139
+
<Media mobile>
140
140
+
<Footer>
141
141
+
<Actions publication={publication.uri} />
142
142
+
</Footer>
143
143
+
</Media>
133
144
</div>
134
134
-
<Media mobile>
135
135
-
<Footer>
136
136
-
<Actions publication={publication.uri} />
137
137
-
</Footer>
138
138
-
</Media>
139
145
</div>
140
146
</ThemeProvider>
141
147
);
+102
-6
components/ThemeManager/ThemeProvider.tsx
···
5
5
CSSProperties,
6
6
useContext,
7
7
useEffect,
8
8
+
useMemo,
8
9
useState,
9
10
} from "react";
10
11
import { colorToString, useColorAttribute } from "./useColorAttribute";
···
12
13
import { parse, contrastLstar, ColorSpace, sRGB } from "colorjs.io/fn";
13
14
14
15
import { useEntity } from "src/replicache";
16
16
+
import { useLeafletPublicationData } from "components/PageSWRDataProvider";
15
17
16
18
type CSSVariables = {
17
19
"--bg-leaflet": string;
···
52
54
local?: boolean;
53
55
children: React.ReactNode;
54
56
}) {
57
57
+
let { data } = useLeafletPublicationData();
58
58
+
if (!data[0]) return <LeafletThemeProvider {...props} />;
59
59
+
return <PublicationThemeProvider {...props} />;
60
60
+
}
61
61
+
export function PublicationThemeProvider(props: {
62
62
+
entityID: string | null;
63
63
+
local?: boolean;
64
64
+
children: React.ReactNode;
65
65
+
}) {
66
66
+
let bgLeaflet = useMemo(() => {
67
67
+
return parseColor(`#FDFCFA`);
68
68
+
}, []);
69
69
+
let bgPage = useColorAttribute(props.entityID, "theme/card-background");
70
70
+
let primary = useColorAttribute(props.entityID, "theme/primary");
71
71
+
72
72
+
let highlight1 = useEntity(props.entityID, "theme/highlight-1");
73
73
+
let highlight2 = useColorAttribute(props.entityID, "theme/highlight-2");
74
74
+
let highlight3 = useColorAttribute(props.entityID, "theme/highlight-3");
75
75
+
76
76
+
let accent1 = useColorAttribute(props.entityID, "theme/accent-background");
77
77
+
let accent2 = useColorAttribute(props.entityID, "theme/accent-text");
78
78
+
// set accent contrast to the accent color that has the highest contrast with the page background
79
79
+
let accentContrast = [accent1, accent2].sort((a, b) => {
80
80
+
return (
81
81
+
getColorContrast(colorToString(b, "rgb"), colorToString(bgPage, "rgb")) -
82
82
+
getColorContrast(colorToString(a, "rgb"), colorToString(bgPage, "rgb"))
83
83
+
);
84
84
+
})[0];
85
85
+
86
86
+
return (
87
87
+
<BaseThemeProvider
88
88
+
bgLeaflet={bgLeaflet}
89
89
+
bgPage={bgPage}
90
90
+
primary={primary}
91
91
+
highlight2={highlight2}
92
92
+
highlight3={highlight3}
93
93
+
highlight1={highlight1?.data.value}
94
94
+
accent1={accent1}
95
95
+
accent2={accent2}
96
96
+
accentContrast={accentContrast}
97
97
+
>
98
98
+
{props.children}
99
99
+
</BaseThemeProvider>
100
100
+
);
101
101
+
}
102
102
+
103
103
+
export function LeafletThemeProvider(props: {
104
104
+
entityID: string | null;
105
105
+
local?: boolean;
106
106
+
children: React.ReactNode;
107
107
+
}) {
55
108
let bgLeaflet = useColorAttribute(props.entityID, "theme/page-background");
56
109
let bgPage = useColorAttribute(props.entityID, "theme/card-background");
57
110
let primary = useColorAttribute(props.entityID, "theme/primary");
···
70
123
);
71
124
})[0];
72
125
126
126
+
return (
127
127
+
<BaseThemeProvider
128
128
+
bgLeaflet={bgLeaflet}
129
129
+
bgPage={bgPage}
130
130
+
primary={primary}
131
131
+
highlight2={highlight2}
132
132
+
highlight3={highlight3}
133
133
+
highlight1={highlight1?.data.value}
134
134
+
accent1={accent1}
135
135
+
accent2={accent2}
136
136
+
accentContrast={accentContrast}
137
137
+
>
138
138
+
{props.children}
139
139
+
</BaseThemeProvider>
140
140
+
);
141
141
+
}
142
142
+
143
143
+
let BaseThemeProvider = ({
144
144
+
local,
145
145
+
bgLeaflet,
146
146
+
bgPage,
147
147
+
primary,
148
148
+
accent1,
149
149
+
accent2,
150
150
+
accentContrast,
151
151
+
highlight1,
152
152
+
highlight2,
153
153
+
highlight3,
154
154
+
children,
155
155
+
}: {
156
156
+
local?: boolean;
157
157
+
bgLeaflet: AriaColor;
158
158
+
bgPage: AriaColor;
159
159
+
primary: AriaColor;
160
160
+
accent1: AriaColor;
161
161
+
accent2: AriaColor;
162
162
+
accentContrast: AriaColor;
163
163
+
highlight1?: string;
164
164
+
highlight2: AriaColor;
165
165
+
highlight3: AriaColor;
166
166
+
children: React.ReactNode;
167
167
+
}) => {
73
168
useEffect(() => {
74
74
-
if (props.local) return;
169
169
+
if (local) return;
75
170
let el = document.querySelector(":root") as HTMLElement;
76
171
if (!el) return;
77
172
setCSSVariableToColor(el, "--bg-leaflet", bgLeaflet);
···
90
185
91
186
//highlight 1 is special because its default value is a calculated value
92
187
if (highlight1) {
93
93
-
let color = parseColor(`hsba(${highlight1.data.value})`);
188
188
+
let color = parseColor(`hsba(${highlight1})`);
94
189
el?.style.setProperty(
95
190
"--highlight-1",
96
191
`rgb(${colorToString(color, "rgb")})`,
···
112
207
accentContrast === accent1 ? "1" : "0",
113
208
);
114
209
}, [
115
115
-
props.local,
210
210
+
local,
116
211
bgLeaflet,
117
212
bgPage,
118
213
primary,
···
137
232
"--accent-contrast": colorToString(accentContrast, "rgb"),
138
233
"--accent-1-is-contrast": accentContrast === accent1 ? 1 : 0,
139
234
"--highlight-1": highlight1
140
140
-
? `rgb(${colorToString(parseColor(`hsba(${highlight1.data.value})`), "rgb")})`
235
235
+
? `rgb(${colorToString(parseColor(`hsba(${highlight1})`), "rgb")})`
141
236
: "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 75%)",
142
237
"--highlight-2": colorToString(highlight2, "rgb"),
143
238
"--highlight-3": colorToString(highlight3, "rgb"),
144
239
} as CSSProperties
145
240
}
146
241
>
147
147
-
{props.children}
242
242
+
{" "}
243
243
+
{children}{" "}
148
244
</div>
149
245
);
150
150
-
}
246
246
+
};
151
247
152
248
let CardThemeProviderContext = createContext<null | string>(null);
153
249
export function NestedCardThemeProvider(props: { children: React.ReactNode }) {