a tool for shared writing and social publishing
at update/thread-viewer 195 lines 6.5 kB view raw
1"use client"; 2 3import React, { JSX, useState } from "react"; 4import { useUIState } from "src/useUIState"; 5import { useEntitySetContext } from "../EntitySetProvider"; 6 7import { useReplicache } from "src/replicache"; 8 9import { Media } from "../Media"; 10import { MenuItem, Menu } from "../Menu"; 11import { PageThemeSetter } from "../ThemeManager/PageThemeSetter"; 12import { PageShareMenu } from "./PageShareMenu"; 13import { useUndoState } from "src/undoManager"; 14import { CloseTiny } from "components/Icons/CloseTiny"; 15import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny"; 16import { PaintSmall } from "components/Icons/PaintSmall"; 17import { ShareSmall } from "components/Icons/ShareSmall"; 18import { useCardBorderHidden } from "./useCardBorderHidden"; 19import { useLeafletPublicationData } from "components/PageSWRDataProvider"; 20 21export const PageOptionButton = ({ 22 children, 23 secondary, 24 className, 25 disabled, 26 ...props 27}: { 28 children: React.ReactNode; 29 secondary?: boolean; 30 className?: string; 31 disabled?: boolean; 32} & Omit<JSX.IntrinsicElements["button"], "content">) => { 33 const cardBorderHidden = useCardBorderHidden(); 34 return ( 35 <button 36 className={` 37 pageOptionsTrigger 38 shrink-0 39 pt-[2px] h-5 w-5 p-0.5 mx-auto 40 border border-border 41 ${secondary ? "bg-border text-bg-page" : "bg-bg-page text-border"} 42 ${disabled && "opacity-50"} 43 ${cardBorderHidden ? "rounded-md" : `rounded-b-md sm:rounded-l-none sm:rounded-r-md`} 44 flex items-center justify-center 45 ${className} 46 47 `} 48 {...props} 49 > 50 {children} 51 </button> 52 ); 53}; 54 55export const PageOptions = (props: { 56 entityID: string; 57 first: boolean | undefined; 58 isFocused: boolean; 59}) => { 60 return ( 61 <div 62 className={`pageOptions w-fit z-10 63 ${props.isFocused ? "block" : "sm:hidden block"} 64 absolute sm:-right-[19px] right-3 sm:top-3 top-0 65 flex sm:flex-col flex-row-reverse gap-1 items-start`} 66 > 67 {!props.first && ( 68 <PageOptionButton 69 secondary 70 onClick={() => { 71 useUIState.getState().closePage(props.entityID); 72 }} 73 > 74 <CloseTiny /> 75 </PageOptionButton> 76 )} 77 <OptionsMenu entityID={props.entityID} first={!!props.first} /> 78 <UndoButtons /> 79 </div> 80 ); 81}; 82 83export const UndoButtons = () => { 84 let undoState = useUndoState(); 85 let { undoManager } = useReplicache(); 86 return ( 87 <Media mobile> 88 {undoState.canUndo && ( 89 <div className="gap-1 flex sm:flex-col"> 90 <PageOptionButton secondary onClick={() => undoManager.undo()}> 91 <UndoTiny /> 92 </PageOptionButton> 93 94 <PageOptionButton 95 secondary 96 onClick={() => undoManager.redo()} 97 disabled={!undoState.canRedo} 98 > 99 <RedoTiny /> 100 </PageOptionButton> 101 </div> 102 )} 103 </Media> 104 ); 105}; 106 107export const OptionsMenu = (props: { entityID: string; first: boolean }) => { 108 let [state, setState] = useState<"normal" | "theme" | "share">("normal"); 109 let { permissions } = useEntitySetContext(); 110 if (!permissions.write) return null; 111 112 let { data: pub, mutate } = useLeafletPublicationData(); 113 if (pub && props.first) return; 114 return ( 115 <Menu 116 align="end" 117 asChild 118 onOpenChange={(open) => { 119 if (!open) setState("normal"); 120 }} 121 trigger={ 122 <PageOptionButton className="!w-8 !h-5 sm:!w-5 sm:!h-8"> 123 <MoreOptionsTiny className="sm:rotate-90" /> 124 </PageOptionButton> 125 } 126 > 127 {state === "normal" ? ( 128 <> 129 {!props.first && ( 130 <MenuItem 131 onSelect={(e) => { 132 e.preventDefault(); 133 setState("share"); 134 }} 135 > 136 <ShareSmall /> Share Page 137 </MenuItem> 138 )} 139 {!pub && ( 140 <MenuItem 141 onSelect={(e) => { 142 e.preventDefault(); 143 setState("theme"); 144 }} 145 > 146 <PaintSmall /> Theme Page 147 </MenuItem> 148 )} 149 </> 150 ) : state === "theme" ? ( 151 <PageThemeSetter entityID={props.entityID} /> 152 ) : state === "share" ? ( 153 <PageShareMenu entityID={props.entityID} /> 154 ) : null} 155 </Menu> 156 ); 157}; 158 159const UndoTiny = () => { 160 return ( 161 <svg 162 width="16" 163 height="16" 164 viewBox="0 0 16 16" 165 fill="none" 166 xmlns="http://www.w3.org/2000/svg" 167 > 168 <path 169 fillRule="evenodd" 170 clipRule="evenodd" 171 d="M5.98775 3.14543C6.37828 2.75491 6.37828 2.12174 5.98775 1.73122C5.59723 1.34069 4.96407 1.34069 4.57354 1.73122L1.20732 5.09744C0.816798 5.48796 0.816798 6.12113 1.20732 6.51165L4.57354 9.87787C4.96407 10.2684 5.59723 10.2684 5.98775 9.87787C6.37828 9.48735 6.37828 8.85418 5.98775 8.46366L4.32865 6.80456H9.6299C12.1732 6.80456 13.0856 8.27148 13.0856 9.21676C13.0856 9.84525 12.8932 10.5028 12.5318 10.9786C12.1942 11.4232 11.6948 11.7367 10.9386 11.7367H9.43173C8.87944 11.7367 8.43173 12.1844 8.43173 12.7367C8.43173 13.2889 8.87944 13.7367 9.43173 13.7367H10.9386C12.3587 13.7367 13.4328 13.0991 14.1246 12.1883C14.7926 11.3086 15.0856 10.2062 15.0856 9.21676C15.0856 6.92612 13.0205 4.80456 9.6299 4.80456L4.32863 4.80456L5.98775 3.14543Z" 172 fill="currentColor" 173 /> 174 </svg> 175 ); 176}; 177 178const RedoTiny = () => { 179 return ( 180 <svg 181 width="16" 182 height="16" 183 viewBox="0 0 16 16" 184 fill="none" 185 xmlns="http://www.w3.org/2000/svg" 186 > 187 <path 188 fillRule="evenodd" 189 clipRule="evenodd" 190 d="M10.0122 3.14543C9.62172 2.75491 9.62172 2.12174 10.0122 1.73122C10.4028 1.34069 11.0359 1.34069 11.4265 1.73122L14.7927 5.09744C15.1832 5.48796 15.1832 6.12113 14.7927 6.51165L11.4265 9.87787C11.0359 10.2684 10.4028 10.2684 10.0122 9.87787C9.62172 9.48735 9.62172 8.85418 10.0122 8.46366L11.6713 6.80456H6.3701C3.82678 6.80456 2.91443 8.27148 2.91443 9.21676C2.91443 9.84525 3.10681 10.5028 3.46817 10.9786C3.8058 11.4232 4.30523 11.7367 5.06143 11.7367H6.56827C7.12056 11.7367 7.56827 12.1844 7.56827 12.7367C7.56827 13.2889 7.12056 13.7367 6.56827 13.7367H5.06143C3.6413 13.7367 2.56723 13.0991 1.87544 12.1883C1.20738 11.3086 0.914429 10.2062 0.914429 9.21676C0.914429 6.92612 2.97946 4.80456 6.3701 4.80456L11.6714 4.80456L10.0122 3.14543Z" 191 fill="currentColor" 192 /> 193 </svg> 194 ); 195};