# Description of Changes
- Migrated all dynamic right-rail controls into their owning views (file
editor, page editor, viewer) using dedicated helper hooks, so the rail
is
purely a renderer now.
- Tightened layout/animation: dynamic buttons grow in with a top-down
reveal
and center correctly.
---
## Checklist
### General
- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [x] 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)
- [x] I have performed a self-review of my own code
- [x] 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.
89 lines
3.0 KiB
TypeScript
89 lines
3.0 KiB
TypeScript
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
|
import { RightRailAction, RightRailButtonConfig } from '../types/rightRail';
|
|
|
|
interface RightRailContextValue {
|
|
buttons: RightRailButtonConfig[];
|
|
actions: Record<string, RightRailAction>;
|
|
allButtonsDisabled: boolean;
|
|
registerButtons: (buttons: RightRailButtonConfig[]) => void;
|
|
unregisterButtons: (ids: string[]) => void;
|
|
setAction: (id: string, action?: RightRailAction) => void;
|
|
setAllRightRailButtonsDisabled: (disabled: boolean) => void;
|
|
clear: () => void;
|
|
}
|
|
|
|
const RightRailContext = createContext<RightRailContextValue | undefined>(undefined);
|
|
|
|
export function RightRailProvider({ children }: { children: React.ReactNode }) {
|
|
const [buttons, setButtons] = useState<RightRailButtonConfig[]>([]);
|
|
const [actions, setActions] = useState<Record<string, RightRailAction>>({});
|
|
const [allButtonsDisabled, setAllButtonsDisabled] = useState<boolean>(false);
|
|
|
|
const registerButtons = useCallback((newButtons: RightRailButtonConfig[]) => {
|
|
setButtons(prev => {
|
|
const byId = new Map(prev.map(b => [b.id, b] as const));
|
|
newButtons.forEach(nb => {
|
|
const existing = byId.get(nb.id) || ({} as RightRailButtonConfig);
|
|
byId.set(nb.id, { ...existing, ...nb });
|
|
});
|
|
const merged = Array.from(byId.values());
|
|
merged.sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.id.localeCompare(b.id));
|
|
if (process.env.NODE_ENV === 'development') {
|
|
const ids = newButtons.map(b => b.id);
|
|
const dupes = ids.filter((id, idx) => ids.indexOf(id) !== idx);
|
|
if (dupes.length) console.warn('[RightRail] Duplicate ids in registerButtons:', dupes);
|
|
}
|
|
return merged;
|
|
});
|
|
}, []);
|
|
|
|
const unregisterButtons = useCallback((ids: string[]) => {
|
|
setButtons(prev => prev.filter(b => !ids.includes(b.id)));
|
|
setActions(prev => Object.fromEntries(Object.entries(prev).filter(([id]) => !ids.includes(id))));
|
|
}, []);
|
|
|
|
const setAction = useCallback((id: string, action?: RightRailAction) => {
|
|
setActions(prev => {
|
|
if (!action) {
|
|
if (!(id in prev)) return prev;
|
|
const next = { ...prev };
|
|
delete next[id];
|
|
return next;
|
|
}
|
|
return { ...prev, [id]: action };
|
|
});
|
|
}, []);
|
|
|
|
const setAllRightRailButtonsDisabled = useCallback((disabled: boolean) => {
|
|
setAllButtonsDisabled(disabled);
|
|
}, []);
|
|
|
|
const clear = useCallback(() => {
|
|
setButtons([]);
|
|
setActions({});
|
|
}, []);
|
|
|
|
const value = useMemo<RightRailContextValue>(() => ({
|
|
buttons,
|
|
actions,
|
|
allButtonsDisabled,
|
|
registerButtons,
|
|
unregisterButtons,
|
|
setAction,
|
|
setAllRightRailButtonsDisabled,
|
|
clear
|
|
}), [buttons, actions, allButtonsDisabled, registerButtons, unregisterButtons, setAction, setAllRightRailButtonsDisabled, clear]);
|
|
|
|
return (
|
|
<RightRailContext.Provider value={value}>
|
|
{children}
|
|
</RightRailContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useRightRail() {
|
|
const ctx = useContext(RightRailContext);
|
|
if (!ctx) throw new Error('useRightRail must be used within RightRailProvider');
|
|
return ctx;
|
|
}
|