a tool for shared writing and social publishing
at update/delete-leaflets 308 lines 11 kB view raw
1import { pickers } from "../ThemeSetter"; 2import { theme } from "tailwind.config"; 3import { PageBackgroundColorPicker } from "../Pickers/PageThemePickers"; 4import { Color, ColorSwatch } from "react-aria-components"; 5import { BlockImageSmall } from "components/Icons/BlockImageSmall"; 6import { ColorPicker } from "../Pickers/ColorPicker"; 7import { CloseContrastSmall } from "components/Icons/CloseContrastSmall"; 8import * as Slider from "@radix-ui/react-slider"; 9import { Toggle } from "components/Toggle"; 10import { DeleteSmall } from "components/Icons/DeleteSmall"; 11import { ImageState } from "../PubThemeSetter"; 12import { Radio } from "components/Checkbox"; 13import { Input } from "components/Input"; 14 15export const BackgroundPicker = (props: { 16 backgroundColor: Color; 17 setBackgroundColor: (c: Color) => void; 18 pageBackground: Color; 19 setPageBackground: (c: Color) => void; 20 openPicker: pickers; 21 setOpenPicker: (p: pickers) => void; 22 bgImage: ImageState | null; 23 setBgImage: (i: ImageState | null) => void; 24 hasPageBackground: boolean; 25 setHasPageBackground: (s: boolean) => void; 26}) => { 27 return ( 28 <> 29 {props.bgImage && props.bgImage !== null ? ( 30 <BackgroundImagePicker 31 bgColor={props.backgroundColor} 32 bgImage={props.bgImage} 33 setBgImage={props.setBgImage} 34 thisPicker={"page-background-image"} 35 openPicker={props.openPicker} 36 setOpenPicker={props.setOpenPicker} 37 closePicker={() => props.setOpenPicker("null")} 38 setValue={props.setBackgroundColor} 39 /> 40 ) : ( 41 <div className="relative"> 42 <ColorPicker 43 label={"Background"} 44 value={props.backgroundColor} 45 setValue={props.setBackgroundColor} 46 thisPicker={"leaflet"} 47 openPicker={props.openPicker} 48 setOpenPicker={props.setOpenPicker} 49 closePicker={() => props.setOpenPicker("null")} 50 alpha={!!props.bgImage} 51 /> 52 {!props.bgImage && ( 53 <label 54 className={` 55 text-[#969696] hover:cursor-pointer shrink-0 56 absolute top-0 right-0 57 `} 58 > 59 <BlockImageSmall /> 60 <div className="hidden"> 61 <input 62 type="file" 63 accept="image/*" 64 hidden 65 onChange={async (e) => { 66 let file = e.currentTarget.files?.[0]; 67 if (file) { 68 const reader = new FileReader(); 69 reader.onload = (e) => { 70 props.setBgImage({ 71 src: e.target?.result as string, 72 file, 73 repeat: null, 74 }); 75 props.setOpenPicker("page-background-image"); 76 }; 77 reader.readAsDataURL(file); 78 } 79 }} 80 /> 81 </div> 82 </label> 83 )} 84 </div> 85 )} 86 <PageBackgroundColorPicker 87 label={"Containers"} 88 value={props.pageBackground} 89 setValue={props.setPageBackground} 90 thisPicker={"page"} 91 openPicker={props.openPicker} 92 setOpenPicker={props.setOpenPicker} 93 alpha={props.hasPageBackground ? true : false} 94 /> 95 <hr className="border-border-light" /> 96 <div className="flex gap-2 items-center"> 97 <Toggle 98 toggleOn={props.hasPageBackground} 99 setToggleOn={() => { 100 props.setHasPageBackground(!props.hasPageBackground); 101 props.hasPageBackground && 102 props.openPicker === "page" && 103 props.setOpenPicker("null"); 104 }} 105 disabledColor1="#8C8C8C" 106 disabledColor2="#DBDBDB" 107 /> 108 <button 109 className="flex gap-2 items-center" 110 onClick={() => { 111 props.setHasPageBackground(!props.hasPageBackground); 112 props.hasPageBackground && props.setOpenPicker("null"); 113 }} 114 > 115 <div className="font-bold">Page Background</div> 116 <div className="italic text-[#8C8C8C]"> 117 {props.hasPageBackground ? "" : "hidden"} 118 </div> 119 </button> 120 </div> 121 </> 122 ); 123}; 124 125const BackgroundImagePicker = (props: { 126 disabled?: boolean; 127 bgImage: ImageState | null; 128 setBgImage: (i: ImageState | null) => void; 129 bgColor: Color; 130 openPicker: pickers; 131 thisPicker: pickers; 132 setOpenPicker: (thisPicker: pickers) => void; 133 closePicker: () => void; 134 setValue: (c: Color) => void; 135}) => { 136 let open = props.openPicker == props.thisPicker; 137 138 return ( 139 <> 140 <div className="bgPickerColorLabel flex gap-2 items-center"> 141 <button 142 disabled={props.disabled} 143 onClick={() => { 144 if (props.openPicker === props.thisPicker) { 145 props.setOpenPicker("null"); 146 } else { 147 props.setOpenPicker(props.thisPicker); 148 } 149 }} 150 className="flex gap-2 items-center disabled:text-tertiary grow" 151 > 152 <ColorSwatch 153 color={props.bgColor} 154 className={`w-6 h-6 rounded-full border-2 border-white shadow-[0_0_0_1px_#8C8C8C] ${props.disabled ? "opacity-50" : ""}`} 155 style={{ 156 backgroundImage: props.bgImage 157 ? `url(${props.bgImage.src})` 158 : undefined, 159 backgroundPosition: "center", 160 backgroundSize: "cover", 161 }} 162 /> 163 <strong className={` text-[#595959]`}>Background</strong> 164 <div className="italic text-[#8C8C8C]">image</div> 165 </button> 166 <div className="flex gap-1 text-[#8C8C8C]"> 167 <button onClick={() => props.setBgImage(null)}> 168 <DeleteSmall /> 169 </button> 170 <label className="hover:cursor-pointer "> 171 <BlockImageSmall /> 172 <div className="hidden"> 173 <input 174 type="file" 175 accept="image/*" 176 hidden 177 onChange={async (e) => { 178 let file = e.currentTarget.files?.[0]; 179 if (file) { 180 const reader = new FileReader(); 181 reader.onload = (e) => { 182 if (!props.bgImage) return; 183 props.setBgImage({ 184 ...props.bgImage, 185 src: e.target?.result as string, 186 file, 187 }); 188 }; 189 reader.readAsDataURL(file); 190 } 191 }} 192 /> 193 </div> 194 </label> 195 </div> 196 </div> 197 {open && ( 198 <div className="pageImagePicker flex flex-col gap-2"> 199 <ImageSettings 200 bgImage={props.bgImage} 201 setBgImage={props.setBgImage} 202 /> 203 </div> 204 )} 205 </> 206 ); 207}; 208 209export const ImageSettings = (props: { 210 bgImage: ImageState | null; 211 setBgImage: (i: ImageState | null) => void; 212}) => { 213 return ( 214 <> 215 <div className="themeBGImageControls font-bold flex flex-col gap-1 items-center px-3"> 216 <label htmlFor="cover" className="w-full"> 217 <Radio 218 radioCheckedClassName="text-[#595959]!" 219 radioEmptyClassName="text-[#969696]!" 220 type="radio" 221 id="cover" 222 name="bg-image-options" 223 value="cover" 224 checked={!props.bgImage?.repeat} 225 onChange={async (e) => { 226 if (!e.currentTarget.checked) return; 227 if (!props.bgImage) return; 228 props.setBgImage({ ...props.bgImage, repeat: null }); 229 }} 230 > 231 <div 232 className={`w-full cursor-pointer ${!props.bgImage?.repeat ? "text-[#595959]" : " text-[#969696]"}`} 233 > 234 cover 235 </div> 236 </Radio> 237 </label> 238 <label htmlFor="repeat" className="pb-3 w-full"> 239 <Radio 240 type="radio" 241 id="repeat" 242 name="bg-image-options" 243 value="repeat" 244 radioCheckedClassName="text-[#595959]!" 245 radioEmptyClassName="text-[#969696]!" 246 checked={!!props.bgImage?.repeat} 247 onChange={async (e) => { 248 if (!e.currentTarget.checked) return; 249 if (!props.bgImage) return; 250 props.setBgImage({ ...props.bgImage, repeat: 500 }); 251 }} 252 > 253 <div className="flex flex-col gap-2 w-full"> 254 <div className="flex gap-2"> 255 <div 256 className={`shink-0 grow-0 w-fit z-10 cursor-pointer ${props.bgImage?.repeat ? "text-[#595959]" : " text-[#969696]"}`} 257 > 258 repeat 259 </div> 260 <div 261 className={`flex font-normal ${props.bgImage?.repeat ? "text-[#969696]" : " text-[#C3C3C3]"}`} 262 > 263 <Input 264 type="number" 265 className="w-10 text-right appearance-none" 266 max={3000} 267 min={10} 268 value={props.bgImage?.repeat || 500} 269 onChange={(e) => { 270 if (!props.bgImage) return; 271 props.setBgImage({ 272 ...props.bgImage, 273 repeat: parseInt(e.currentTarget.value), 274 }); 275 }} 276 />{" "} 277 px 278 </div> 279 </div> 280 <Slider.Root 281 className={`relative grow flex items-center select-none touch-none w-full h-fit px-1 `} 282 value={[props.bgImage?.repeat || 500]} 283 max={3000} 284 min={10} 285 step={10} 286 onValueChange={(value) => { 287 if (!props.bgImage) return; 288 props.setBgImage({ ...props.bgImage, repeat: value[0] }); 289 }} 290 > 291 <Slider.Track 292 className={`${props.bgImage?.repeat ? "bg-[#595959]" : " bg-[#C3C3C3]"} relative grow rounded-full h-[3px]`} 293 ></Slider.Track> 294 <Slider.Thumb 295 className={` 296 flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 297 ${props.bgImage?.repeat ? "bg-[#595959]" : " bg-[#C3C3C3] "} 298 ${props.bgImage?.repeat && "shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]"} `} 299 aria-label="Volume" 300 /> 301 </Slider.Root> 302 </div> 303 </Radio> 304 </label> 305 </div> 306 </> 307 ); 308};