From d7b4fbeba3da77600585a101859149d7d3935580 Mon Sep 17 00:00:00 2001 From: Shehan Peiris Date: Wed, 13 Nov 2024 11:58:31 +0530 Subject: [PATCH] Category Management Component --- description.css | 26 ++++++++ index.html | 84 ++++++++++++++++++++++++++ main.js | 153 ++++++++++++++++++++++++++++++++++++++++++++++++ style.css | 45 ++++++++++++++ 4 files changed, 308 insertions(+) create mode 100644 description.css create mode 100644 index.html create mode 100644 main.js create mode 100644 style.css diff --git a/description.css b/description.css new file mode 100644 index 0000000..e839ca6 --- /dev/null +++ b/description.css @@ -0,0 +1,26 @@ +.project-container { + + margin: 20px auto; + padding: 20px; + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); + border-radius: 8px; + background-color: #f8f9fa; +} + +.project-header { + color: #0d6efd; +} + +.feature-item { + margin-bottom: 15px; +} + +.btn-clone { + margin-top: 20px; +} + +.screenshot { + max-width: 100%; + border-radius: 8px; + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..e310138 --- /dev/null +++ b/index.html @@ -0,0 +1,84 @@ + + + + + + + Add Category Component + + + + + + + + +
+

Add Category

+
+ + +
+
+ +
+ + +

Add Sub Category for:

+
+ + +
+
+
+ +
+
+

Category and Subcategory Management Component

+

A Bootstrap-based UI component for managing categories and their respective sub-categories + with + options to add, select, and delete, plus keyboard shortcuts for quick actions.

+ +

Features

+
    +
  • Add Categories: Enter a category name and add + it to the list.
  • +
  • Select and Add Subcategories: Select a + category to enable adding related sub-categories.
  • +
  • Move Deleted Subcategories: If a category is + deleted, its sub-categories are moved to the default "Others" section.
  • +
  • Quick Actions: Each category and sub-category + tag has a cross (×) button for deletion.
  • +
  • Keyboard Support: Press "Enter" to quickly + submit category or sub-category input.
  • +
+ + +

Code Summary

+

This component includes:

+
    +
  • Category Input Field: For entering and + submitting new categories.
  • +
  • Category Tags: Displayed in a container with + each category selectable and removable.
  • +
  • Subcategory Input Field: Enabled only when a + category is selected.
  • +
  • Subcategory Tags: Displayed with delete + options and associated with the selected category.
  • +
+ + +
+
+ + + + + + + + \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..7d8f4d6 --- /dev/null +++ b/main.js @@ -0,0 +1,153 @@ +const addCategoryButton = document.getElementById("addCategoryButton"); +const categoryInput = document.getElementById("categoryInput"); +const categoriesContainer = document.getElementById("categoriesContainer"); +const subCategoryInput = document.getElementById("subCategoryInput"); +const addSubCategoryButton = document.getElementById("addSubCategoryButton"); +const subCategoriesContainer = document.getElementById("subCategoriesContainer"); +const selectedCategoryName = document.getElementById("selectedCategoryName"); + +let categories = {}; // Object to store categories and their sub-categories +let selectedCategory = null; // Track currently selected category + +addCategoryButton.addEventListener("click", addCategory); +addSubCategoryButton.addEventListener("click", addSubCategory); +categoryInput.addEventListener("keypress", (e) => { if (e.key === "Enter") addCategory(); }); +subCategoryInput.addEventListener("keypress", (e) => { if (e.key === "Enter") addSubCategory(); }); + +function addCategory() { + const categoryText = categoryInput.value.trim(); + if (categoryText && !categories[categoryText]) { // Avoid duplicates + categories[categoryText] = ["Others"]; // Initialize with default "Others" sub-category + + const categoryTag = document.createElement("span"); + categoryTag.classList.add("category-tag"); + categoryTag.textContent = categoryText; + + // Cross button for removing category + const removeBtn = document.createElement("span"); + removeBtn.classList.add("remove-btn"); + removeBtn.textContent = "×"; + removeBtn.addEventListener("click", (e) => { + e.stopPropagation(); + confirmCategoryDeletion(categoryText); + }); + categoryTag.appendChild(removeBtn); + + // Click event to select category + categoryTag.addEventListener("click", () => { + document.querySelectorAll(".category-tag").forEach(tag => { + tag.classList.remove("selected"); + }); + categoryTag.classList.add("selected"); + + selectedCategory = categoryText; // Set the selected category + selectedCategoryName.textContent = categoryText; // Display selected category name + + // Enable sub-category input and button + subCategoryInput.disabled = false; + addSubCategoryButton.disabled = false; + + displaySubCategories(); // Show its sub-categories + }); + + categoriesContainer.appendChild(categoryTag); + categoryInput.value = ""; + } +} + +function addSubCategory() { + const subCategoryText = subCategoryInput.value.trim(); + if (subCategoryText && selectedCategory && !categories[selectedCategory].includes(subCategoryText)) { + // Add sub-category to the selected category + categories[selectedCategory].push(subCategoryText); + subCategoryInput.value = ""; + displaySubCategories(); // Refresh sub-categories display + } +} + +function displaySubCategories() { + // Clear the current sub-categories display + subCategoriesContainer.innerHTML = ""; + + // Display sub-categories for the selected category + categories[selectedCategory].forEach(subCategory => { + const subCategoryTag = document.createElement("span"); + subCategoryTag.classList.add("subcategory-tag"); + subCategoryTag.textContent = subCategory; + + // Add "Others" class if it's the default category + if (subCategory === "Others") { + subCategoryTag.classList.add("others-tag"); + } + + // Cross button for removing sub-category + const removeBtn = document.createElement("span"); + removeBtn.classList.add("remove-btn"); + removeBtn.textContent = "×"; + removeBtn.addEventListener("click", () => { + moveToOthers(subCategory); + }); + subCategoryTag.appendChild(removeBtn); + + subCategoriesContainer.appendChild(subCategoryTag); + }); +} + +function confirmCategoryDeletion(category) { + const confirmDelete = confirm(`If you delete the category "${category}", all sub-categories will move to "Others". Are you sure?`); + if (confirmDelete) { + // Move sub-categories to "Others" before deletion + categories["Others"] = (categories["Others"] || []).concat(categories[category].filter(sub => sub !== "Others")); + delete categories[category]; + if (selectedCategory === category) { + selectedCategory = null; // Clear selected category + selectedCategoryName.textContent = ""; // Clear display of selected category name + subCategoryInput.disabled = true; + addSubCategoryButton.disabled = true; + } + categoriesContainer.innerHTML = ""; // Clear all categories + subCategoriesContainer.innerHTML = ""; // Clear sub-categories display + renderCategories(); + } +} + +function moveToOthers(subCategory) { + if (selectedCategory && subCategory !== "Others") { + categories[selectedCategory] = categories[selectedCategory].filter(sub => sub !== subCategory); + if (!categories["Others"]) categories["Others"] = []; + categories["Others"].push(subCategory); + displaySubCategories(); + } +} + +function renderCategories() { + for (const category in categories) { + const categoryTag = document.createElement("span"); + categoryTag.classList.add("category-tag"); + categoryTag.textContent = category; + + // Cross button for each category + const removeBtn = document.createElement("span"); + removeBtn.classList.add("remove-btn"); + removeBtn.textContent = "×"; + removeBtn.addEventListener("click", (e) => { + e.stopPropagation(); + confirmCategoryDeletion(category); + }); + categoryTag.appendChild(removeBtn); + + categoryTag.addEventListener("click", () => { + document.querySelectorAll(".category-tag").forEach(tag => { + tag.classList.remove("selected"); + }); + categoryTag.classList.add("selected"); + selectedCategory = category; + selectedCategoryName.textContent = category; + subCategoryInput.disabled = false; + addSubCategoryButton.disabled = false; + displaySubCategories(); + }); + + categoriesContainer.appendChild(categoryTag); + } +} \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..3bc6b05 --- /dev/null +++ b/style.css @@ -0,0 +1,45 @@ +.addCategoryComp { + margin: 10px; + padding: 20px; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); + border-radius: 10px; + background-color: #f9f9f9; +} + +.category-tag, +.subcategory-tag { + display: inline-flex; + align-items: center; + padding: 5px 10px; + margin: 5px 5px 0 0; + border-radius: 15px; + font-weight: bold; + font-size: 0.9rem; + cursor: pointer; + background-color: #e7f1ff; + color: #0d6efd; +} + +.category-tag .remove-btn, +.subcategory-tag .remove-btn { + font-weight: bold; + font-size: 0.8rem; + color: #dc3545; + margin-left: 8px; + cursor: pointer; +} + +.category-tag.selected { + background-color: #0d6efd; + color: white; +} + +.subcategory-tag { + background-color: #f1f3f5; + color: #495057; +} + +.others-tag { + font-style: italic; + opacity: 0.8; +} \ No newline at end of file