a tool for shared writing and social publishing
at update/reader 177 lines 6.2 kB view raw
1import * as Slider from "@radix-ui/react-slider"; 2import { theme } from "../../../tailwind.config"; 3 4import { Color } from "react-aria-components"; 5import { Input } from "components/Input"; 6import { Radio } from "components/Checkbox"; 7 8import { useEntity, useReplicache } from "src/replicache"; 9import { addImage } from "src/utils/addImage"; 10import { BlockImageSmall } from "components/Icons/BlockImageSmall"; 11import { CloseContrastSmall } from "components/Icons/CloseContrastSmall"; 12 13export const ImageSettings = (props: { 14 entityID: string; 15 card?: boolean; 16 setValue: (c: Color) => void; 17}) => { 18 let image = useEntity( 19 props.entityID, 20 props.card ? "theme/card-background-image" : "theme/background-image", 21 ); 22 let repeat = useEntity( 23 props.entityID, 24 props.card 25 ? "theme/card-background-image-repeat" 26 : "theme/background-image-repeat", 27 ); 28 let pageType = useEntity(props.entityID, "page/type")?.data.value; 29 let { rep } = useReplicache(); 30 return ( 31 <> 32 <div className="themeBGImageControls font-bold flex flex-col gap-1 items-center px-3"> 33 <label htmlFor="cover" className="w-full"> 34 <Radio 35 radioCheckedClassName="text-[#595959]!" 36 radioEmptyClassName="text-[#969696]!" 37 type="radio" 38 id="cover" 39 name="bg-image-options" 40 value="cover" 41 checked={!repeat} 42 onChange={async (e) => { 43 if (!e.currentTarget.checked) return; 44 if (!repeat) return; 45 if (repeat) await rep?.mutate.retractFact({ factID: repeat.id }); 46 }} 47 > 48 <div 49 className={`w-full cursor-pointer ${!repeat ? "text-[#595959]" : " text-[#969696]"}`} 50 > 51 cover 52 </div> 53 </Radio> 54 </label> 55 <label htmlFor="repeat" className="pb-3 w-full"> 56 <Radio 57 type="radio" 58 id="repeat" 59 name="bg-image-options" 60 value="repeat" 61 radioCheckedClassName="text-[#595959]!" 62 radioEmptyClassName="text-[#969696]!" 63 checked={!!repeat} 64 onChange={async (e) => { 65 if (!e.currentTarget.checked) return; 66 if (repeat) return; 67 await rep?.mutate.assertFact({ 68 entity: props.entityID, 69 attribute: props.card 70 ? "theme/card-background-image-repeat" 71 : "theme/background-image-repeat", 72 data: { type: "number", value: 500 }, 73 }); 74 }} 75 > 76 <div className="flex flex-col w-full"> 77 <div className="flex gap-2"> 78 <div 79 className={`shink-0 grow-0 w-fit z-10 cursor-pointer ${repeat ? "text-[#595959]" : " text-[#969696]"}`} 80 > 81 repeat 82 </div> 83 <div 84 className={`flex font-normal ${repeat ? "text-[#969696]" : " text-[#C3C3C3]"}`} 85 > 86 <Input 87 type="number" 88 className="w-10 text-right appearance-none bg-transparent" 89 max={3000} 90 min={10} 91 value={repeat ? repeat.data.value : 500} 92 onChange={(e) => { 93 rep?.mutate.assertFact({ 94 entity: props.entityID, 95 attribute: props.card 96 ? "theme/card-background-image-repeat" 97 : "theme/background-image-repeat", 98 data: { 99 type: "number", 100 value: parseInt(e.currentTarget.value), 101 }, 102 }); 103 }} 104 />{" "} 105 px 106 </div> 107 </div> 108 <Slider.Root 109 className={`relative grow flex items-center select-none touch-none w-full h-fit px-1 `} 110 value={[repeat ? repeat.data.value : 500]} 111 max={3000} 112 min={10} 113 step={10} 114 onValueChange={(value) => { 115 rep?.mutate.assertFact({ 116 entity: props.entityID, 117 attribute: props.card 118 ? "theme/card-background-image-repeat" 119 : "theme/background-image-repeat", 120 data: { type: "number", value: value[0] }, 121 }); 122 }} 123 > 124 <Slider.Track 125 className={`${repeat ? "bg-[#595959]" : " bg-[#C3C3C3]"} relative grow rounded-full h-[3px] my-2`} 126 ></Slider.Track> 127 <Slider.Thumb 128 className={` 129 flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 130 ${repeat ? "bg-[#595959] shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]" : " bg-[#C3C3C3] "} 131 `} 132 aria-label="Volume" 133 /> 134 </Slider.Root> 135 </div> 136 </Radio> 137 </label> 138 </div> 139 </> 140 ); 141}; 142 143export const ImageInput = (props: { 144 entityID: string; 145 onChange?: () => void; 146 card?: boolean; 147}) => { 148 let pageType = useEntity(props.entityID, "page/type")?.data.value; 149 let { rep } = useReplicache(); 150 return ( 151 <input 152 type="file" 153 accept="image/*" 154 onChange={async (e) => { 155 let file = e.currentTarget.files?.[0]; 156 if (!file || !rep) return; 157 158 await addImage(file, rep, { 159 entityID: props.entityID, 160 attribute: props.card 161 ? "theme/card-background-image" 162 : "theme/background-image", 163 }); 164 props.onChange?.(); 165 166 if (pageType === "canvas") { 167 rep && 168 rep.mutate.assertFact({ 169 entity: props.entityID, 170 attribute: "canvas/background-pattern", 171 data: { type: "canvas-pattern-union", value: "plain" }, 172 }); 173 } 174 }} 175 /> 176 ); 177};