a tool for shared writing and social publishing

adjusted page width setter to have narrow and wide presets

+165 -89
+1 -13
app/globals.css
··· 132 132 133 133 @media (min-width: 640px) { 134 134 :root { 135 + /*picks between max width and screen width with 64px of padding*/ 135 136 --page-width-unitless: min( 136 137 var(--page-max-width-unitless), 137 138 calc(var(--leaflet-width-unitless) - 128) ··· 139 140 --page-width-units: min( 140 141 calc(var(--page-max-width-unitless) * 1px), 141 142 calc(100vw - 128px) 142 - ); 143 - } 144 - } 145 - 146 - @media (min-width: 1280px) { 147 - :root { 148 - --page-width-unitless: min( 149 - var(--page-max-width-unitless), 150 - calc((var(--leaflet-width-unitless) / 2) - 32) 151 - ); 152 - --page-width-units: min( 153 - calc(var(--page-max-width-unitless) * 1px), 154 - calc((100vw / 2) - 32px) 155 143 ); 156 144 } 157 145 }
+164 -76
components/ThemeManager/Pickers/PageWidthSetter.tsx
··· 1 1 import * as Slider from "@radix-ui/react-slider"; 2 2 import { Input } from "components/Input"; 3 + import { Radio } from "components/Checkbox"; 3 4 import { useEntity, useReplicache } from "src/replicache"; 4 5 import { pickers } from "../ThemeSetter"; 5 - import { useState } from "react"; 6 + import { useState, useEffect } from "react"; 6 7 7 8 export const PageWidthSetter = (props: { 8 9 entityID: string; ··· 12 13 closePicker: () => void; 13 14 }) => { 14 15 let { rep } = useReplicache(); 15 - let pageWidth = useEntity(props.entityID, "theme/page-width"); 16 - let currentValue = pageWidth?.data.value || 624; 16 + let pageWidth = useEntity(props.entityID, "theme/page-width")?.data.value; 17 + let currentValue = pageWidth || 624; 17 18 let [interimValue, setInterimValue] = useState<number>(currentValue); 19 + let [selectedPreset, setSelectedPreset] = useState< 20 + "default" | "wide" | "custom" 21 + >( 22 + currentValue === 624 ? "default" : currentValue === 756 ? "wide" : "custom", 23 + ); 18 24 let min = 324; 19 25 let max = 1200; 20 26 21 27 let open = props.openPicker == props.thisPicker; 22 28 29 + // Update interim value when current value changes 30 + useEffect(() => { 31 + setInterimValue(currentValue); 32 + }, [currentValue]); 33 + 34 + const setPageWidth = (value: number) => { 35 + rep?.mutate.assertFact({ 36 + entity: props.entityID, 37 + attribute: "theme/page-width", 38 + data: { 39 + type: "number", 40 + value: value, 41 + }, 42 + }); 43 + }; 44 + 23 45 return ( 24 46 <div className="pageWidthSetter flex flex-col gap-2 px-2 py-[6px] border border-[#CCCCCC] rounded-md"> 25 47 <div className="flex flex-col gap-2"> ··· 37 59 Max Page Width 38 60 </button> 39 61 <div className="flex font-normal text-[#969696]"> 40 - <Input 41 - type="number" 42 - className="relative w-10 text-right appearance-none bg-transparent" 43 - max={max} 44 - min={min} 45 - value={interimValue} 46 - onFocus={(e) => { 47 - e.preventDefault(); 48 - props.setOpenPicker(props.thisPicker); 49 - }} 50 - onChange={(e) => { 51 - setInterimValue(parseInt(e.currentTarget.value)); 52 - }} 53 - onKeyDown={(e) => { 54 - if (e.key === "Enter" || e.key === "Escape") { 55 - e.preventDefault(); 56 - let clampedValue = interimValue; 57 - if (!isNaN(interimValue)) { 58 - clampedValue = Math.max(min, Math.min(max, interimValue)); 59 - setInterimValue(clampedValue); 60 - } 61 - rep?.mutate.assertFact({ 62 - entity: props.entityID, 63 - attribute: "theme/page-width", 64 - data: { 65 - type: "number", 66 - value: clampedValue, 67 - }, 68 - }); 69 - } 70 - }} 71 - onBlur={() => { 72 - let clampedValue = interimValue; 73 - if (!isNaN(interimValue)) { 74 - clampedValue = Math.max(min, Math.min(max, interimValue)); 75 - setInterimValue(clampedValue); 76 - } 77 - rep?.mutate.assertFact({ 78 - entity: props.entityID, 79 - attribute: "theme/page-width", 80 - data: { 81 - type: "number", 82 - value: clampedValue, 83 - }, 84 - }); 85 - }} 86 - /> 87 - px 62 + {currentValue}px 88 63 </div> 89 64 </div> 90 65 {open && ( 91 - <Slider.Root 92 - className="relative grow flex items-center select-none touch-none w-full h-fit px-1 mb-1" 93 - value={[interimValue]} 94 - max={max} 95 - min={min} 96 - step={16} 97 - onValueChange={(value) => { 98 - setInterimValue(value[0]); 99 - }} 100 - onPointerUp={() => { 101 - rep?.mutate.assertFact({ 102 - entity: props.entityID, 103 - attribute: "theme/page-width", 104 - data: { type: "number", value: interimValue }, 105 - }); 106 - }} 107 - > 108 - <Slider.Track className="bg-[#595959] relative grow rounded-full h-[3px]" /> 109 - <Slider.Thumb 110 - className="flex w-4 h-4 outline-none! rounded-full border-2 border-white cursor-pointer bg-[#595959] 111 - focus:shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C] 112 - " 113 - aria-label="Max Page Width" 114 - /> 115 - </Slider.Root> 66 + <div className="flex flex-col gap-1 px-3"> 67 + <label htmlFor="default" className="w-full"> 68 + <Radio 69 + radioCheckedClassName="text-[#595959]!" 70 + radioEmptyClassName="text-[#969696]!" 71 + type="radio" 72 + id="default" 73 + name="page-width-options" 74 + value="default" 75 + checked={selectedPreset === "default"} 76 + onChange={(e) => { 77 + if (!e.currentTarget.checked) return; 78 + setSelectedPreset("default"); 79 + setPageWidth(624); 80 + }} 81 + > 82 + <div 83 + className={`w-full cursor-pointer ${selectedPreset === "default" ? "text-[#595959]" : "text-[#969696]"}`} 84 + > 85 + default (624px) 86 + </div> 87 + </Radio> 88 + </label> 89 + <label htmlFor="wide" className="w-full"> 90 + <Radio 91 + radioCheckedClassName="text-[#595959]!" 92 + radioEmptyClassName="text-[#969696]!" 93 + type="radio" 94 + id="wide" 95 + name="page-width-options" 96 + value="wide" 97 + checked={selectedPreset === "wide"} 98 + onChange={(e) => { 99 + if (!e.currentTarget.checked) return; 100 + setSelectedPreset("wide"); 101 + setPageWidth(756); 102 + }} 103 + > 104 + <div 105 + className={`w-full cursor-pointer ${selectedPreset === "wide" ? "text-[#595959]" : "text-[#969696]"}`} 106 + > 107 + wide (756px) 108 + </div> 109 + </Radio> 110 + </label> 111 + <label htmlFor="custom" className="pb-3 w-full"> 112 + <Radio 113 + type="radio" 114 + id="custom" 115 + name="page-width-options" 116 + value="custom" 117 + radioCheckedClassName="text-[#595959]!" 118 + radioEmptyClassName="text-[#969696]!" 119 + checked={selectedPreset === "custom"} 120 + onChange={(e) => { 121 + if (!e.currentTarget.checked) return; 122 + setSelectedPreset("custom"); 123 + if (selectedPreset !== "custom") { 124 + setPageWidth(currentValue); 125 + setInterimValue(currentValue); 126 + } 127 + }} 128 + > 129 + <div className="flex flex-col gap-2 w-full"> 130 + <div className="flex gap-2"> 131 + <div 132 + className={`shrink-0 grow-0 w-fit z-10 cursor-pointer ${selectedPreset === "custom" ? "text-[#595959]" : "text-[#969696]"}`} 133 + > 134 + custom 135 + </div> 136 + <div 137 + className={`flex font-normal ${selectedPreset === "custom" ? "text-[#969696]" : "text-[#C3C3C3]"}`} 138 + > 139 + <Input 140 + type="number" 141 + className="w-10 text-right appearance-none bg-transparent" 142 + max={max} 143 + min={min} 144 + value={interimValue} 145 + onChange={(e) => { 146 + setInterimValue(parseInt(e.currentTarget.value)); 147 + }} 148 + onKeyDown={(e) => { 149 + if (e.key === "Enter" || e.key === "Escape") { 150 + e.preventDefault(); 151 + let clampedValue = interimValue; 152 + if (!isNaN(interimValue)) { 153 + clampedValue = Math.max( 154 + min, 155 + Math.min(max, interimValue), 156 + ); 157 + setInterimValue(clampedValue); 158 + } 159 + setPageWidth(clampedValue); 160 + } 161 + }} 162 + onBlur={() => { 163 + let clampedValue = interimValue; 164 + if (!isNaN(interimValue)) { 165 + clampedValue = Math.max( 166 + min, 167 + Math.min(max, interimValue), 168 + ); 169 + setInterimValue(clampedValue); 170 + } 171 + setPageWidth(clampedValue); 172 + }} 173 + /> 174 + px 175 + </div> 176 + </div> 177 + <Slider.Root 178 + className={`relative grow flex items-center select-none touch-none w-full h-fit px-1`} 179 + value={[interimValue]} 180 + max={max} 181 + min={min} 182 + step={16} 183 + onValueChange={(value) => { 184 + setInterimValue(value[0]); 185 + }} 186 + onValueCommit={(value) => { 187 + setPageWidth(value[0]); 188 + }} 189 + > 190 + <Slider.Track 191 + className={`${selectedPreset === "custom" ? "bg-[#595959]" : "bg-[#C3C3C3]"} relative grow rounded-full h-[3px]`} 192 + /> 193 + <Slider.Thumb 194 + className={`flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 195 + ${selectedPreset === "custom" ? "bg-[#595959] shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]" : "bg-[#C3C3C3]"} 196 + `} 197 + aria-label="Max Page Width" 198 + /> 199 + </Slider.Root> 200 + </div> 201 + </Radio> 202 + </label> 203 + </div> 116 204 )} 117 205 </div> 118 206 </div>