| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | /* | 
					
						
							|  |  |  | 	GoToSocial | 
					
						
							| 
									
										
										
										
											2023-03-12 18:49:06 +01:00
										 |  |  | 	Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | 	SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | 	it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  | 	the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | 	(at your option) any later version. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | 	GNU Affero General Public License for more details. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  | 	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-09 14:14:20 +02:00
										 |  |  | import React, { useRef } from "react"; | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import type { | 
					
						
							|  |  |  | 	ReactNode, | 
					
						
							|  |  |  | 	RefObject, | 
					
						
							|  |  |  | } from "react"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import type { | 
					
						
							|  |  |  | 	FileFormInputHook, | 
					
						
							| 
									
										
										
										
											2025-01-05 13:20:33 +01:00
										 |  |  | 	NumberFormInputHook, | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 	RadioFormInputHook, | 
					
						
							|  |  |  | 	TextFormInputHook, | 
					
						
							|  |  |  | } from "../../lib/form/types"; | 
					
						
							| 
									
										
										
										
											2024-05-07 19:48:12 +02:00
										 |  |  | import { nanoid } from "nanoid"; | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export interface TextInputProps extends React.DetailedHTMLProps< | 
					
						
							|  |  |  | 	React.InputHTMLAttributes<HTMLInputElement>, | 
					
						
							|  |  |  | 	HTMLInputElement | 
					
						
							|  |  |  | > { | 
					
						
							| 
									
										
										
										
											2024-05-05 13:47:22 +02:00
										 |  |  | 	label?: ReactNode; | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 	field: TextFormInputHook; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | export function TextInput({label, field, ...props}: TextInputProps) { | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 	const { onChange, value, ref } = field; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							| 
									
										
										
										
											2023-02-03 12:07:40 +01:00
										 |  |  | 		<div className={`form-field text${field.valid ? "" : " invalid"}`}> | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 			<label> | 
					
						
							|  |  |  | 				{label} | 
					
						
							|  |  |  | 				<input | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 					onChange={onChange} | 
					
						
							|  |  |  | 					value={value} | 
					
						
							|  |  |  | 					ref={ref as RefObject<HTMLInputElement>} | 
					
						
							|  |  |  | 					{...props} | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 				/> | 
					
						
							|  |  |  | 			</label> | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-05 13:20:33 +01:00
										 |  |  | export interface NumberInputProps extends React.DetailedHTMLProps< | 
					
						
							|  |  |  | 	React.InputHTMLAttributes<HTMLInputElement>, | 
					
						
							|  |  |  | 	HTMLInputElement | 
					
						
							|  |  |  | > { | 
					
						
							|  |  |  | 	label?: ReactNode; | 
					
						
							|  |  |  | 	field: NumberFormInputHook; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function NumberInput({label, field, ...props}: NumberInputProps) { | 
					
						
							|  |  |  | 	const { onChange, value, ref } = field; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className={`form-field number${field.valid ? "" : " invalid"}`}> | 
					
						
							|  |  |  | 			<label> | 
					
						
							|  |  |  | 				{label} | 
					
						
							|  |  |  | 				<input | 
					
						
							|  |  |  | 					onChange={onChange} | 
					
						
							|  |  |  | 					value={value} | 
					
						
							|  |  |  | 					ref={ref as RefObject<HTMLInputElement>} | 
					
						
							|  |  |  | 					{...props} | 
					
						
							|  |  |  | 				/> | 
					
						
							|  |  |  | 			</label> | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | export interface TextAreaProps extends React.DetailedHTMLProps< | 
					
						
							|  |  |  | 	React.TextareaHTMLAttributes<HTMLTextAreaElement>, | 
					
						
							|  |  |  | 	HTMLTextAreaElement | 
					
						
							|  |  |  | > { | 
					
						
							| 
									
										
										
										
											2024-05-05 13:47:22 +02:00
										 |  |  | 	label?: ReactNode; | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 	field: TextFormInputHook; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function TextArea({label, field, ...props}: TextAreaProps) { | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 	const { onChange, value, ref } = field; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className="form-field textarea"> | 
					
						
							|  |  |  | 			<label> | 
					
						
							|  |  |  | 				{label} | 
					
						
							|  |  |  | 				<textarea | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 					onChange={onChange} | 
					
						
							|  |  |  | 					value={value} | 
					
						
							|  |  |  | 					ref={ref as RefObject<HTMLTextAreaElement>} | 
					
						
							|  |  |  | 					{...props} | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 				/> | 
					
						
							|  |  |  | 			</label> | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | export interface FileInputProps extends React.DetailedHTMLProps< | 
					
						
							|  |  |  | 	React.InputHTMLAttributes<HTMLInputElement>, | 
					
						
							|  |  |  | 	HTMLInputElement | 
					
						
							|  |  |  | > { | 
					
						
							| 
									
										
										
										
											2024-05-05 13:47:22 +02:00
										 |  |  | 	label?: ReactNode; | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 	field: FileFormInputHook; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function FileInput({ label, field, ...props }: FileInputProps) { | 
					
						
							| 
									
										
										
										
											2025-04-09 14:14:20 +02:00
										 |  |  | 	const ref = useRef<HTMLInputElement>(null); | 
					
						
							|  |  |  | 	const { onChange, infoComponent } = field; | 
					
						
							| 
									
										
										
										
											2024-05-07 19:48:12 +02:00
										 |  |  | 	const id = nanoid(); | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className="form-field file"> | 
					
						
							| 
									
										
										
										
											2025-04-09 14:14:20 +02:00
										 |  |  | 			<label | 
					
						
							|  |  |  | 				className="label-wrapper" | 
					
						
							|  |  |  | 				htmlFor={id} | 
					
						
							|  |  |  | 				tabIndex={0} | 
					
						
							| 
									
										
										
										
											2025-04-14 15:12:21 +02:00
										 |  |  | 				onKeyDown={(e) => { | 
					
						
							|  |  |  | 					if (e.key === "Enter") { | 
					
						
							| 
									
										
										
										
											2025-05-06 08:06:52 +00:00
										 |  |  | 						ref.current?.click(); | 
					
						
							| 
									
										
										
										
											2025-04-14 15:12:21 +02:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				}} | 
					
						
							| 
									
										
										
										
											2025-04-09 14:14:20 +02:00
										 |  |  | 				role="button" | 
					
						
							|  |  |  | 			> | 
					
						
							|  |  |  | 				<div className="label-label"> | 
					
						
							|  |  |  | 					{label} | 
					
						
							|  |  |  | 				</div> | 
					
						
							|  |  |  | 				<div className="label-button"> | 
					
						
							|  |  |  | 					<div className="file-input button">Browse</div> | 
					
						
							|  |  |  | 				</div> | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 			</label> | 
					
						
							| 
									
										
										
										
											2024-05-07 19:48:12 +02:00
										 |  |  | 			<input | 
					
						
							|  |  |  | 				id={id} | 
					
						
							|  |  |  | 				type="file" | 
					
						
							|  |  |  | 				className="hidden" | 
					
						
							|  |  |  | 				onChange={onChange} | 
					
						
							| 
									
										
										
										
											2025-04-09 14:14:20 +02:00
										 |  |  | 				ref={ref} | 
					
						
							| 
									
										
										
										
											2024-05-07 19:48:12 +02:00
										 |  |  | 				{...props} | 
					
						
							|  |  |  | 			/> | 
					
						
							|  |  |  | 			{infoComponent} | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | export function Checkbox({ label, field, ...inputProps }) { | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 	const { onChange, value } = field; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className="form-field checkbox"> | 
					
						
							|  |  |  | 			<label> | 
					
						
							|  |  |  | 				<input | 
					
						
							|  |  |  | 					type="checkbox" | 
					
						
							|  |  |  | 					checked={value} | 
					
						
							|  |  |  | 					onChange={onChange} | 
					
						
							|  |  |  | 					{...inputProps} | 
					
						
							|  |  |  | 				/> {label} | 
					
						
							|  |  |  | 			</label> | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | export interface SelectProps extends React.DetailedHTMLProps< | 
					
						
							|  |  |  | 	React.SelectHTMLAttributes<HTMLSelectElement>, | 
					
						
							|  |  |  | 	HTMLSelectElement | 
					
						
							|  |  |  | > { | 
					
						
							| 
									
										
										
										
											2024-05-05 13:47:22 +02:00
										 |  |  | 	label?: ReactNode; | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 	field: TextFormInputHook; | 
					
						
							|  |  |  | 	children?: ReactNode; | 
					
						
							|  |  |  | 	options: React.JSX.Element; | 
					
						
							| 
									
										
										
										
											2024-07-17 16:46:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * Optional callback function that is | 
					
						
							|  |  |  | 	 * triggered along with the select's onChange. | 
					
						
							|  |  |  | 	 *  | 
					
						
							|  |  |  | 	 * _selectValue is the current value of | 
					
						
							|  |  |  | 	 * the select after onChange is triggered. | 
					
						
							|  |  |  | 	 *  | 
					
						
							|  |  |  | 	 * @param _selectValue  | 
					
						
							|  |  |  | 	 * @returns  | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	onChangeCallback?: (_selectValue: string | undefined) => void; | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-17 16:46:52 +02:00
										 |  |  | export function Select({ | 
					
						
							|  |  |  | 	label, | 
					
						
							|  |  |  | 	field, | 
					
						
							|  |  |  | 	children, | 
					
						
							|  |  |  | 	options, | 
					
						
							|  |  |  | 	onChangeCallback, | 
					
						
							|  |  |  | 	...props | 
					
						
							|  |  |  | }: SelectProps) { | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 	const { onChange, value, ref } = field; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className="form-field select"> | 
					
						
							|  |  |  | 			<label> | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 				{label} | 
					
						
							|  |  |  | 				{children} | 
					
						
							| 
									
										
										
										
											2024-08-21 10:43:43 +02:00
										 |  |  | 				<div className="select-wrapper"> | 
					
						
							|  |  |  | 					<select | 
					
						
							|  |  |  | 						onChange={(e: React.ChangeEvent<HTMLSelectElement>) => { | 
					
						
							|  |  |  | 							onChange(e); | 
					
						
							|  |  |  | 							if (onChangeCallback !== undefined) { | 
					
						
							|  |  |  | 								onChangeCallback(e.target.value); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						}} | 
					
						
							|  |  |  | 						value={value} | 
					
						
							|  |  |  | 						ref={ref as RefObject<HTMLSelectElement>} | 
					
						
							|  |  |  | 						{...props} | 
					
						
							|  |  |  | 					> | 
					
						
							|  |  |  | 						{options} | 
					
						
							|  |  |  | 					</select> | 
					
						
							|  |  |  | 				</div> | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 			</label> | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | export interface RadioGroupProps extends React.DetailedHTMLProps< | 
					
						
							|  |  |  | 	React.InputHTMLAttributes<HTMLInputElement>, | 
					
						
							|  |  |  | 	HTMLInputElement | 
					
						
							|  |  |  | > { | 
					
						
							| 
									
										
										
										
											2024-05-05 13:47:22 +02:00
										 |  |  | 	label?: ReactNode; | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 	field: RadioFormInputHook; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function RadioGroup({ label, field, ...props }: RadioGroupProps) { | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 	return ( | 
					
						
							|  |  |  | 		<div className="form-field radio"> | 
					
						
							|  |  |  | 			{Object.entries(field.options).map(([value, radioLabel]) => ( | 
					
						
							|  |  |  | 				<label key={value}> | 
					
						
							|  |  |  | 					<input | 
					
						
							|  |  |  | 						type="radio" | 
					
						
							|  |  |  | 						name={field.name} | 
					
						
							|  |  |  | 						value={value} | 
					
						
							|  |  |  | 						checked={field.value == value} | 
					
						
							|  |  |  | 						onChange={field.onChange} | 
					
						
							| 
									
										
										
										
											2023-10-17 12:46:06 +02:00
										 |  |  | 						{...props} | 
					
						
							| 
									
										
										
										
											2023-01-18 14:45:14 +01:00
										 |  |  | 					/> | 
					
						
							|  |  |  | 					{radioLabel} | 
					
						
							|  |  |  | 				</label> | 
					
						
							|  |  |  | 			))} | 
					
						
							|  |  |  | 			{label} | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | } |