a tool for shared writing and social publishing
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #60 from hyperlink-academy/help-popover

Help popover

authored by

Brendan Schlagel and committed by
GitHub
093619f4 151e2a3a

+186 -47
+33 -35
components/Blocks/MailboxBlock.tsx
··· 265 265 <Popover 266 266 className="max-w-xs" 267 267 trigger={<InfoSmall className="shrink-0 text-accent-contrast" />} 268 - content={ 269 - <div className="text-sm text-secondary flex flex-col gap-2"> 270 - {props.subscriber ? ( 271 - <> 272 - <p className="font-bold"> 273 - Get a notification whenever the creator posts to this mailbox! 274 - </p> 275 - <p> 276 - Your contact info will be kept private, and you can unsubscribe 277 - anytime. 278 - </p> 279 - </> 280 - ) : ( 281 - <> 282 - <p className="font-bold"> 283 - When you post to this mailbox, subscribers will be notified! 284 - </p> 285 - <p>Reader contact info is kept private.</p> 286 - <p>You can have one draft post at a time.</p> 287 - </> 288 - )} 289 - </div> 290 - } 291 - /> 268 + > 269 + <div className="text-sm text-secondary flex flex-col gap-2"> 270 + {props.subscriber ? ( 271 + <> 272 + <p className="font-bold"> 273 + Get a notification whenever the creator posts to this mailbox! 274 + </p> 275 + <p> 276 + Your contact info will be kept private, and you can unsubscribe 277 + anytime. 278 + </p> 279 + </> 280 + ) : ( 281 + <> 282 + <p className="font-bold"> 283 + When you post to this mailbox, subscribers will be notified! 284 + </p> 285 + <p>Reader contact info is kept private.</p> 286 + <p>You can have one draft post at a time.</p> 287 + </> 288 + )} 289 + </div> 290 + </Popover> 292 291 ); 293 292 }; 294 293 ··· 305 304 {props.unconfirmed ? "Confirm" : "Subscribe"} 306 305 </div> 307 306 } 308 - content={ 309 - <div className="text-secondary flex flex-col gap-2 py-1"> 310 - <SubscribeForm 311 - compact 312 - entityID={props.entityID} 313 - role="author" 314 - parent={props.parent} 315 - /> 316 - </div> 317 - } 318 - /> 307 + > 308 + <div className="text-secondary flex flex-col gap-2 py-1"> 309 + <SubscribeForm 310 + compact 311 + entityID={props.entityID} 312 + role="author" 313 + parent={props.parent} 314 + /> 315 + </div> 316 + </Popover> 319 317 ); 320 318 }; 321 319
+117
components/HelpPopover.tsx
··· 1 + import { isMac } from "@react-aria/utils"; 2 + import { HelpSmall } from "./Icons"; 3 + import { ShortcutKey } from "./Layout"; 4 + import { Media } from "./Media"; 5 + import { Popover } from "./Popover"; 6 + import { metaKey } from "src/utils/metaKey"; 7 + 8 + export const HelpPopover = () => { 9 + return ( 10 + <Popover 11 + className="max-w-xs w-full" 12 + trigger={ 13 + <div className="p-1 rounded-full bg-accent-1 text-accent-2"> 14 + <HelpSmall />{" "} 15 + </div> 16 + } 17 + > 18 + <div className="flex flex-col text-sm gap-1 text-secondary"> 19 + {/* <div className="font-bold text-base">Welcome to Leaflet!</div> */} 20 + <div> 21 + Welcome to <strong>Leaflet</strong> — a fun, fast, easy-to-share 22 + document editor. 23 + </div> 24 + <div 25 + className="py-2 px-1 -mx-1 rounded-md " 26 + style={{ 27 + backgroundColor: 28 + "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 85%)", 29 + }} 30 + > 31 + <a 32 + href="https://leaflet.pub/0325b34c-1948-412c-a6fb-d155fd2fe6ed" 33 + target="_blank" 34 + > 35 + <strong>Learn more</strong> 36 + </a>{" "} 37 + about Leaflet, or{" "} 38 + <a href="mailto:contact@hyperlink.academy"> 39 + <strong>email us</strong> 40 + </a>{" "} 41 + with questions or feedback! 42 + </div> 43 + <Media mobile={false}> 44 + <hr className="text-border my-1" /> 45 + <div className="flex flex-col gap-1"> 46 + <Label>Text Shortcuts</Label> 47 + <KeyboardShortcut name="Bold" keys={[metaKey(), "B"]} /> 48 + <KeyboardShortcut name="Italic" keys={[metaKey(), "I"]} /> 49 + <KeyboardShortcut name="Underline" keys={[metaKey(), "U"]} /> 50 + <KeyboardShortcut 51 + name="Highlight" 52 + keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "H"]} 53 + /> 54 + <KeyboardShortcut 55 + name="Strikethrough" 56 + keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "X"]} 57 + /> 58 + <KeyboardShortcut name="Inline Link" keys={[metaKey(), "K"]} /> 59 + 60 + <Label>Block Shortcuts</Label> 61 + {/* shift + up/down arrows (or click + drag): select multiple blocks */} 62 + <KeyboardShortcut 63 + name="Move Block Up" 64 + keys={["Shift", metaKey(), "↑"]} 65 + /> 66 + <KeyboardShortcut 67 + name="Move Block Down" 68 + keys={["Shift", metaKey(), "↓"]} 69 + /> 70 + {/* cmd/ctrl-a: first selects all text in a block; again selects all blocks on page */} 71 + {/* cmd/ctrl + up/down arrows: go to beginning / end of doc */} 72 + 73 + <Label>Outliner Shortcuts</Label> 74 + <KeyboardShortcut 75 + name="Make List" 76 + keys={[metaKey(), isMac() ? "Opt" : "Alt", "L"]} 77 + /> 78 + {/* tab / shift + tab: indent / outdent */} 79 + <KeyboardShortcut 80 + name="Toggle Checkbox" 81 + keys={[metaKey(), "Enter"]} 82 + /> 83 + <KeyboardShortcut 84 + name="Toggle Fold" 85 + keys={[metaKey(), "Shift", "Enter"]} 86 + /> 87 + <KeyboardShortcut 88 + name="Fold All" 89 + keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↑"]} 90 + /> 91 + <KeyboardShortcut 92 + name="Unfold All" 93 + keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↓"]} 94 + /> 95 + </div> 96 + </Media> 97 + </div> 98 + </Popover> 99 + ); 100 + }; 101 + 102 + const KeyboardShortcut = (props: { name: string; keys: string[] }) => { 103 + return ( 104 + <div className="flex justify-between items-center"> 105 + {props.name} 106 + <div className="flex gap-1 items-center font-bold"> 107 + {props.keys.map((key, index) => { 108 + return <ShortcutKey key={index}>{key}</ShortcutKey>; 109 + })} 110 + </div> 111 + </div> 112 + ); 113 + }; 114 + 115 + const Label = (props: { children: React.ReactNode }) => { 116 + return <div className="text-tertiary font-bold pt-2 ">{props.children}</div>; 117 + };
+20
components/Icons.tsx
··· 170 170 ); 171 171 }; 172 172 173 + export const HelpSmall = (props: Props) => { 174 + return ( 175 + <svg 176 + width="24" 177 + height="24" 178 + viewBox="0 0 24 24" 179 + fill="none" 180 + xmlns="http://www.w3.org/2000/svg" 181 + {...props} 182 + > 183 + <path 184 + fillRule="evenodd" 185 + clipRule="evenodd" 186 + d="M12 20.5C16.6944 20.5 20.5 16.6944 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5C7.30558 3.5 3.5 7.30558 3.5 12C3.5 16.6944 7.30558 20.5 12 20.5ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22ZM10.7623 14.1436C10.6242 14.1436 10.5123 14.0317 10.5123 13.8936V11.737C10.5123 11.6028 10.6182 11.493 10.7521 11.4839C11.505 11.4329 12.1113 11.2873 12.5709 11.047C13.0783 10.7818 13.3321 10.3204 13.3321 9.66306V9.42086C13.3321 8.93648 13.1879 8.59049 12.8996 8.3829C12.6228 8.17531 12.2768 8.07151 11.8616 8.07151C11.3888 8.07151 11.0024 8.20991 10.7025 8.4867C10.4678 8.69435 10.2896 8.95501 10.168 9.26868C10.1131 9.41021 9.95475 9.4897 9.81469 9.43115L8.17697 8.74653C8.0589 8.69717 7.99639 8.56631 8.03977 8.44591C8.13721 8.17547 8.26363 7.91227 8.41902 7.65633C8.62662 7.32187 8.89187 7.02778 9.2148 6.77406C9.53772 6.5088 9.92407 6.29544 10.3739 6.13398C10.8236 5.97252 11.3484 5.89179 11.9481 5.89179C12.5593 5.89179 13.1129 5.97828 13.6088 6.15128C14.1163 6.32427 14.5488 6.56646 14.9063 6.87785C15.2638 7.18924 15.5406 7.56406 15.7367 8.00231C15.9327 8.44057 16.0308 8.91918 16.0308 9.43816C16.0308 9.92255 15.9443 10.3666 15.7713 10.7702C15.6098 11.1623 15.3849 11.5083 15.0966 11.8082C14.8198 12.0965 14.4911 12.3329 14.1105 12.5175C13.8037 12.6709 13.4809 12.7844 13.1421 12.8581C13.0227 12.8841 12.9342 12.9876 12.9342 13.1098V13.8936C12.9342 14.0317 12.8222 14.1436 12.6842 14.1436H10.7623ZM10.6403 17.4448C10.8724 17.6768 11.2558 17.7929 11.7906 17.7929C12.3354 17.7929 12.7188 17.6768 12.9408 17.4448C13.1729 17.2127 13.2889 16.8265 13.2889 16.5036V16.3041C13.2889 15.9913 13.1729 15.5428 12.9408 15.3107C12.7088 15.0787 12.3253 14.9626 11.7906 14.9626C11.2558 14.9626 10.8724 15.0787 10.6403 15.3107C10.4184 15.5327 10.3074 15.9812 10.3074 16.3041V16.5036C10.3074 16.8265 10.4184 17.2127 10.6403 17.4448Z" 187 + fill="currentColor" 188 + /> 189 + </svg> 190 + ); 191 + }; 192 + 173 193 export const HomeSmall = (props: Props) => { 174 194 return ( 175 195 <svg
+2
components/MobileFooter.tsx
··· 6 6 import { ShareOptions } from "./ShareOptions"; 7 7 import { HomeButton } from "./HomeButton"; 8 8 import { useEntitySetContext } from "./EntitySetProvider"; 9 + import { HelpPopover } from "./HelpPopover"; 9 10 10 11 export function MobileFooter(props: { entityID: string }) { 11 12 let focusedBlock = useUIState((s) => s.focusedEntity); ··· 31 32 <div className="z-10 pb-2 px-2 flex justify-between"> 32 33 <HomeButton /> 33 34 <div className="flex flex-row gap-[6px] items-center "> 35 + <HelpPopover /> 34 36 <ThemePopover entityID={props.entityID} /> 35 37 <ShareOptions rootEntity={props.entityID} /> 36 38 </div>
+2
components/Pages.tsx
··· 25 25 import { useEffect } from "react"; 26 26 import { DraftPostOptions } from "./Blocks/MailboxBlock"; 27 27 import { useIsMobile } from "src/hooks/isMobile"; 28 + import { HelpPopover } from "./HelpPopover"; 28 29 29 30 export function Pages(props: { rootPage: string }) { 30 31 let openPages = useUIState((s) => s.openPages); ··· 57 58 <div className="flex flex-col justify-center gap-2 "> 58 59 <ShareOptions rootEntity={props.rootPage} /> 59 60 <LeafletOptions entityID={props.rootPage} /> 61 + <HelpPopover /> 60 62 <hr className="text-border my-3" /> 61 63 <HomeButton /> 62 64 </div>
+2 -2
components/Popover.tsx
··· 4 4 5 5 export const Popover = (props: { 6 6 trigger: React.ReactNode; 7 - content: React.ReactNode; 7 + children: React.ReactNode; 8 8 align?: "start" | "end" | "center"; 9 9 background?: string; 10 10 border?: string; ··· 20 20 sideOffset={4} 21 21 collisionPadding={16} 22 22 > 23 - {props.content} 23 + {props.children} 24 24 <RadixPopover.Arrow asChild width={16} height={8} viewBox="0 0 16 8"> 25 25 <PopoverArrow 26 26 arrowFill={
+10 -10
components/ThemeManager/ThemeSetter.tsx
··· 50 50 51 51 export function setColorAttribute( 52 52 rep: Replicache<ReplicacheMutators> | null, 53 - entity: string 53 + entity: string, 54 54 ) { 55 55 return (attribute: keyof FilterAttributes<{ type: "color" }>) => 56 56 (color: Color) => ··· 68 68 let primaryValue = useColorAttribute(props.entityID, "theme/primary"); 69 69 let accent1Value = useColorAttribute( 70 70 props.entityID, 71 - "theme/accent-background" 71 + "theme/accent-background", 72 72 ); 73 73 let accent2Value = useColorAttribute(props.entityID, "theme/accent-text"); 74 74 let permission = useEntitySetContext().permissions.write; 75 75 let backgroundImage = useEntity(props.entityID, "theme/background-image"); 76 76 let backgroundRepeat = useEntity( 77 77 props.entityID, 78 - "theme/background-image-repeat" 78 + "theme/background-image-repeat", 79 79 ); 80 80 81 81 let [openPicker, setOpenPicker] = useState<pickers>( 82 - props.home === true ? "leaflet" : "null" 82 + props.home === true ? "leaflet" : "null", 83 83 ); 84 84 let set = useMemo(() => { 85 85 return setColorAttribute(rep, props.entityID); ··· 89 89 let values = [] as string[]; 90 90 for (let i = 0; i < 3; i++) { 91 91 values.push( 92 - `${Math.floor(Math.random() * 100)}% ${Math.floor(Math.random() * 100)}%` 92 + `${Math.floor(Math.random() * 100)}% ${Math.floor(Math.random() * 100)}%`, 93 93 ); 94 94 } 95 95 return values; ··· 123 123 style={{ maxHeight: viewheight ? viewheight * 0.8 : "80vh" }} 124 124 className="z-20 themeSetterWrapper w-80 h-fit bg-white rounded-md border border-border flex" 125 125 align="center" 126 - sideOffset={6} 126 + sideOffset={4} 127 127 collisionPadding={16} 128 128 > 129 129 <div className="themeSetterContent flex flex-col w-full overflow-y-scroll no-scrollbar"> ··· 304 304 onFocus={(e) => { 305 305 e.currentTarget.setSelectionRange( 306 306 1, 307 - e.currentTarget.value.length 307 + e.currentTarget.value.length, 308 308 ); 309 309 }} 310 310 onKeyDown={(e) => { ··· 328 328 onFocus={(e) => { 329 329 e.currentTarget.setSelectionRange( 330 330 0, 331 - e.currentTarget.value.length - 1 331 + e.currentTarget.value.length - 1, 332 332 ); 333 333 }} 334 334 onKeyDown={(e) => { ··· 436 436 onFocus={(e) => { 437 437 e.currentTarget.setSelectionRange( 438 438 1, 439 - e.currentTarget.value.length 439 + e.currentTarget.value.length, 440 440 ); 441 441 }} 442 442 onKeyDown={(e) => { ··· 483 483 value={bgColor} 484 484 onChange={setColorAttribute( 485 485 rep, 486 - props.entityID 486 + props.entityID, 487 487 )("theme/page-background")} 488 488 > 489 489 <ColorArea