XCL Web Application Platform 2.5.0
The XoopsCube Legacy Project
Loading...
Searching...
No Matches
function.message_suggestlist.php
1<?php
12
13if (!defined('XOOPS_ROOT_PATH')) {
14 exit();
15}
16function smarty_function_message_suggestlist($params, &$smarty)
17{
18 $name = isset($params['name']) ? trim($params['name']) : 'uname';
19 $size = isset($params['size']) ? (int)$params['size'] : 30;
20 $username = isset($params['uname']) ? trim($params['uname']) : '';
21 $id = isset($params['id']) ? trim($params['id']) : 'user-autocomplete';
22 $placeholder = isset($params['placeholder']) ? trim($params['placeholder']) : 'Type a username';
23
24 $root = XCube_Root::getSingleton();
25 $db = $root->mController->getDB();
26
27 // Get users data
28 $users = [];
29 $sql = 'SELECT `uname` FROM `' . $db->prefix('users') . '` ';
30 $sql.= 'WHERE `uid` <> ' . $root->mContext->mXoopsUser->get('uid') . ' ';
31 $sql.= 'ORDER BY `uname`';
32 $result = $db->query($sql);
33 while ([$uname] = $db->fetchRow($result)) {
34 $users[] = htmlspecialchars($uname, ENT_QUOTES);
35 }
36
37 // JSON encode the users array for JavaScript
38 $usersJson = json_encode($users);
39
40 // Create the HTML structure
41 $html = '<div class="autocomplete-container">';
42 $html .= '<input id="'.$id.'" type="text" name="'.$name.'" value="'.htmlspecialchars($username, ENT_QUOTES).'"
43 autocomplete="off" size="'.$size.'" placeholder="'.$placeholder.'" class="user-input">';
44 $html .= '<div id="'.$id.'-results" class="autocomplete-results"></div>';
45 $html .= '</div>';
46
47 // Add CSS for styling
48 $html .= '<style>
49 .autocomplete-container {
50 position: relative;
51 width: 100%;
52 max-width: '.($size * 10).'px;
53 }
54 .user-input {
55 width: 100%;
56 padding: 8px;
57 border: 1px solid red;
58 border-radius: 4px;
59 box-sizing: border-box;
60 }
61 .autocomplete-results {
62 position: absolute;
63 border: 1px solid #212121;
64 border-top: none;
65 z-index: 99;
66 top: 100%;
67 left: 0;
68 right: 0;
69 max-height: 200px;
70 overflow-y: auto;
71 background-color: #000;
72 display: none;
73 }
74 .autocomplete-item {
75 padding: 8px;
76 cursor: pointer;
77 }
78 .autocomplete-item:hover {
79 background-color: #212121;
80 }
81 .autocomplete-active {
82 background-color: #272727;
83 }
84 </style>';
85
86 // Add JavaScript for autocomplete functionality
87 $html .= '<script>
88 document.addEventListener("DOMContentLoaded", function() {
89 const users = '.$usersJson.';
90 const inputElement = document.getElementById("'.$id.'");
91 const resultsElement = document.getElementById("'.$id.'-results");
92 let currentFocus = -1;
93
94 // Function to display matching results
95 function showResults(value) {
96 // Clear previous results
97 resultsElement.innerHTML = "";
98 resultsElement.style.display = "none";
99
100 if (!value) return false;
101
102 const matchingUsers = users.filter(user =>
103 user.toLowerCase().indexOf(value.toLowerCase()) > -1
104 );
105
106 if (matchingUsers.length > 0) {
107 resultsElement.style.display = "block";
108
109 matchingUsers.forEach(user => {
110 const item = document.createElement("div");
111 item.className = "autocomplete-item";
112
113 // Highlight the matching part
114 const matchIndex = user.toLowerCase().indexOf(value.toLowerCase());
115 const beforeMatch = user.substring(0, matchIndex);
116 const match = user.substring(matchIndex, matchIndex + value.length);
117 const afterMatch = user.substring(matchIndex + value.length);
118
119 item.innerHTML = beforeMatch + "<strong>" + match + "</strong>" + afterMatch;
120
121 // Set click event
122 item.addEventListener("click", function() {
123 inputElement.value = user;
124 resultsElement.style.display = "none";
125 });
126
127 resultsElement.appendChild(item);
128 });
129 }
130 }
131
132 // Input event listener
133 inputElement.addEventListener("input", function() {
134 showResults(this.value);
135 });
136
137 // Focus event listener
138 inputElement.addEventListener("focus", function() {
139 if (this.value) showResults(this.value);
140 });
141
142 // Blur event listener - delay hiding to allow for clicks
143 inputElement.addEventListener("blur", function() {
144 setTimeout(function() {
145 resultsElement.style.display = "none";
146 }, 200);
147 });
148
149 // Key navigation
150 inputElement.addEventListener("keydown", function(e) {
151 const items = resultsElement.getElementsByClassName("autocomplete-item");
152
153 if (e.keyCode === 40) { // Down arrow
154 currentFocus++;
155 setActive(items);
156 e.preventDefault();
157 } else if (e.keyCode === 38) { // Up arrow
158 currentFocus--;
159 setActive(items);
160 e.preventDefault();
161 } else if (e.keyCode === 13) { // Enter
162 e.preventDefault();
163 if (currentFocus > -1 && items[currentFocus]) {
164 items[currentFocus].click();
165 }
166 }
167 });
168
169 // Set active item
170 function setActive(items) {
171 if (!items.length) return;
172
173 // Remove active class from all items
174 for (let i = 0; i < items.length; i++) {
175 items[i].classList.remove("autocomplete-active");
176 }
177
178 // Adjust currentFocus if out of bounds
179 if (currentFocus >= items.length) currentFocus = 0;
180 if (currentFocus < 0) currentFocus = items.length - 1;
181
182 // Add active class to current item
183 items[currentFocus].classList.add("autocomplete-active");
184
185 // Scroll to active item if needed
186 items[currentFocus].scrollIntoView({ block: "nearest" });
187 }
188 });
189 </script>';
190
191 echo $html;
192}