mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-20 05:10:26 -05:00
ac74218fc9
Replace the separate plus and bolt floating controls with a single Actions menu inside #fabDock, driven by app/static/floating-actions.js. The dock stacks Actions, optional team chat, and AI Helper using shared CSS variables for spacing; the AI control is a circular FAB matching the other buttons. Move the chat widget panel to a fixed viewport overlay so dock z-index no longer paints controls over the open panel, and lift the panel bottom when the admin version banner or mobile bottom nav applies. Fade non-actions dock children while the actions menu is open (fab-dock--menu-open). Update README.md, docs/UI_GUIDELINES.md, and the advanced-features implementation summaries so contributors describe the floating hub instead of global-fab.js. Keep app/static/quick-actions.js aligned with the retired mount pattern for any remaining references.
119 lines
3.0 KiB
JavaScript
119 lines
3.0 KiB
JavaScript
/**
|
|
* Floating Actions Hub
|
|
* Controls the single bottom-right actions menu.
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
function getDock() {
|
|
return document.getElementById('fabDock');
|
|
}
|
|
|
|
function getRoot() {
|
|
return document.getElementById('unifiedActionsRoot');
|
|
}
|
|
|
|
function getButton() {
|
|
return document.getElementById('unifiedActionsFab');
|
|
}
|
|
|
|
function getMenu() {
|
|
return document.getElementById('unifiedActionsMenu');
|
|
}
|
|
|
|
function getUrl(name, fallback) {
|
|
var dock = getDock();
|
|
return (dock && dock.getAttribute(name)) || fallback;
|
|
}
|
|
|
|
function setOpen(open) {
|
|
var root = getRoot();
|
|
var button = getButton();
|
|
var menu = getMenu();
|
|
var dock = getDock();
|
|
if (!root || !button || !menu) return;
|
|
|
|
root.classList.toggle('is-open', open);
|
|
menu.classList.toggle('hidden', !open);
|
|
menu.setAttribute('aria-hidden', open ? 'false' : 'true');
|
|
button.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
if (dock) {
|
|
dock.classList.toggle('fab-dock--menu-open', open);
|
|
}
|
|
}
|
|
|
|
function close() {
|
|
setOpen(false);
|
|
}
|
|
|
|
function toggle() {
|
|
var root = getRoot();
|
|
if (!root) return;
|
|
setOpen(!root.classList.contains('is-open'));
|
|
}
|
|
|
|
function startTimer() {
|
|
close();
|
|
var startButton = document.querySelector('#openStartTimer');
|
|
if (startButton) {
|
|
startButton.click();
|
|
return;
|
|
}
|
|
|
|
var dashboard = getUrl('data-dashboard-url', '/');
|
|
window.location.href = dashboard.split('#')[0] + '#start-timer';
|
|
}
|
|
|
|
function navigateTo(attr, fallback) {
|
|
close();
|
|
window.location.href = getUrl(attr, fallback);
|
|
}
|
|
|
|
function runAction(action) {
|
|
if (action === 'start') {
|
|
startTimer();
|
|
} else if (action === 'log') {
|
|
navigateTo('data-manual-entry-url', '/timer/manual_entry');
|
|
} else if (action === 'task') {
|
|
navigateTo('data-new-task-url', '/tasks/create');
|
|
} else if (action === 'project') {
|
|
navigateTo('data-new-project-url', '/projects/create');
|
|
} else if (action === 'client') {
|
|
navigateTo('data-new-client-url', '/clients/create');
|
|
} else if (action === 'reports') {
|
|
navigateTo('data-reports-url', '/reports/');
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
var root = getRoot();
|
|
var button = getButton();
|
|
var menu = getMenu();
|
|
if (!root || !button || !menu) return;
|
|
|
|
button.addEventListener('click', function (event) {
|
|
event.stopPropagation();
|
|
toggle();
|
|
});
|
|
|
|
menu.querySelectorAll('[data-action]').forEach(function (item) {
|
|
item.addEventListener('click', function () {
|
|
runAction(item.getAttribute('data-action'));
|
|
});
|
|
});
|
|
|
|
document.addEventListener('click', function (event) {
|
|
if (!root.classList.contains('is-open')) return;
|
|
if (root.contains(event.target)) return;
|
|
close();
|
|
});
|
|
|
|
document.addEventListener('keydown', function (event) {
|
|
if (event.key === 'Escape' && root.classList.contains('is-open')) {
|
|
close();
|
|
button.focus();
|
|
}
|
|
});
|
|
});
|
|
})();
|