a tool for shared writing and social publishing
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};