mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 20:00:15 +01:00
* fix: ReDoS in ChatGPT Import * ci: should correctly process citations from real ChatGPT data * ci: Add ReDoS vulnerability test for processAssistantMessage * refactor: Update thread management and citation handling * refactor(validateImageRequest): robust validation * refactor(Prompt.js): update name search regex to escape special characters * refactor(Preset): exclude user from preset update to prevent mass assignment * refactor(files.js): Improve file deletion process * ci: updated validateImageRequest.spec.js * a11y: plugin pagination * refactor(CreatePromptForm.tsx): Improve input field styling * chore(Prompts): typing and accessibility * fix: prompt creation access role check * chore: remove duplicate jsdocs
118 lines
3.3 KiB
TypeScript
118 lines
3.3 KiB
TypeScript
import React from 'react';
|
|
import { useLocalize } from '~/hooks';
|
|
|
|
type TPluginPaginationProps = {
|
|
currentPage: number;
|
|
maxPage: number;
|
|
onChangePage: (page: number) => void;
|
|
};
|
|
|
|
const PluginPagination: React.FC<TPluginPaginationProps> = ({
|
|
currentPage,
|
|
maxPage,
|
|
onChangePage,
|
|
}) => {
|
|
const localize = useLocalize();
|
|
const pages = [...Array(maxPage).keys()].map((i) => i + 1);
|
|
|
|
const handlePageChange = (page: number) => {
|
|
if (page < 1 || page > maxPage) {
|
|
return;
|
|
}
|
|
onChangePage(page);
|
|
};
|
|
|
|
return (
|
|
<div className="flex gap-2 text-sm text-black/60 dark:text-white/70">
|
|
<div
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-label="Previous page"
|
|
onClick={() => handlePageChange(currentPage - 1)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
onChangePage(currentPage - 1);
|
|
}
|
|
}}
|
|
className={`flex cursor-default items-center text-sm ${
|
|
currentPage === 1
|
|
? 'text-black/70 opacity-50 dark:text-white/70'
|
|
: 'text-black/70 hover:text-black/50 dark:text-white/70 dark:hover:text-white/50'
|
|
}`}
|
|
style={{ userSelect: 'none' }}
|
|
>
|
|
<svg
|
|
stroke="currentColor"
|
|
fill="none"
|
|
strokeWidth="2"
|
|
viewBox="0 0 24 24"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="h-4 w-4"
|
|
height="1em"
|
|
width="1em"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<polyline points="15 18 9 12 15 6" />
|
|
</svg>
|
|
{localize('com_ui_prev')}
|
|
</div>
|
|
{pages.map((page) => (
|
|
<div
|
|
role="button"
|
|
key={page}
|
|
tabIndex={0}
|
|
className={`flex h-5 w-5 items-center justify-center text-sm ${
|
|
currentPage === page
|
|
? 'text-blue-600 hover:text-blue-600 dark:text-blue-600 dark:hover:text-blue-600'
|
|
: 'text-black/70 hover:text-black/50 dark:text-white/70 dark:hover:text-white/50'
|
|
}`}
|
|
style={{ userSelect: 'none' }}
|
|
onClick={() => onChangePage(page)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
onChangePage(page);
|
|
}
|
|
}}
|
|
>
|
|
{page}
|
|
</div>
|
|
))}
|
|
<div
|
|
role="button"
|
|
aria-label="Next page"
|
|
tabIndex={0}
|
|
onClick={() => handlePageChange(currentPage + 1)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
onChangePage(currentPage + 1);
|
|
}
|
|
}}
|
|
className={`flex cursor-default items-center text-sm ${
|
|
currentPage === maxPage
|
|
? 'text-black/70 opacity-50 dark:text-white/70'
|
|
: 'text-black/70 hover:text-black/50 dark:text-white/70 dark:hover:text-white/50'
|
|
}`}
|
|
style={{ userSelect: 'none' }}
|
|
>
|
|
{localize('com_ui_next')}
|
|
<svg
|
|
stroke="currentColor"
|
|
fill="none"
|
|
strokeWidth="2"
|
|
viewBox="0 0 24 24"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="h-4 w-4"
|
|
height="1em"
|
|
width="1em"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<polyline points="9 18 15 12 9 6" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default PluginPagination;
|