Feat: Server Props Search

Feat: Add custom prop adds
Feat: Add Removing Props
This commit is contained in:
MCHost
2025-06-26 02:39:42 -04:00
parent 728ea10e80
commit 375d1400d6
2 changed files with 416 additions and 245 deletions

View File

@ -42,6 +42,8 @@
--spacing: 0.25rem;
--container-md: 28rem;
--container-xl: 36rem;
--text-xs: 0.75rem;
--text-xs--line-height: calc(1 / 0.75);
--text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875);
--text-lg: 1.125rem;
@ -224,6 +226,9 @@
.inset-0 {
inset: calc(var(--spacing) * 0);
}
.z-50 {
z-index: 50;
}
.container {
width: 100%;
@media (width >= 40rem) {
@ -251,6 +256,9 @@
.mt-4 {
margin-top: calc(var(--spacing) * 4);
}
.mr-2 {
margin-right: calc(var(--spacing) * 2);
}
.mb-1 {
margin-bottom: calc(var(--spacing) * 1);
}
@ -278,6 +286,9 @@
.inline-block {
display: inline-block;
}
.table {
display: table;
}
.h-24 {
height: calc(var(--spacing) * 24);
}
@ -296,9 +307,15 @@
.min-h-full {
min-height: 100%;
}
.w-1 {
width: calc(var(--spacing) * 1);
}
.w-1\/3 {
width: calc(1/3 * 100%);
}
.w-2 {
width: calc(var(--spacing) * 2);
}
.w-2\/3 {
width: calc(2/3 * 100%);
}
@ -320,9 +337,15 @@
.flex-1 {
flex: 1;
}
.flex-shrink {
flex-shrink: 1;
}
.flex-grow {
flex-grow: 1;
}
.border-collapse {
border-collapse: collapse;
}
.transform {
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
}
@ -518,6 +541,10 @@
font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height));
}
.text-xs {
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
}
.leading-relaxed {
--tw-leading: var(--leading-relaxed);
line-height: var(--leading-relaxed);
@ -552,6 +579,9 @@
.text-white {
color: var(--color-white);
}
.underline {
text-decoration-line: underline;
}
.opacity-50 {
opacity: 50%;
}
@ -559,6 +589,10 @@
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.outline {
outline-style: var(--tw-outline-style);
outline-width: 1px;
}
.filter {
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
}
@ -634,6 +668,13 @@
}
}
}
.hover\:text-red-700 {
&:hover {
@media (hover: hover) {
color: var(--color-red-700);
}
}
}
.sm\:w-\[90\%\] {
@media (width >= 40rem) {
width: 90%;
@ -1188,6 +1229,11 @@
inherits: false;
initial-value: 0 0 #0000;
}
@property --tw-outline-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@property --tw-blur {
syntax: "*";
inherits: false;
@ -1296,6 +1342,7 @@
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-offset-shadow: 0 0 #0000;
--tw-outline-style: solid;
--tw-blur: initial;
--tw-brightness: initial;
--tw-contrast: initial;

View File

@ -1451,6 +1451,15 @@ document.addEventListener('DOMContentLoaded', () => {
searchContainer.appendChild(searchLabel);
searchContainer.appendChild(searchInput);
// Add custom property button (mini style)
const addButton = document.createElement('button');
addButton.textContent = 'Add Property';
addButton.type = 'button';
addButton.className = 'mt-2 bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded text-xs';
addButton.addEventListener('click', showCustomPropertyForm);
searchContainer.appendChild(addButton);
fieldsContainer.appendChild(searchContainer);
// Add input event listener
@ -1480,32 +1489,147 @@ document.addEventListener('DOMContentLoaded', () => {
elements.searchInput.value = '';
renderPropertiesList(displayProperties, '');
elements.editPropertiesModal.classList.add('hidden');
hideCustomPropertyForm();
});
closeButton.dataset.closeHandlerAdded = 'true';
}
}
function showCustomPropertyForm() {
let formContainer = elements.propertiesFields.querySelector('#customPropertyForm');
if (formContainer) {
formContainer.remove();
}
formContainer = document.createElement('div');
formContainer.id = 'customPropertyForm';
formContainer.className = 'mb-4 p-4 bg-gray-800 rounded';
const form = document.createElement('form');
form.addEventListener('submit', (e) => e.preventDefault());
const keyInput = document.createElement('input');
keyInput.type = 'text';
keyInput.placeholder = 'Property name';
keyInput.className = 'bg-gray-700 px-4 py-2 rounded text-white w-full mb-2';
const valueInput = document.createElement('input');
valueInput.type = 'text';
valueInput.placeholder = 'Property value';
valueInput.className = 'bg-gray-700 px-4 py-2 rounded text-white w-full mb-2';
const addButton = document.createElement('button');
addButton.type = 'button';
addButton.textContent = 'Add';
addButton.className = 'bg-green-600 hover:bg-green-700 text-white px-2 py-1 rounded text-xs mr-2';
addButton.addEventListener('click', () => {
const key = keyInput.value.trim();
const value = valueInput.value.trim();
if (key && value) {
displayProperties[key] = value;
allProperties[key] = value;
renderPropertiesList(displayProperties, elements.searchInput.value);
formContainer.remove();
} else {
showNotification('Please enter both property name and value', 'error', 'custom-property-error');
}
});
const cancelButton = document.createElement('button');
cancelButton.type = 'button';
cancelButton.textContent = 'Cancel';
cancelButton.className = 'bg-gray-600 hover:bg-gray-700 text-white px-2 py-1 rounded text-xs';
cancelButton.addEventListener('click', hideCustomPropertyForm);
form.appendChild(keyInput);
form.appendChild(valueInput);
form.appendChild(addButton);
form.appendChild(cancelButton);
formContainer.appendChild(form);
elements.propertiesFields.insertBefore(formContainer, elements.propertiesFields.querySelector('#propertiesList'));
}
function hideCustomPropertyForm() {
const formContainer = elements.propertiesFields.querySelector('#customPropertyForm');
if (formContainer) {
formContainer.remove();
}
}
function showDeleteConfirmationModal(key) {
const modal = document.createElement('div');
modal.id = 'deleteConfirmationModal';
modal.className = 'absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
const modalContent = document.createElement('div');
modalContent.className = 'bg-gray-800 p-6 rounded-lg max-w-md w-full';
const header = document.createElement('h3');
header.className = 'text-lg font-medium text-white mb-4';
header.textContent = 'Confirm Deletion';
const message = document.createElement('p');
message.className = 'text-white mb-6';
message.textContent = `Are you sure you want to delete the property "${key}"? This action cannot be undone.`;
const buttonContainer = document.createElement('div');
buttonContainer.className = 'flex justify-end space-x-2';
const cancelButton = document.createElement('button');
cancelButton.type = 'button';
cancelButton.className = 'bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded';
cancelButton.textContent = 'Cancel';
cancelButton.addEventListener('click', () => modal.remove());
const deleteButton = document.createElement('button');
deleteButton.type = 'button';
deleteButton.className = 'bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded';
deleteButton.textContent = 'Delete';
deleteButton.addEventListener('click', () => {
delete displayProperties[key];
delete allProperties[key];
renderPropertiesList(displayProperties, elements.searchInput.value);
modal.remove();
showNotification(`Property "${key}" deleted`, 'success', 'delete-property-success');
});
buttonContainer.appendChild(cancelButton);
buttonContainer.appendChild(deleteButton);
modalContent.appendChild(header);
modalContent.appendChild(message);
modalContent.appendChild(buttonContainer);
modal.appendChild(modalContent);
elements.editPropertiesModal.appendChild(modal);
}
function renderPropertiesList(properties, filter = '') {
const propertiesList = elements.propertiesFields.querySelector('#propertiesList');
propertiesList.innerHTML = '';
// Filter properties based on search input
const filteredProperties = Object.entries(properties).filter(([key]) =>
key.toLowerCase().includes(filter.toLowerCase())
);
// Render filtered properties
filteredProperties.forEach(([key, value]) => {
if (filteredSettings.includes(key)) {
return;
}
console.log(`Rendering field for ${key}: ${value}`); // Debug log
console.log(`Rendering field for ${key}: ${value}`);
const fieldDiv = document.createElement('div');
fieldDiv.className = 'flex items-center space-x-2';
fieldDiv.style.display = 'flex';
const deleteButton = document.createElement('button');
deleteButton.type = 'button';
deleteButton.className = 'text-red-500 hover:text-red-700';
deleteButton.innerHTML = '✕';
deleteButton.title = `Delete ${key}`;
deleteButton.addEventListener('click', () => showDeleteConfirmationModal(key));
let inputType = 'text';
let isBoolean = value.toLowerCase() === 'true' || value.toLowerCase() === 'false';
if (isBoolean) {
@ -1520,7 +1644,6 @@ document.addEventListener('DOMContentLoaded', () => {
label.setAttribute('for', `prop-${key}`);
if (inputType === 'switch') {
// Hidden text input for accessibility and form association
const hiddenInput = document.createElement('input');
hiddenInput.type = 'text';
hiddenInput.id = `prop-${key}`;
@ -1556,7 +1679,6 @@ document.addEventListener('DOMContentLoaded', () => {
switchHandle.style.position = 'absolute';
switchHandle.style.zIndex = '11';
// Handle click and keyboard events
const toggleSwitch = () => {
const currentValue = hiddenInput.value === 'true';
const newValue = !currentValue;
@ -1576,6 +1698,7 @@ document.addEventListener('DOMContentLoaded', () => {
switchContainer.appendChild(switchTrack);
switchContainer.appendChild(switchHandle);
fieldDiv.appendChild(deleteButton);
fieldDiv.appendChild(label);
fieldDiv.appendChild(hiddenInput);
fieldDiv.appendChild(switchContainer);
@ -1584,11 +1707,12 @@ document.addEventListener('DOMContentLoaded', () => {
input.id = `prop-${key}`;
input.name = key;
input.className = 'bg-gray-700 px-4 py-2 rounded text-white w-2/3';
input.type = inputType;
input.type = 'number';
input.value = value;
if (inputType === 'number') {
input.min = '0';
}
fieldDiv.appendChild(deleteButton);
fieldDiv.appendChild(label);
fieldDiv.appendChild(input);
}
@ -1635,7 +1759,7 @@ document.addEventListener('DOMContentLoaded', () => {
const key = `action-save-properties`;
const notification = showNotification('Saving server properties...', 'loading', key);
const properties = {};
const inputs = elements.propertiesFields.querySelectorAll('input:not(#propertiesSearch)');
const inputs = elements.propertiesFields.querySelectorAll('input:not(#propertiesSearch):not([id="customPropertyKey"]):not([id="customPropertyValue"])');
// Collect modified properties
inputs.forEach(input => {