feat: add Bates numbering tool with PDF processing capabilities
- Implemented bates-numbering-page.ts for handling Bates numbering logic. - Created a new HTML page for Bates numbering functionality. - Added style presets and file handling for multiple PDF uploads. - Integrated user interface elements for file selection, style customization, and preview. - Enhanced main.ts to support collapsible categories and compact mode for tool grid. - Updated types for Bates numbering in bates-numbering-type.ts. - Registered the new tool in tools.html and updated routing in vite.config.ts.
This commit is contained in:
103
src/js/main.ts
103
src/js/main.ts
@@ -255,19 +255,78 @@ const init = async () => {
|
||||
if (dom.toolGrid) {
|
||||
dom.toolGrid.textContent = '';
|
||||
|
||||
let collapsedCategories: string[] = [];
|
||||
try {
|
||||
const stored = localStorage.getItem('collapsedCategories');
|
||||
if (stored) collapsedCategories = JSON.parse(stored);
|
||||
} catch {
|
||||
localStorage.removeItem('collapsedCategories');
|
||||
}
|
||||
|
||||
function saveCollapsedCategories() {
|
||||
localStorage.setItem(
|
||||
'collapsedCategories',
|
||||
JSON.stringify(collapsedCategories)
|
||||
);
|
||||
}
|
||||
|
||||
categories.forEach((category) => {
|
||||
const categoryGroup = document.createElement('div');
|
||||
categoryGroup.className = 'category-group col-span-full';
|
||||
|
||||
const title = document.createElement('h2');
|
||||
title.className =
|
||||
'text-xl font-bold text-indigo-400 mb-4 mt-8 first:mt-0 text-white';
|
||||
const header = document.createElement('button');
|
||||
header.className = 'category-header';
|
||||
header.type = 'button';
|
||||
|
||||
const title = document.createElement('span');
|
||||
const categoryKey = categoryTranslationKeys[category.name];
|
||||
title.textContent = categoryKey ? t(categoryKey) : category.name;
|
||||
|
||||
const chevron = document.createElement('i');
|
||||
chevron.setAttribute('data-lucide', 'chevron-down');
|
||||
chevron.className =
|
||||
'category-chevron w-5 h-5 text-gray-400 transition-transform duration-300';
|
||||
|
||||
header.append(title, chevron);
|
||||
|
||||
const toolsContainer = document.createElement('div');
|
||||
toolsContainer.className =
|
||||
'grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 md:gap-6';
|
||||
'category-tools grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 md:gap-6';
|
||||
|
||||
const isCollapsed = collapsedCategories.includes(category.name);
|
||||
if (isCollapsed) {
|
||||
categoryGroup.classList.add('collapsed');
|
||||
toolsContainer.style.maxHeight = '0px';
|
||||
}
|
||||
|
||||
toolsContainer.addEventListener('transitionend', (e) => {
|
||||
if ((e as TransitionEvent).propertyName !== 'max-height') return;
|
||||
if (!categoryGroup.classList.contains('collapsed')) {
|
||||
toolsContainer.style.maxHeight = 'none';
|
||||
toolsContainer.style.overflow = 'visible';
|
||||
}
|
||||
});
|
||||
|
||||
header.addEventListener('click', () => {
|
||||
const collapsed = categoryGroup.classList.toggle('collapsed');
|
||||
if (collapsed) {
|
||||
toolsContainer.style.maxHeight = toolsContainer.scrollHeight + 'px';
|
||||
toolsContainer.style.overflow = 'hidden';
|
||||
requestAnimationFrame(() => {
|
||||
toolsContainer.style.maxHeight = '0px';
|
||||
});
|
||||
if (!collapsedCategories.includes(category.name)) {
|
||||
collapsedCategories.push(category.name);
|
||||
}
|
||||
} else {
|
||||
toolsContainer.style.overflow = 'hidden';
|
||||
toolsContainer.style.maxHeight = toolsContainer.scrollHeight + 'px';
|
||||
collapsedCategories = collapsedCategories.filter(
|
||||
(n) => n !== category.name
|
||||
);
|
||||
}
|
||||
saveCollapsedCategories();
|
||||
});
|
||||
|
||||
category.tools.forEach((tool) => {
|
||||
let toolCard: HTMLDivElement | HTMLAnchorElement;
|
||||
@@ -312,8 +371,13 @@ const init = async () => {
|
||||
toolsContainer.appendChild(toolCard);
|
||||
});
|
||||
|
||||
categoryGroup.append(title, toolsContainer);
|
||||
categoryGroup.append(header, toolsContainer);
|
||||
dom.toolGrid.appendChild(categoryGroup);
|
||||
|
||||
if (!isCollapsed) {
|
||||
toolsContainer.style.maxHeight = 'none';
|
||||
toolsContainer.style.overflow = 'visible';
|
||||
}
|
||||
});
|
||||
|
||||
const searchBar = document.getElementById('search-bar');
|
||||
@@ -547,6 +611,35 @@ const init = async () => {
|
||||
});
|
||||
}
|
||||
|
||||
const compactModeToggle = document.getElementById(
|
||||
'compact-mode-toggle'
|
||||
) as HTMLInputElement;
|
||||
|
||||
const savedCompactMode = localStorage.getItem('compactMode') === 'true';
|
||||
if (compactModeToggle) {
|
||||
compactModeToggle.checked = savedCompactMode;
|
||||
}
|
||||
applyCompactMode(savedCompactMode);
|
||||
|
||||
function applyCompactMode(enabled: boolean) {
|
||||
if (dom.toolGrid) {
|
||||
dom.toolGrid.classList.toggle('compact-mode', enabled);
|
||||
dom.toolGrid
|
||||
.querySelectorAll('.category-group:not(.collapsed) .category-tools')
|
||||
.forEach((container) => {
|
||||
(container as HTMLElement).style.maxHeight = 'none';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (compactModeToggle) {
|
||||
compactModeToggle.addEventListener('change', (e) => {
|
||||
const enabled = (e.target as HTMLInputElement).checked;
|
||||
localStorage.setItem('compactMode', enabled.toString());
|
||||
applyCompactMode(enabled);
|
||||
});
|
||||
}
|
||||
|
||||
// Shortcuts UI Handlers
|
||||
if (dom.openShortcutsBtn) {
|
||||
dom.openShortcutsBtn.addEventListener('click', () => {
|
||||
|
||||
Reference in New Issue
Block a user