13if (!defined(
'XOOPS_ROOT_PATH')) {
16function smarty_function_message_suggestlist($params, &$smarty)
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';
24 $root = XCube_Root::getSingleton();
25 $db = $root->mController->getDB();
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);
38 $usersJson = json_encode($users);
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>';
49 .autocomplete-container {
52 max-width: '.($size * 10).
'px;
57 border: 1px solid red;
59 box-sizing: border-box;
61 .autocomplete-results {
63 border: 1px solid #212121;
71 background-color: #000;
78 .autocomplete-item:hover {
79 background-color: #212121;
81 .autocomplete-active {
82 background-color: #272727;
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;
94 // Function to display matching results
95 function showResults(value) {
96 // Clear previous results
97 resultsElement.innerHTML = "";
98 resultsElement.style.display = "none";
100 if (!value) return false;
102 const matchingUsers = users.filter(user =>
103 user.toLowerCase().indexOf(value.toLowerCase()) > -1
106 if (matchingUsers.length > 0) {
107 resultsElement.style.display = "block";
109 matchingUsers.forEach(user => {
110 const item = document.createElement("div");
111 item.className = "autocomplete-item";
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);
119 item.innerHTML = beforeMatch + "<strong>" + match + "</strong>" + afterMatch;
122 item.addEventListener("click", function() {
123 inputElement.value = user;
124 resultsElement.style.display = "none";
127 resultsElement.appendChild(item);
132 // Input event listener
133 inputElement.addEventListener("input", function() {
134 showResults(this.value);
137 // Focus event listener
138 inputElement.addEventListener("focus", function() {
139 if (this.value) showResults(this.value);
142 // Blur event listener - delay hiding to allow for clicks
143 inputElement.addEventListener("blur", function() {
144 setTimeout(function() {
145 resultsElement.style.display = "none";
150 inputElement.addEventListener("keydown", function(e) {
151 const items = resultsElement.getElementsByClassName("autocomplete-item");
153 if (e.keyCode === 40) { // Down arrow
157 } else if (e.keyCode === 38) { // Up arrow
161 } else if (e.keyCode === 13) { // Enter
163 if (currentFocus > -1 && items[currentFocus]) {
164 items[currentFocus].click();
170 function setActive(items) {
171 if (!items.length) return;
173 // Remove active class from all items
174 for (let i = 0; i < items.length; i++) {
175 items[i].classList.remove("autocomplete-active");
178 // Adjust currentFocus if out of bounds
179 if (currentFocus >= items.length) currentFocus = 0;
180 if (currentFocus < 0) currentFocus = items.length - 1;
182 // Add active class to current item
183 items[currentFocus].classList.add("autocomplete-active");
185 // Scroll to active item if needed
186 items[currentFocus].scrollIntoView({ block: "nearest" });