| 
									
										
										
										
											2025-08-10 18:56:37 +03:00
										 |  |  | import { Dropdown as BootstrapDropdown } from "bootstrap"; | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  | import { ComponentChildren } from "preact"; | 
					
						
							|  |  |  | import Icon from "./Icon"; | 
					
						
							| 
									
										
										
										
											2025-08-10 20:24:20 +03:00
										 |  |  | import { useEffect, useMemo, useRef, type CSSProperties } from "preact/compat"; | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | interface FormListOpts { | 
					
						
							|  |  |  |     children: ComponentChildren; | 
					
						
							|  |  |  |     onSelect?: (value: string) => void; | 
					
						
							| 
									
										
										
										
											2025-08-06 16:16:30 +03:00
										 |  |  |     style?: CSSProperties; | 
					
						
							| 
									
										
										
										
											2025-08-10 20:24:20 +03:00
										 |  |  |     fullHeight?: boolean; | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-10 20:24:20 +03:00
										 |  |  | export default function FormList({ children, onSelect, style, fullHeight }: FormListOpts) { | 
					
						
							| 
									
										
										
										
											2025-08-10 18:56:37 +03:00
										 |  |  |     const wrapperRef = useRef<HTMLDivElement | null>(null); | 
					
						
							|  |  |  |     const triggerRef = useRef<HTMLButtonElement | null>(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     useEffect(() => { | 
					
						
							|  |  |  |         if (!triggerRef.current || !wrapperRef.current) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         const $wrapperRef = $(wrapperRef.current); | 
					
						
							|  |  |  |         const dropdown = BootstrapDropdown.getOrCreateInstance(triggerRef.current); | 
					
						
							|  |  |  |         $wrapperRef.on("hide.bs.dropdown", (e) => e.preventDefault()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return () => { | 
					
						
							|  |  |  |             $wrapperRef.off("hide.bs.dropdown"); | 
					
						
							|  |  |  |             dropdown.dispose(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }, [ triggerRef, wrapperRef ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-10 20:24:20 +03:00
										 |  |  |     const builtinStyles = useMemo(() => { | 
					
						
							|  |  |  |         const style: CSSProperties = {}; | 
					
						
							|  |  |  |         if (fullHeight) { | 
					
						
							|  |  |  |             style.height = "100%"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return style; | 
					
						
							|  |  |  |     }, [ fullHeight ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  |     return ( | 
					
						
							| 
									
										
										
										
											2025-08-10 20:24:20 +03:00
										 |  |  |         <div className="dropdownWrapper" ref={wrapperRef} style={builtinStyles}> | 
					
						
							|  |  |  |             <div className="dropdown" style={builtinStyles}> | 
					
						
							| 
									
										
										
										
											2025-08-10 18:56:37 +03:00
										 |  |  |                 <button | 
					
						
							|  |  |  |                     ref={triggerRef} | 
					
						
							|  |  |  |                     type="button" style="display: none;" | 
					
						
							|  |  |  |                     data-bs-toggle="dropdown" data-bs-display="static"> | 
					
						
							|  |  |  |                 </button> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 <div class="dropdown-menu static show" style={{ | 
					
						
							|  |  |  |                     ...style ?? {}, | 
					
						
							| 
									
										
										
										
											2025-08-10 20:24:20 +03:00
										 |  |  |                     ...builtinStyles, | 
					
						
							| 
									
										
										
										
											2025-08-10 18:56:37 +03:00
										 |  |  |                     position: "relative", | 
					
						
							|  |  |  |                 }} onClick={(e) => { | 
					
						
							|  |  |  |                     const value = (e.target as HTMLElement)?.dataset?.value; | 
					
						
							|  |  |  |                     if (value && onSelect) { | 
					
						
							|  |  |  |                         onSelect(value); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }}> | 
					
						
							|  |  |  |                     {children} | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |             </div> | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  |         </div> | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-21 20:16:06 +03:00
										 |  |  | export interface FormListBadge { | 
					
						
							|  |  |  |     className?: string; | 
					
						
							|  |  |  |     text: string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  | interface FormListItemOpts { | 
					
						
							| 
									
										
										
										
											2025-08-06 12:12:37 +03:00
										 |  |  |     children: ComponentChildren; | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  |     icon?: string; | 
					
						
							|  |  |  |     value?: string; | 
					
						
							| 
									
										
										
										
											2025-08-06 16:16:30 +03:00
										 |  |  |     title?: string; | 
					
						
							| 
									
										
										
										
											2025-08-10 17:53:45 +03:00
										 |  |  |     active?: boolean; | 
					
						
							| 
									
										
										
										
											2025-08-21 20:16:06 +03:00
										 |  |  |     badges?: FormListBadge[]; | 
					
						
							| 
									
										
										
										
											2025-08-21 20:30:12 +03:00
										 |  |  |     disabled?: boolean; | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-21 20:30:12 +03:00
										 |  |  | export function FormListItem({ children, icon, value, title, active, badges, disabled }: FormListItemOpts) { | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  |     return ( | 
					
						
							| 
									
										
										
										
											2025-08-10 18:56:37 +03:00
										 |  |  |         <a | 
					
						
							| 
									
										
										
										
											2025-08-21 20:30:12 +03:00
										 |  |  |             class={`dropdown-item ${active ? "active" : ""} ${disabled ? "disabled" : ""}`} | 
					
						
							| 
									
										
										
										
											2025-08-10 18:56:37 +03:00
										 |  |  |             data-value={value} title={title} | 
					
						
							|  |  |  |             tabIndex={0} | 
					
						
							|  |  |  |         > | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  |             <Icon icon={icon} />  | 
					
						
							| 
									
										
										
										
											2025-08-06 12:12:37 +03:00
										 |  |  |             {children} | 
					
						
							| 
									
										
										
										
											2025-08-21 20:16:06 +03:00
										 |  |  |             {badges && badges.map(({ className, text }) => ( | 
					
						
							|  |  |  |                 <span className={`badge ${className ?? ""}`}>{text}</span> | 
					
						
							|  |  |  |             ))} | 
					
						
							| 
									
										
										
										
											2025-08-06 11:39:31 +03:00
										 |  |  |         </a> | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface FormListHeaderOpts { | 
					
						
							|  |  |  |     text: string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function FormListHeader({ text }: FormListHeaderOpts) { | 
					
						
							|  |  |  |     return ( | 
					
						
							|  |  |  |         <li> | 
					
						
							|  |  |  |             <h6 className="dropdown-header">{text}</h6> | 
					
						
							|  |  |  |         </li> | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-08-21 20:30:12 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function FormDivider() { | 
					
						
							|  |  |  |     return <div className="dropdown-divider" />; | 
					
						
							|  |  |  | } |