feat: Enhanced What's New feature with NON_TOOLS category and global footer
✨ What's New Feature & Navigation Improvements: - Added attractive 'What's New' button to homepage with gradient design and sparkle effects - Created NON_TOOLS category for better navigation organization (Home, What's New) - Separated navigation items in sidebar and mobile menu with clear visual hierarchy - Implemented unified global footer across all pages for consistency 🎨 Design Enhancements: - Stunning gradient button with indigo→purple→pink colors and hover animations - Perfect placement between stats and tools grid for maximum visibility - Consistent indigo-purple theming for non-tools category - Professional sparkle effects and scale transforms on hover 🔧 Technical Improvements: - Removed duplicate footer from Terms of Service page - Unified footer implementation reduces code duplication - Enhanced mobile dropdown with proper NON_TOOLS separation - Updated sidebar with category-based styling and separators 📁 Files Modified: - /src/config/tools.js - Added NON_TOOLS category and What's New entry - /src/components/ToolSidebar.js - Separated NON_TOOLS with visual hierarchy - /src/components/Layout.js - Updated mobile menu and implemented global footer - /src/pages/Home.js - Added attractive What's New button with animations - /src/pages/TermsOfService.js - Removed duplicate footer - /src/pages/ReleaseNotes.js - Updated with latest implementation details
This commit is contained in:
@@ -51,6 +51,8 @@ const TableEditor = () => {
|
||||
const [originalFileName, setOriginalFileName] = useState(""); // For export naming
|
||||
const [isTableFullscreen, setIsTableFullscreen] = useState(false); // For fullscreen table view
|
||||
const [frozenColumns, setFrozenColumns] = useState(0); // Number of columns to freeze on horizontal scroll
|
||||
const [columnWidths, setColumnWidths] = useState({}); // Store custom column widths
|
||||
const [resizing, setResizing] = useState(null); // Track which column is being resized
|
||||
const [showClearConfirmModal, setShowClearConfirmModal] = useState(false); // For clear confirmation modal
|
||||
const [showInputChangeModal, setShowInputChangeModal] = useState(false); // For input method change confirmation
|
||||
const [pendingTabChange, setPendingTabChange] = useState(null); // Store pending tab change
|
||||
@@ -1162,6 +1164,40 @@ const TableEditor = () => {
|
||||
setData([...data, newRow]);
|
||||
};
|
||||
|
||||
// Column resize functions
|
||||
const getColumnWidth = (columnId) => {
|
||||
return columnWidths[columnId] || 150; // Default width
|
||||
};
|
||||
|
||||
const handleResizeStart = (e, columnId) => {
|
||||
e.preventDefault();
|
||||
const startX = e.clientX;
|
||||
const startWidth = getColumnWidth(columnId);
|
||||
|
||||
setResizing({ columnId, startX, startWidth });
|
||||
|
||||
const handleMouseMove = (e) => {
|
||||
if (!resizing && resizing?.columnId === columnId) return;
|
||||
|
||||
const deltaX = e.clientX - startX;
|
||||
const newWidth = Math.max(50, startWidth + deltaX); // Minimum width of 50px
|
||||
|
||||
setColumnWidths(prev => ({
|
||||
...prev,
|
||||
[columnId]: newWidth
|
||||
}));
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
setResizing(null);
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
|
||||
// Add new column
|
||||
const addColumn = () => {
|
||||
const newColumnId = `col_${Date.now()}`;
|
||||
@@ -2185,11 +2221,12 @@ const TableEditor = () => {
|
||||
<thead className="bg-gray-50 dark:bg-gray-700 sticky top-0 z-10">
|
||||
<tr>
|
||||
<th
|
||||
className={`px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider ${
|
||||
className={`px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border-r border-gray-200 dark:border-gray-600 ${
|
||||
frozenColumns > 0
|
||||
? "sticky left-0 z-20 bg-blue-50 dark:!bg-blue-900 w-12"
|
||||
: "w-12"
|
||||
? "sticky left-0 z-20 bg-blue-50 dark:!bg-blue-900"
|
||||
: ""
|
||||
}`}
|
||||
style={{ width: '40px', maxWidth: '40px', minWidth: '40px' }}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -2214,24 +2251,23 @@ const TableEditor = () => {
|
||||
{columns.map((column, index) => {
|
||||
const isFrozen = index < frozenColumns;
|
||||
const leftOffset = isFrozen
|
||||
? 45 + index * 150 // 45px for checkbox column + 150px per frozen column (no gap)
|
||||
? 40 + columns.slice(0, index).reduce((acc, col) => acc + getColumnWidth(col.id), 0)
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<th
|
||||
key={column.id}
|
||||
className={`px-4 py-3 text-left text-sm font-medium text-gray-500 dark:text-gray-300 tracking-wider hover:bg-gray-100 dark:hover:bg-gray-600 ${
|
||||
className={`relative px-4 py-3 text-left text-sm font-medium text-gray-500 dark:text-gray-300 tracking-wider hover:bg-gray-100 dark:hover:bg-gray-600 border-r border-gray-200 dark:border-gray-600 ${
|
||||
isFrozen
|
||||
? "sticky z-20 bg-blue-50 dark:!bg-blue-900 min-w-[150px]"
|
||||
: "min-w-0"
|
||||
? "sticky z-20 bg-blue-50 dark:!bg-blue-900"
|
||||
: ""
|
||||
}`}
|
||||
style={
|
||||
isFrozen
|
||||
? {
|
||||
left: `${leftOffset}px`,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
style={{
|
||||
width: `${getColumnWidth(column.id)}px`,
|
||||
minWidth: `${getColumnWidth(column.id)}px`,
|
||||
maxWidth: `${getColumnWidth(column.id)}px`,
|
||||
...(isFrozen ? { left: `${leftOffset}px` } : {})
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -2287,6 +2323,13 @@ const TableEditor = () => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Resize handle */}
|
||||
<div
|
||||
className="absolute right-0 top-0 bottom-0 w-1 cursor-col-resize hover:bg-blue-500 hover:w-1.5 transition-all z-30"
|
||||
onMouseDown={(e) => handleResizeStart(e, column.id)}
|
||||
title="Drag to resize column"
|
||||
/>
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
@@ -2310,11 +2353,12 @@ const TableEditor = () => {
|
||||
className="hover:bg-gray-50 dark:hover:bg-gray-700"
|
||||
>
|
||||
<td
|
||||
className={`px-4 py-3 ${
|
||||
className={`px-4 py-3 border-r border-gray-200 dark:border-gray-600 ${
|
||||
frozenColumns > 0
|
||||
? "sticky left-0 z-10 bg-blue-50 dark:!bg-blue-900 w-12"
|
||||
: "w-12"
|
||||
? "sticky left-0 z-10 bg-blue-50 dark:!bg-blue-900"
|
||||
: ""
|
||||
}`}
|
||||
style={{ width: '40px', maxWidth: '40px', minWidth: '40px' }}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -2334,24 +2378,23 @@ const TableEditor = () => {
|
||||
{columns.map((column, index) => {
|
||||
const isFrozen = index < frozenColumns;
|
||||
const leftOffset = isFrozen
|
||||
? 45 + index * 150 // 45px for checkbox column + 150px per frozen column (no gap)
|
||||
? 40 + columns.slice(0, index).reduce((acc, col) => acc + getColumnWidth(col.id), 0)
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<td
|
||||
key={column.id}
|
||||
className={`px-4 py-3 text-sm text-gray-900 dark:text-gray-100 ${
|
||||
className={`px-4 py-3 text-sm text-gray-900 dark:text-gray-100 border-r border-gray-200 dark:border-gray-600 break-words ${
|
||||
isFrozen
|
||||
? "sticky z-10 bg-blue-50 dark:!bg-blue-900 min-w-[150px]"
|
||||
? "sticky z-10 bg-blue-50 dark:!bg-blue-900"
|
||||
: ""
|
||||
}`}
|
||||
style={
|
||||
isFrozen
|
||||
? {
|
||||
left: `${leftOffset}px`,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
style={{
|
||||
width: `${getColumnWidth(column.id)}px`,
|
||||
minWidth: `${getColumnWidth(column.id)}px`,
|
||||
maxWidth: `${getColumnWidth(column.id)}px`,
|
||||
...(isFrozen ? { left: `${leftOffset}px` } : {})
|
||||
}}
|
||||
>
|
||||
{editingCell?.rowId === row.id &&
|
||||
editingCell?.columnId === column.id ? (
|
||||
@@ -2478,24 +2521,19 @@ const TableEditor = () => {
|
||||
|
||||
{/* System Row - Add Row */}
|
||||
<tr className="border-t-2 border-dashed border-gray-300 dark:border-gray-600">
|
||||
{/* Sticky Add Row button on the left */}
|
||||
<td className="sticky left-0 bg-white dark:bg-gray-800 z-20 py-4 px-4">
|
||||
<td
|
||||
colSpan={columns.length + 1}
|
||||
className="py-4 px-4 relative"
|
||||
>
|
||||
<button
|
||||
onClick={addRow}
|
||||
className="flex items-center justify-center gap-2 text-gray-500 hover:text-blue-600 px-3 py-2 rounded-lg transition-colors group whitespace-nowrap"
|
||||
className="flex items-center justify-center gap-2 text-gray-500 hover:text-blue-600 px-3 py-2 rounded-lg transition-colors group whitespace-nowrap sticky left-4"
|
||||
title="Add new row"
|
||||
>
|
||||
<Plus className="h-4 w-4 group-hover:scale-110 transition-transform" />
|
||||
<span className="text-sm font-medium">Add Row</span>
|
||||
</button>
|
||||
</td>
|
||||
{/* Empty cells to fill the rest of the row */}
|
||||
<td
|
||||
colSpan={columns.length + 1}
|
||||
className="py-4"
|
||||
>
|
||||
{/* Empty space for visual consistency */}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user