Feature/v2/compare tool (#4751)

# Description of Changes

- Addition of the compare tool
- 
---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.

---------

Co-authored-by: James Brunton <[email protected]>
This commit is contained in:
EthanHealy01
2025-11-12 14:54:01 +00:00
committed by GitHub
co-authored by James Brunton
parent f22f697edc
commit a5e2b54274
49 changed files with 6651 additions and 81 deletions
@@ -93,13 +93,10 @@ export function ToastProvider({ children }: { children: React.ReactNode }) {
progress,
} as ToastInstance;
// Detect completion
// Detect completion but do not auto-flip to success.
// Callers (e.g., compare workbench) explicitly set alertType when done.
if (typeof progress === 'number' && progress >= 100 && !t.justCompleted) {
// On completion: finalize type as success unless explicitly provided otherwise
next.justCompleted = false;
if (!updates.alertType) {
next.alertType = 'success';
}
next.justCompleted = true;
}
return next;
@@ -31,6 +31,13 @@
flex-direction: column-reverse;
}
.toast-container--bottom-center {
bottom: 16px;
left: 50%;
transform: translateX(-50%);
flex-direction: column-reverse;
}
/* Toast Item Styles */
.toast-item {
min-width: 320px;
@@ -8,6 +8,7 @@ const locationToClass: Record<ToastLocation, string> = {
'top-right': 'toast-container--top-right',
'bottom-left': 'toast-container--bottom-left',
'bottom-right': 'toast-container--bottom-right',
'bottom-center': 'toast-container--bottom-center',
};
function getToastItemClass(t: ToastInstance): string {
@@ -44,7 +45,7 @@ export default function ToastRenderer() {
if (!acc[key]) acc[key] = [] as ToastInstance[];
acc[key].push(t);
return acc;
}, { 'top-left': [], 'top-right': [], 'bottom-left': [], 'bottom-right': [] });
}, { 'top-left': [], 'top-right': [], 'bottom-left': [], 'bottom-right': [], 'bottom-center': [] });
return (
<>
+17 -10
View File
@@ -1,4 +1,4 @@
import { ToastOptions } from '@app/components/toast/types';
import { ToastApi, ToastInstance, ToastOptions } from '@app/components/toast/types';
import { useToast, ToastProvider } from '@app/components/toast/ToastContext';
import ToastRenderer from '@app/components/toast/ToastRenderer';
@@ -7,18 +7,26 @@ export { useToast, ToastProvider, ToastRenderer };
// Global imperative API via module singleton
let _api: ReturnType<typeof createImperativeApi> | null = null;
type ToastContextApi = ToastApi & { toasts: ToastInstance[] };
function createImperativeApi() {
const subscribers: Array<(fn: any) => void> = [];
let api: any = null;
const subscribers: Array<(fn: ToastContextApi) => void> = [];
let api: ToastContextApi | null = null;
return {
provide(instance: any) {
provide(instance: ToastContextApi) {
api = instance;
subscribers.splice(0).forEach(cb => cb(api));
subscribers.splice(0).forEach(cb => cb(instance));
},
get(): ToastContextApi | null {
return api;
},
onReady(cb: (readyApi: ToastContextApi) => void) {
if (api) {
cb(api);
} else {
subscribers.push(cb);
}
},
get(): any | null { return api; },
onReady(cb: (api: any) => void) {
if (api) cb(api); else subscribers.push(cb);
}
};
}
@@ -58,4 +66,3 @@ export function dismissAllToasts() {
_api?.get()?.dismissAll();
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { ReactNode } from 'react';
export type ToastLocation = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
export type ToastLocation = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'bottom-center';
export type ToastAlertType = 'success' | 'error' | 'warning' | 'neutral';
export interface ToastOptions {