a tool for shared writing and social publishing
at refactor/shared-home-layout 332 lines 10 kB view raw
1"use client"; 2import { Popover } from "components/Popover"; 3import { theme } from "../../tailwind.config"; 4 5import { Color } from "react-aria-components"; 6 7import { LeafletBGPicker } from "./Pickers/LeafletBGPicker"; 8import { 9 PageBackgroundPicker, 10 PageBorderHider, 11 PageThemePickers, 12} from "./Pickers/PageThemePickers"; 13import { useMemo, useState } from "react"; 14import { ReplicacheMutators, useEntity, useReplicache } from "src/replicache"; 15import { Replicache } from "replicache"; 16import { FilterAttributes } from "src/replicache/attributes"; 17import { colorToString } from "components/ThemeManager/useColorAttribute"; 18import { useEntitySetContext } from "components/EntitySetProvider"; 19import { ActionButton } from "components/ActionBar/ActionButton"; 20import { CheckboxChecked } from "components/Icons/CheckboxChecked"; 21import { CheckboxEmpty } from "components/Icons/CheckboxEmpty"; 22import { PaintSmall } from "components/Icons/PaintSmall"; 23import { AccentPickers } from "./Pickers/AccentPickers"; 24import { useLeafletPublicationData } from "components/PageSWRDataProvider"; 25import { useIsMobile } from "src/hooks/isMobile"; 26import { Toggle } from "components/Toggle"; 27 28export type pickers = 29 | "null" 30 | "leaflet" 31 | "page" 32 | "accent-1" 33 | "accent-2" 34 | "text" 35 | "highlight-1" 36 | "highlight-2" 37 | "highlight-3" 38 | "page-background-image"; 39 40export function setColorAttribute( 41 rep: Replicache<ReplicacheMutators> | null, 42 entity: string, 43) { 44 return (attribute: keyof FilterAttributes<{ type: "color" }>) => 45 (color: Color) => 46 rep?.mutate.assertFact({ 47 entity, 48 attribute, 49 data: { type: "color", value: colorToString(color, "hsba") }, 50 }); 51} 52export const ThemePopover = (props: { entityID: string; home?: boolean }) => { 53 let { rep } = useReplicache(); 54 let { data: pub } = useLeafletPublicationData(); 55 let isMobile = useIsMobile(); 56 57 // I need to get these variables from replicache and then write them to the DB. I also need to parse them into a state that can be used here. 58 let permission = useEntitySetContext().permissions.write; 59 let leafletBGImage = useEntity(props.entityID, "theme/background-image"); 60 let leafletBGRepeat = useEntity( 61 props.entityID, 62 "theme/background-image-repeat", 63 ); 64 65 let [openPicker, setOpenPicker] = useState<pickers>( 66 props.home === true ? "leaflet" : "null", 67 ); 68 let set = useMemo(() => { 69 return setColorAttribute(rep, props.entityID); 70 }, [rep, props.entityID]); 71 72 if (!permission) return null; 73 if (pub) return null; 74 75 return ( 76 <> 77 <Popover 78 className="w-80 bg-white" 79 arrowFill="#FFFFFF" 80 asChild 81 side={isMobile ? "top" : "right"} 82 align={isMobile ? "center" : "start"} 83 trigger={<ActionButton icon={<PaintSmall />} label="Theme" />} 84 > 85 <div className="themeSetterContent flex flex-col w-full overflow-y-scroll no-scrollbar"> 86 <div className="themeBGLeaflet flex"> 87 <div 88 className={`bgPicker flex flex-col gap-0 -mb-[6px] z-10 w-full `} 89 > 90 <div className="bgPickerBody w-full flex flex-col gap-2 p-2 mt-1 border border-[#CCCCCC] rounded-md"> 91 <LeafletBGPicker 92 entityID={props.entityID} 93 thisPicker={"leaflet"} 94 openPicker={openPicker} 95 setOpenPicker={setOpenPicker} 96 closePicker={() => setOpenPicker("null")} 97 setValue={set("theme/page-background")} 98 /> 99 <PageBackgroundPicker 100 entityID={props.entityID} 101 setValue={set("theme/card-background")} 102 openPicker={openPicker} 103 setOpenPicker={setOpenPicker} 104 home={props.home} 105 /> 106 <hr className=" border-[#CCCCCC]" /> 107 <PageBorderHider 108 entityID={props.entityID} 109 openPicker={openPicker} 110 setOpenPicker={setOpenPicker} 111 /> 112 </div> 113 114 <SectionArrow 115 fill="white" 116 stroke="#CCCCCC" 117 className="ml-2 -mt-px" 118 /> 119 </div> 120 </div> 121 122 <div 123 onClick={(e) => { 124 e.currentTarget === e.target && setOpenPicker("leaflet"); 125 }} 126 style={{ 127 backgroundImage: leafletBGImage 128 ? `url(${leafletBGImage.data.src})` 129 : undefined, 130 backgroundRepeat: leafletBGRepeat ? "repeat" : "no-repeat", 131 backgroundPosition: "center", 132 backgroundSize: !leafletBGRepeat 133 ? "cover" 134 : `calc(${leafletBGRepeat.data.value}px / 2 )`, 135 }} 136 className={`bg-bg-leaflet px-3 pt-4 pb-0 mb-2 flex flex-col gap-4 rounded-md border border-border`} 137 > 138 <PageThemePickers 139 entityID={props.entityID} 140 openPicker={openPicker} 141 setOpenPicker={(pickers) => setOpenPicker(pickers)} 142 /> 143 <div className="flex flex-col -gap-[6px]"> 144 <div className={`flex flex-col z-10 -mb-[6px] `}> 145 <AccentPickers 146 entityID={props.entityID} 147 openPicker={openPicker} 148 setOpenPicker={(pickers) => setOpenPicker(pickers)} 149 /> 150 <SectionArrow 151 fill={theme.colors["accent-2"]} 152 stroke={theme.colors["accent-1"]} 153 className="ml-2" 154 /> 155 </div> 156 157 <SampleButton 158 entityID={props.entityID} 159 setOpenPicker={setOpenPicker} 160 /> 161 </div> 162 163 <SamplePage 164 setOpenPicker={setOpenPicker} 165 home={props.home} 166 entityID={props.entityID} 167 /> 168 </div> 169 {!props.home && <WatermarkSetter entityID={props.entityID} />} 170 </div> 171 </Popover> 172 </> 173 ); 174}; 175 176function WatermarkSetter(props: { entityID: string }) { 177 let { rep } = useReplicache(); 178 let checked = useEntity(props.entityID, "theme/page-leaflet-watermark"); 179 180 function handleToggle() { 181 rep?.mutate.assertFact({ 182 entity: props.entityID, 183 attribute: "theme/page-leaflet-watermark", 184 data: { type: "boolean", value: !checked?.data.value }, 185 }); 186 } 187 return ( 188 <div className="flex gap-2 items-start mt-0.5"> 189 <Toggle 190 toggleOn={!!checked?.data.value} 191 setToggleOn={() => { 192 handleToggle(); 193 }} 194 disabledColor1="#8C8C8C" 195 disabledColor2="#DBDBDB" 196 /> 197 <button 198 className="flex gap-2 items-center -mt-0.5" 199 onClick={() => { 200 handleToggle(); 201 }} 202 > 203 <div className="flex flex-col gap-0 items-start"> 204 <div className="font-bold">Show Leaflet Watermark</div> 205 <div className="text-sm text-[#969696]">Help us spread the word!</div> 206 </div> 207 </button> 208 </div> 209 ); 210} 211 212const SampleButton = (props: { 213 entityID: string; 214 setOpenPicker: (thisPicker: pickers) => void; 215}) => { 216 return ( 217 <div 218 onClick={(e) => { 219 e.target === e.currentTarget && props.setOpenPicker("accent-1"); 220 }} 221 className="pointer-cursor font-bold relative text-center text-lg py-2 rounded-md bg-accent-1 text-accent-2 shadow-md flex items-center justify-center" 222 > 223 <div 224 className="cursor-pointer w-fit" 225 onClick={() => { 226 props.setOpenPicker("accent-2"); 227 }} 228 > 229 Example Button 230 </div> 231 </div> 232 ); 233}; 234const SamplePage = (props: { 235 entityID: string; 236 home: boolean | undefined; 237 setOpenPicker: (picker: "page" | "text") => void; 238}) => { 239 let pageBGImage = useEntity(props.entityID, "theme/card-background-image"); 240 let pageBGRepeat = useEntity( 241 props.entityID, 242 "theme/card-background-image-repeat", 243 ); 244 let pageBGOpacity = useEntity( 245 props.entityID, 246 "theme/card-background-image-opacity", 247 ); 248 let pageBorderHidden = useEntity(props.entityID, "theme/card-border-hidden") 249 ?.data.value; 250 251 return ( 252 <div 253 onClick={(e) => { 254 e.currentTarget === e.target && props.setOpenPicker("page"); 255 }} 256 className={` 257 text-primary relative 258 ${ 259 pageBorderHidden 260 ? "py-2 px-0 border border-transparent" 261 : `cursor-pointer p-2 border border-border border-b-transparent shadow-md 262 ${props.home ? "rounded-md " : "rounded-t-lg "}` 263 }`} 264 style={ 265 pageBorderHidden 266 ? undefined 267 : { 268 backgroundColor: "rgba(var(--bg-page), var(--bg-page-alpha))", 269 } 270 } 271 > 272 <div 273 className="background absolute top-0 right-0 bottom-0 left-0 z-0 rounded-t-lg" 274 style={ 275 pageBorderHidden 276 ? undefined 277 : { 278 backgroundImage: pageBGImage 279 ? `url(${pageBGImage.data.src})` 280 : undefined, 281 282 backgroundRepeat: pageBGRepeat ? "repeat" : "no-repeat", 283 opacity: pageBGOpacity?.data.value || 1, 284 backgroundSize: !pageBGRepeat 285 ? "cover" 286 : `calc(${pageBGRepeat.data.value}px / 2 )`, 287 } 288 } 289 /> 290 <div className="z-10 relative"> 291 <p 292 onClick={() => { 293 props.setOpenPicker("text"); 294 }} 295 className="cursor-pointer font-bold w-fit" 296 > 297 Hello! 298 </p> 299 <small onClick={() => props.setOpenPicker("text")}> 300 Welcome to{" "} 301 <span className="font-bold text-accent-contrast">Leaflet</span> a 302 fun and easy way to make, share, and collab on little bits of paper 303 </small> 304 </div> 305 </div> 306 ); 307}; 308 309export const SectionArrow = (props: { 310 fill: string; 311 stroke: string; 312 className: string; 313}) => { 314 return ( 315 <svg 316 width="24" 317 height="12" 318 viewBox="0 0 24 12" 319 fill="none" 320 xmlns="http://www.w3.org/2000/svg" 321 className={props.className} 322 > 323 <path d="M11.9999 12L24 0H0L11.9999 12Z" fill={props.fill} /> 324 <path 325 fillRule="evenodd" 326 clipRule="evenodd" 327 d="M1.33552 0L12 10.6645L22.6645 0H24L12 12L0 0H1.33552Z" 328 fill={props.stroke} 329 /> 330 </svg> 331 ); 332};