mirror of
https://github.com/tabler/tabler.git
synced 2026-06-17 12:50:03 +04:00
feat: Add four new modals (new task, edit profile, confirm delete, change password) (#2529)
This commit is contained in:
@@ -80,7 +80,7 @@
|
||||
<!-- BEGIN NAVBAR NAV -->
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" data-bs-toggle="offcanvas" data-bs-target="#offcanvasSettings">
|
||||
<a class="nav-link" href="#" data-bs-toggle="offcanvas" data-bs-target="#offcanvas-settings">
|
||||
<span class="badge badge-sm bg-red text-red-fg">New</span>
|
||||
<span class="nav-link-icon d-md-none d-lg-inline-block">
|
||||
{% include "ui/icon.html" icon="settings" %}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="password-modal-label">Change password</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="password-current">Current password</label>
|
||||
{% include "ui/form/input-group.html" type="password" id="password-current" placeholder="Enter your current password" append-button="eye:Show password" flat=true %}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="password-new">New password</label>
|
||||
{% include "ui/form/input-group.html" type="password" id="password-new" placeholder="Enter new password" append-button="eye:Show password" flat=true %}
|
||||
<small class="form-hint">
|
||||
Your password must be 8-20 characters long, contain letters and numbers, and must not contain
|
||||
spaces, special characters, or emoji.
|
||||
</small>
|
||||
<div class="mt-2">
|
||||
<div class="progress" style="height: 4px;">
|
||||
<div class="progress-bar" id="password-strength" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div class="text-secondary text-xs mt-1" id="password-strength-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="password-confirm">Confirm new password</label>
|
||||
{% include "ui/form/input-group.html" type="password" id="password-confirm" placeholder="Confirm your new password" append-button="eye:Show password" flat=true %}
|
||||
<div class="invalid-feedback d-none" id="password-match-error">
|
||||
Passwords do not match.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include "ui/button.html" type="submit" text="Update password" color="primary" block=true class="mt-4" %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% capture_script %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
function setupPasswordToggle(inputId) {
|
||||
const input = document.getElementById(inputId);
|
||||
if (!input) return;
|
||||
|
||||
const inputGroup = input.closest('.input-group');
|
||||
if (!inputGroup) return;
|
||||
|
||||
const toggleLink = inputGroup.querySelector('a.link-secondary');
|
||||
if (!toggleLink) return;
|
||||
|
||||
toggleLink.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const isPassword = input.type === 'password';
|
||||
input.type = isPassword ? 'text' : 'password';
|
||||
|
||||
// Update tooltip text
|
||||
const tooltipText = isPassword ? 'Hide password' : 'Show password';
|
||||
this.setAttribute('title', tooltipText);
|
||||
this.setAttribute('data-bs-original-title', tooltipText);
|
||||
|
||||
// Update icon (simple approach - toggle classes if needed)
|
||||
const svg = this.querySelector('svg');
|
||||
if (svg) {
|
||||
const use = svg.querySelector('use');
|
||||
if (use) {
|
||||
use.setAttribute('href', isPassword ? '#icon-eye-off' : '#icon-eye');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupPasswordToggle('password-current');
|
||||
setupPasswordToggle('password-new');
|
||||
setupPasswordToggle('password-confirm');
|
||||
|
||||
const newPasswordInput = document.getElementById('password-new');
|
||||
const strengthBar = document.getElementById('password-strength');
|
||||
const strengthText = document.getElementById('password-strength-text');
|
||||
|
||||
if (newPasswordInput && strengthBar && strengthText) {
|
||||
newPasswordInput.addEventListener('input', function() {
|
||||
const password = this.value;
|
||||
let strength = 0;
|
||||
let strengthLabel = '';
|
||||
|
||||
if (password.length >= 8) strength++;
|
||||
if (password.length >= 12) strength++;
|
||||
if (/[a-z]/.test(password) && /[A-Z]/.test(password)) strength++;
|
||||
if (/\d/.test(password)) strength++;
|
||||
if (/[^a-zA-Z0-9]/.test(password)) strength++;
|
||||
|
||||
const percentage = (strength / 5) * 100;
|
||||
strengthBar.style.width = percentage + '%';
|
||||
|
||||
if (strength <= 2) {
|
||||
strengthBar.className = 'progress-bar bg-danger';
|
||||
strengthLabel = 'Weak';
|
||||
} else if (strength <= 3) {
|
||||
strengthBar.className = 'progress-bar bg-warning';
|
||||
strengthLabel = 'Fair';
|
||||
} else if (strength <= 4) {
|
||||
strengthBar.className = 'progress-bar bg-info';
|
||||
strengthLabel = 'Good';
|
||||
} else {
|
||||
strengthBar.className = 'progress-bar bg-success';
|
||||
strengthLabel = 'Strong';
|
||||
}
|
||||
|
||||
strengthText.textContent = password ? strengthLabel : '';
|
||||
});
|
||||
}
|
||||
|
||||
const confirmPasswordInput = document.getElementById('password-confirm');
|
||||
const matchError = document.getElementById('password-match-error');
|
||||
|
||||
if (newPasswordInput && confirmPasswordInput && matchError) {
|
||||
function validateMatch() {
|
||||
const newPassword = newPasswordInput.value;
|
||||
const confirmPassword = confirmPasswordInput.value;
|
||||
|
||||
if (confirmPassword && newPassword !== confirmPassword) {
|
||||
confirmPasswordInput.classList.add('is-invalid');
|
||||
matchError.classList.remove('d-none');
|
||||
} else {
|
||||
confirmPasswordInput.classList.remove('is-invalid');
|
||||
matchError.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
newPasswordInput.addEventListener('input', validateMatch);
|
||||
confirmPasswordInput.addEventListener('input', validateMatch);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endcapture_script %}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{% include "ui/modal/close.html" %}
|
||||
<div class="modal-status bg-danger"></div>
|
||||
<div class="modal-body text-center py-4">
|
||||
{% include "ui/icon.html" icon="alert-triangle" color="danger" size="lg" class="mb-2" %}
|
||||
|
||||
<h3 id="confirm-delete-title">Are you sure?</h3>
|
||||
<div class="text-secondary mb-4">
|
||||
<p id="confirm-delete-message">Do you really want to delete this item? This action cannot be undone.</p>
|
||||
<div id="confirm-delete-items" class="text-start d-none">
|
||||
<div class="card card-sm mt-3">
|
||||
<div class="card-body">
|
||||
<div class="fw-bold mb-2">Items to be deleted:</div>
|
||||
<ul class="list-unstyled mb-0" id="confirm-delete-list">
|
||||
<li>• Item 1</li>
|
||||
<li>• Item 2</li>
|
||||
<li>• Item 3</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="form-check text-start">
|
||||
<input class="form-check-input" type="checkbox" id="confirm-delete-checkbox">
|
||||
<span class="form-check-label">I understand this action cannot be undone</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="w-100">
|
||||
<div class="row">
|
||||
<div class="col">{% include "ui/button.html" dismiss=true text="Cancel" block=true %}</div>
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-danger w-100" data-bs-dismiss="modal" id="confirm-delete-button" disabled>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% capture_script %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const checkbox = document.getElementById("confirm-delete-checkbox");
|
||||
const deleteButton = document.getElementById("confirm-delete-button");
|
||||
|
||||
if (checkbox && deleteButton) {
|
||||
checkbox.addEventListener("change", function() {
|
||||
deleteButton.disabled = !this.checked;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endcapture_script %}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="profile-modal-label">Edit profile</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="mb-4">
|
||||
<div class="row align-items-end">
|
||||
<div class="col-auto">
|
||||
{% include "ui/avatar-upload.html" class="rounded" size="lg" %}
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="form-label">Avatar</label>
|
||||
<div class="text-secondary">Click to upload a new avatar</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="profile-first-name">First name</label>
|
||||
<input class="form-control" id="profile-first-name" type="text" placeholder="John">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="profile-last-name">Last name</label>
|
||||
<input class="form-control" id="profile-last-name" type="text" placeholder="Doe">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="profile-email">Email</label>
|
||||
<input class="form-control" id="profile-email" type="email" placeholder="john.doe@example.com">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="profile-phone">Phone</label>
|
||||
<input class="form-control" id="profile-phone" type="tel" placeholder="+1 (555) 123-4567">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="profile-bio">Bio</label>
|
||||
<textarea class="form-control" id="profile-bio" rows="4" placeholder="Tell us about yourself..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="profile-location">Location</label>
|
||||
<input class="form-control" id="profile-location" type="text" placeholder="City, Country">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="profile-birthdate">Date of birth</label>
|
||||
{% include "ui/datepicker.html" layout="icon" id="profile-birthdate" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Social links</label>
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">{% include "ui/icon.html" icon="brand-twitter" %}</span>
|
||||
<input type="text" class="form-control" placeholder="twitter.com/username">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">{% include "ui/icon.html" icon="brand-github" %}</span>
|
||||
<input type="text" class="form-control" placeholder="github.com/username">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">{% include "ui/icon.html" icon="brand-linkedin" %}</span>
|
||||
<input type="text" class="form-control" placeholder="linkedin.com/in/username">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include "ui/button.html" type="submit" text="Save changes" color="primary" block=true class="mt-4" %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="event-modal-label">New event</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="event-title">Title</label>
|
||||
<input class="form-control" id="event-title" type="text" placeholder="Event title">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="event-description">Description</label>
|
||||
<textarea class="form-control" id="event-description" rows="3" placeholder="Event description"></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="event-start">Start</label>
|
||||
{% include "ui/datepicker.html" layout="icon" id="event-start" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="event-end">End</label>
|
||||
{% include "ui/datepicker.html" layout="icon" id="event-end" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "ui/button.html" type="submit" text="Create event" color="primary" block=true class="mt-4" %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="task-modal-label">New task</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="task-name">Task name</label>
|
||||
<input class="form-control" id="task-name" type="text" placeholder="Enter task name">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="task-description">Description</label>
|
||||
<textarea class="form-control" id="task-description" rows="3" placeholder="Enter task description"></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="task-assigned">Assigned to</label>
|
||||
{% include "ui/select.html" key="people" id="task-assigned" indicator="avatar" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="task-priority">Priority</label>
|
||||
<select class="form-select" id="task-priority">
|
||||
<option value="low">Low</option>
|
||||
<option value="medium" selected>Medium</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="task-due-date">Due date</label>
|
||||
{% include "ui/datepicker.html" layout="icon" id="task-due-date" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="task-category">Category / Tags</label>
|
||||
{% include "ui/select.html" key="tags" id="task-category" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "ui/button.html" type="submit" text="Create task" color="primary" block=true class="mt-4" %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="modal-body">
|
||||
<div class="row mb-3 align-items-end">
|
||||
<div class="col-auto">
|
||||
{% include "ui/avatar-upload.html" class="rounded" %}
|
||||
{% include "ui/avatar-upload.html" class="rounded" size="xl" %}
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="form-label">Name</label>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<div class="settings">
|
||||
<a href="#" class="btn btn-floating btn-icon btn-primary" data-bs-toggle="offcanvas" data-bs-target="#offcanvasSettings" aria-controls="offcanvasSettings" aria-label="Theme Settings">
|
||||
<a href="#" class="btn btn-floating btn-icon btn-primary" data-bs-toggle="offcanvas" data-bs-target="#offcanvas-settings" aria-controls="offcanvas-settings" aria-label="Theme Settings">
|
||||
{% include "ui/icon.html" icon="brush" %}
|
||||
</a>
|
||||
|
||||
<form class="offcanvas offcanvas-start offcanvas-narrow" tabindex="-1" id="offcanvasSettings" role="dialog" aria-modal="true" aria-labelledby="offcanvasSettingsTitle">
|
||||
<form class="offcanvas offcanvas-start offcanvas-narrow" tabindex="-1" id="offcanvas-settings" role="dialog" aria-modal="true" aria-labelledby="offcanvas-settings-title">
|
||||
<div class="offcanvas-header">
|
||||
<h2 class="offcanvas-title" id="offcanvasSettingsTitle">Theme Settings</h2>
|
||||
<h2 class="offcanvas-title" id="offcanvas-settings-title">Theme Settings</h2>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body d-flex flex-column">
|
||||
@@ -124,7 +124,7 @@
|
||||
}
|
||||
|
||||
var url = new URL(window.location)
|
||||
var form = document.getElementById("offcanvasSettings")
|
||||
var form = document.getElementById("offcanvas-settings")
|
||||
var resetButton = document.getElementById("reset-changes")
|
||||
|
||||
var checkItems = function () {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{% assign inline = include.inline | default: false %}
|
||||
|
||||
{% capture_modal inline %}
|
||||
<div class="modal modal-blur fade{% if include.class %} {{ include.class }}{% endif %}" {% if include.style %} style="{{ include.style }}"{% endif %} id="modal-{{ modal-id }}" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal modal-blur fade{% if include.class %} {{ include.class }}{% endif %}{% if include.show %} show{% endif %}" {% if include.style %} style="{{ include.style }}"{% endif %} id="modal-{{ modal-id }}" tabindex="-1" role="dialog"{% if include.show %} aria-hidden="false"{% else %} aria-hidden="true"{% endif %}>
|
||||
<div class="modal-dialog{% if size%} modal-{{ size }}{% endif %}{% unless include.top %} modal-dialog-centered{% endunless %}{% if include.scrollable %} modal-dialog-scrollable{% endif %}" role="document">
|
||||
<div class="modal-content">
|
||||
{% include "parts/modals/{{ modal-id }}.html" %}
|
||||
|
||||
Reference in New Issue
Block a user