@pallasoft/ps-file-components 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ps-file-list.d.ts +2 -1
- package/dist/components/ps-file-list.js +2 -2
- package/dist/components/ps-file-search-bar.d.ts +10 -0
- package/dist/components/ps-file-search-bar.js +25 -0
- package/dist/components/ps-file-viewer-content.js +9 -0
- package/dist/components/ps-file-viewer-pane.d.ts +1 -1
- package/dist/components/ps-file-viewer-pane.js +26 -2
- package/dist/components/ps-file-viewer.js +11 -23
- package/dist/components/ps-html-document.d.ts +8 -0
- package/dist/components/ps-html-document.js +23 -0
- package/dist/components/ps-html-viewer.d.ts +2 -0
- package/dist/components/ps-html-viewer.js +17 -0
- package/dist/components/ps-image-text-highlights.d.ts +9 -0
- package/dist/components/ps-image-text-highlights.js +31 -0
- package/dist/components/ps-image-viewer.d.ts +1 -1
- package/dist/components/ps-image-viewer.js +11 -2
- package/dist/components/ps-pdf-page.js +1 -1
- package/dist/components/ps-pdf-viewer.js +6 -2
- package/dist/components/ps-sheet-viewer.d.ts +2 -0
- package/dist/components/ps-sheet-viewer.js +26 -0
- package/dist/components/ps-table-view.d.ts +7 -0
- package/dist/components/ps-table-view.js +17 -0
- package/dist/components/ps-text-viewer.js +7 -14
- package/dist/components/ps-word-viewer.d.ts +2 -0
- package/dist/components/ps-word-viewer.js +17 -0
- package/dist/constants/ps-file-kind-icons.js +3 -0
- package/dist/constants/ps-file-kind.d.ts +3 -0
- package/dist/constants/ps-file-kind.js +3 -0
- package/dist/constants/ps-file-list-labels.d.ts +1 -0
- package/dist/constants/ps-file-list-labels.js +1 -0
- package/dist/constants/ps-file-type-config.d.ts +6 -1
- package/dist/constants/ps-file-type-config.js +16 -2
- package/dist/constants/ps-file-viewer-config.d.ts +1 -0
- package/dist/constants/ps-file-viewer-config.js +1 -0
- package/dist/constants/ps-file-viewer-kinds.d.ts +2 -2
- package/dist/constants/ps-file-viewer-kinds.js +17 -2
- package/dist/constants/ps-file-viewer-labels.d.ts +3 -0
- package/dist/constants/ps-file-viewer-labels.js +3 -0
- package/dist/constants/ps-html-sanitize-config.d.ts +8 -0
- package/dist/constants/ps-html-sanitize-config.js +26 -0
- package/dist/esm/components/ps-file-list.js +2 -2
- package/dist/esm/components/ps-file-search-bar.js +21 -0
- package/dist/esm/components/ps-file-viewer-content.js +9 -0
- package/dist/esm/components/ps-file-viewer-pane.js +27 -3
- package/dist/esm/components/ps-file-viewer.js +13 -25
- package/dist/esm/components/ps-html-document.js +19 -0
- package/dist/esm/components/ps-html-viewer.js +13 -0
- package/dist/esm/components/ps-image-text-highlights.js +27 -0
- package/dist/esm/components/ps-image-viewer.js +13 -4
- package/dist/esm/components/ps-pdf-page.js +1 -1
- package/dist/esm/components/ps-pdf-viewer.js +6 -2
- package/dist/esm/components/ps-sheet-viewer.js +22 -0
- package/dist/esm/components/ps-table-view.js +13 -0
- package/dist/esm/components/ps-text-viewer.js +7 -14
- package/dist/esm/components/ps-word-viewer.js +13 -0
- package/dist/esm/constants/ps-file-kind-icons.js +3 -0
- package/dist/esm/constants/ps-file-kind.js +3 -0
- package/dist/esm/constants/ps-file-list-labels.js +1 -0
- package/dist/esm/constants/ps-file-type-config.js +16 -2
- package/dist/esm/constants/ps-file-viewer-config.js +1 -0
- package/dist/esm/constants/ps-file-viewer-kinds.js +17 -2
- package/dist/esm/constants/ps-file-viewer-labels.js +3 -0
- package/dist/esm/constants/ps-html-sanitize-config.js +23 -0
- package/dist/esm/hooks/use-ps-file-search.js +37 -0
- package/dist/esm/hooks/use-ps-file-text.js +4 -19
- package/dist/esm/hooks/use-ps-find-shortcut.js +23 -0
- package/dist/esm/hooks/use-ps-loaded-file.js +19 -0
- package/dist/esm/index.js +33 -0
- package/dist/esm/models/ps-file-search-state.js +1 -0
- package/dist/esm/models/ps-html-highlight-result.js +1 -0
- package/dist/esm/models/ps-html-text-node.js +1 -0
- package/dist/esm/models/ps-image-match.js +1 -0
- package/dist/esm/models/ps-image-text-box.js +1 -0
- package/dist/esm/models/ps-image-text-layer.js +1 -0
- package/dist/esm/models/ps-image-text-word.js +1 -0
- package/dist/esm/models/ps-item-match-part.js +1 -0
- package/dist/esm/models/ps-item-match.js +1 -0
- package/dist/esm/models/ps-loaded-file-state.js +1 -0
- package/dist/esm/models/ps-normalized-text.js +1 -0
- package/dist/esm/models/ps-pdf-text-item.js +1 -0
- package/dist/esm/models/ps-pdf-viewport-like.js +1 -0
- package/dist/esm/models/ps-search-item.js +1 -0
- package/dist/esm/models/ps-sheet-data.js +1 -0
- package/dist/esm/services/ps-file-fetch-service.js +14 -0
- package/dist/esm/services/ps-file-search-service.js +15 -0
- package/dist/esm/services/ps-file-type-service.js +12 -1
- package/dist/esm/services/ps-html-file-service.js +7 -0
- package/dist/esm/services/ps-html-service.js +83 -0
- package/dist/esm/services/ps-image-text-service.js +28 -0
- package/dist/esm/services/ps-label-service.js +5 -0
- package/dist/esm/services/ps-pdf-service.js +39 -16
- package/dist/esm/services/ps-sheet-service.js +23 -0
- package/dist/esm/services/ps-text-search-service.js +70 -5
- package/dist/esm/services/ps-word-service.js +15 -0
- package/dist/hooks/use-ps-file-search.d.ts +2 -0
- package/dist/hooks/use-ps-file-search.js +41 -0
- package/dist/hooks/use-ps-file-text.js +4 -19
- package/dist/hooks/use-ps-find-shortcut.d.ts +2 -0
- package/dist/hooks/use-ps-find-shortcut.js +27 -0
- package/dist/hooks/use-ps-loaded-file.d.ts +2 -0
- package/dist/hooks/use-ps-loaded-file.js +23 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +33 -0
- package/dist/models/ps-file-search-state.d.ts +13 -0
- package/dist/models/ps-file-search-state.js +2 -0
- package/dist/models/ps-file-viewer-pane-props.d.ts +3 -1
- package/dist/models/ps-html-highlight-result.d.ts +4 -0
- package/dist/models/ps-html-highlight-result.js +2 -0
- package/dist/models/ps-html-text-node.d.ts +4 -0
- package/dist/models/ps-html-text-node.js +2 -0
- package/dist/models/ps-image-match.d.ts +7 -0
- package/dist/models/ps-image-match.js +2 -0
- package/dist/models/ps-image-text-box.d.ts +6 -0
- package/dist/models/ps-image-text-box.js +2 -0
- package/dist/models/ps-image-text-layer.d.ts +6 -0
- package/dist/models/ps-image-text-layer.js +2 -0
- package/dist/models/ps-image-text-word.d.ts +5 -0
- package/dist/models/ps-image-text-word.js +2 -0
- package/dist/models/ps-item-match-part.d.ts +5 -0
- package/dist/models/ps-item-match-part.js +2 -0
- package/dist/models/ps-item-match.d.ts +4 -0
- package/dist/models/ps-item-match.js +2 -0
- package/dist/models/ps-loaded-file-state.d.ts +4 -0
- package/dist/models/ps-loaded-file-state.js +2 -0
- package/dist/models/ps-normalized-text.d.ts +5 -0
- package/dist/models/ps-normalized-text.js +2 -0
- package/dist/models/ps-pdf-match.d.ts +1 -0
- package/dist/models/ps-pdf-text-item.d.ts +7 -0
- package/dist/models/ps-pdf-text-item.js +2 -0
- package/dist/models/ps-pdf-viewport-like.d.ts +5 -0
- package/dist/models/ps-pdf-viewport-like.js +2 -0
- package/dist/models/ps-search-item.d.ts +4 -0
- package/dist/models/ps-search-item.js +2 -0
- package/dist/models/ps-sheet-data.d.ts +4 -0
- package/dist/models/ps-sheet-data.js +2 -0
- package/dist/models/ps-viewer-file.d.ts +2 -0
- package/dist/services/ps-file-fetch-service.d.ts +5 -0
- package/dist/services/ps-file-fetch-service.js +18 -0
- package/dist/services/ps-file-search-service.d.ts +5 -0
- package/dist/services/ps-file-search-service.js +19 -0
- package/dist/services/ps-file-type-service.js +12 -1
- package/dist/services/ps-html-file-service.d.ts +3 -0
- package/dist/services/ps-html-file-service.js +11 -0
- package/dist/services/ps-html-service.d.ts +7 -0
- package/dist/services/ps-html-service.js +120 -0
- package/dist/services/ps-image-text-service.d.ts +5 -0
- package/dist/services/ps-image-text-service.js +32 -0
- package/dist/services/ps-label-service.d.ts +3 -0
- package/dist/services/ps-label-service.js +9 -0
- package/dist/services/ps-pdf-service.d.ts +3 -0
- package/dist/services/ps-pdf-service.js +39 -16
- package/dist/services/ps-sheet-service.d.ts +5 -0
- package/dist/services/ps-sheet-service.js +60 -0
- package/dist/services/ps-text-search-service.d.ts +7 -0
- package/dist/services/ps-text-search-service.js +70 -5
- package/dist/services/ps-word-service.d.ts +3 -0
- package/dist/services/ps-word-service.js +52 -0
- package/package.json +5 -2
|
@@ -8,5 +8,6 @@ export interface PsFileListProps {
|
|
|
8
8
|
labels?: Partial<PsLabelSet<typeof PsFileListLabels>>;
|
|
9
9
|
onDelete?: (file: PsViewerFile) => void;
|
|
10
10
|
onOpen: (index: number) => void;
|
|
11
|
+
onRename?: (file: PsViewerFile) => void;
|
|
11
12
|
}
|
|
12
|
-
export declare const PsFileList: ({ busyIds, files, formatDate, labels, onDelete, onOpen, }: PsFileListProps) => import("react").JSX.Element;
|
|
13
|
+
export declare const PsFileList: ({ busyIds, files, formatDate, labels, onDelete, onOpen, onRename, }: PsFileListProps) => import("react").JSX.Element;
|
|
@@ -9,7 +9,7 @@ const ps_file_size_service_1 = require("../services/ps-file-size-service");
|
|
|
9
9
|
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
10
10
|
const ps_file_icon_button_1 = require("./ps-file-icon-button");
|
|
11
11
|
const ps_file_kind_icon_1 = require("./ps-file-kind-icon");
|
|
12
|
-
const PsFileList = ({ busyIds = [], files, formatDate, labels, onDelete, onOpen, }) => {
|
|
12
|
+
const PsFileList = ({ busyIds = [], files, formatDate, labels, onDelete, onOpen, onRename, }) => {
|
|
13
13
|
const text = { ...ps_file_list_labels_1.PsFileListLabels, ...labels };
|
|
14
14
|
if (files.length === 0)
|
|
15
15
|
return (0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/60', children: text.empty });
|
|
@@ -21,7 +21,7 @@ const PsFileList = ({ busyIds = [], files, formatDate, labels, onDelete, onOpen,
|
|
|
21
21
|
? null
|
|
22
22
|
: formatDate(file.createdAt),
|
|
23
23
|
].filter((value) => value !== null);
|
|
24
|
-
return ((0, jsx_runtime_1.jsxs)("li", { className: 'group flex min-w-0 items-center gap-3 rounded-ps-control border border-line bg-surface p-2 transition hover:border-primary/40', children: [(0, jsx_runtime_1.jsxs)("button", { "aria-label": `${text.open} ${file.name}`, className: 'ps-focus-ring flex min-w-0 flex-1 items-center gap-3 rounded-ps-control text-left', onClick: () => onOpen(index), type: 'button', children: [isImage ? ((0, jsx_runtime_1.jsx)("img", { alt: '', className: 'h-12 w-12 shrink-0 rounded-ps-control border border-line object-cover', loading: 'lazy', src: file.url })) : ((0, jsx_runtime_1.jsx)(ps_file_kind_icon_1.PsFileKindIcon, { className: 'h-12 w-12', contentType: file.contentType, name: file.name })), (0, jsx_runtime_1.jsxs)("span", { className: 'flex min-w-0 flex-col', children: [(0, jsx_runtime_1.jsx)("span", { className: 'truncate text-sm font-medium text-ink group-hover:text-primary', children: file.name }), meta.length > 0 ? ((0, jsx_runtime_1.jsx)("span", { className: 'truncate text-xs text-ink/55', children: meta.join(' · ') })) : null] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 items-center gap-0.5', children: [(0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, iconKey: ps_icons_1.PsIconKey.Download, label: `${text.download} ${file.name}` }), onDelete ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: ps_icons_1.PsIconKey.Trash, label: `${text.delete} ${file.name}`, onClick: () => onDelete(file), tone: 'danger' })) : null] })] }, file.id));
|
|
24
|
+
return ((0, jsx_runtime_1.jsxs)("li", { className: 'group flex min-w-0 items-center gap-3 rounded-ps-control border border-line bg-surface p-2 transition hover:border-primary/40', children: [(0, jsx_runtime_1.jsxs)("button", { "aria-label": `${text.open} ${file.name}`, className: 'ps-focus-ring flex min-w-0 flex-1 items-center gap-3 rounded-ps-control text-left', onClick: () => onOpen(index), type: 'button', children: [isImage ? ((0, jsx_runtime_1.jsx)("img", { alt: '', className: 'h-12 w-12 shrink-0 rounded-ps-control border border-line object-cover', loading: 'lazy', src: file.url })) : ((0, jsx_runtime_1.jsx)(ps_file_kind_icon_1.PsFileKindIcon, { className: 'h-12 w-12', contentType: file.contentType, name: file.name })), (0, jsx_runtime_1.jsxs)("span", { className: 'flex min-w-0 flex-col', children: [(0, jsx_runtime_1.jsx)("span", { className: 'truncate text-sm font-medium text-ink group-hover:text-primary', children: file.name }), meta.length > 0 ? ((0, jsx_runtime_1.jsx)("span", { className: 'truncate text-xs text-ink/55', children: meta.join(' · ') })) : null] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 items-center gap-0.5', children: [(0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, iconKey: ps_icons_1.PsIconKey.Download, label: `${text.download} ${file.name}` }), onRename ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: ps_icons_1.PsIconKey.Pencil, label: `${text.rename} ${file.name}`, onClick: () => onRename(file) })) : null, onDelete ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: ps_icons_1.PsIconKey.Trash, label: `${text.delete} ${file.name}`, onClick: () => onDelete(file), tone: 'danger' })) : null] })] }, file.id));
|
|
25
25
|
}) }));
|
|
26
26
|
};
|
|
27
27
|
exports.PsFileList = PsFileList;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PsFileViewerLabels } from '../constants/ps-file-viewer-labels';
|
|
2
|
+
import type { PsFileSearchState } from '../models/ps-file-search-state';
|
|
3
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
4
|
+
export interface PsFileSearchBarProps {
|
|
5
|
+
className?: string;
|
|
6
|
+
labels: PsLabelSet<typeof PsFileViewerLabels>;
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
state: PsFileSearchState;
|
|
9
|
+
}
|
|
10
|
+
export declare const PsFileSearchBar: ({ className, labels, onClose, state }: PsFileSearchBarProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFileSearchBar = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
6
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
7
|
+
const ps_label_service_1 = require("../services/ps-label-service");
|
|
8
|
+
const ps_file_viewer_button_1 = require("./ps-file-viewer-button");
|
|
9
|
+
const PsFileSearchBar = ({ className, labels, onClose, state }) => ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('flex items-center gap-1 rounded-ps-control bg-white/10 px-3', className), role: 'search', children: [(0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'h-4 w-4 shrink-0 text-white/60', iconKey: ps_icons_1.PsIconKey.Search }), (0, jsx_runtime_1.jsx)("input", { "aria-label": labels.search, className: 'h-ps-control-compact min-w-0 flex-1 bg-transparent text-sm text-white placeholder:text-white/45 focus:outline-none', onChange: (event) => state.setSearch(event.target.value), onKeyDown: (event) => {
|
|
10
|
+
if (event.key === 'Enter') {
|
|
11
|
+
event.preventDefault();
|
|
12
|
+
state.step(event.shiftKey ? -1 : 1);
|
|
13
|
+
}
|
|
14
|
+
else if (event.key === 'Escape' && onClose !== undefined) {
|
|
15
|
+
event.preventDefault();
|
|
16
|
+
event.stopPropagation();
|
|
17
|
+
onClose();
|
|
18
|
+
}
|
|
19
|
+
}, placeholder: labels.search, ref: state.inputRef, type: 'search', value: state.search }), state.query.trim().length > 0 ? ((0, jsx_runtime_1.jsx)("span", { "aria-live": 'polite', className: 'shrink-0 whitespace-nowrap text-xs text-white/60', children: state.matchCount === 0
|
|
20
|
+
? labels.noMatches
|
|
21
|
+
: ps_label_service_1.PsLabelService.fill(labels.matchCount, {
|
|
22
|
+
current: state.activeMatch + 1,
|
|
23
|
+
total: state.matchCount,
|
|
24
|
+
}) })) : null, (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: state.matchCount === 0, label: labels.previousMatch, onClick: () => state.step(-1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone -rotate-90', iconKey: ps_icons_1.PsIconKey.ChevronRight }) }), (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: state.matchCount === 0, label: labels.nextMatch, onClick: () => state.step(1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone rotate-90', iconKey: ps_icons_1.PsIconKey.ChevronRight }) }), onClose === undefined ? null : ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: labels.closeSearch, onClick: onClose, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Close }) }))] }));
|
|
25
|
+
exports.PsFileSearchBar = PsFileSearchBar;
|
|
@@ -4,11 +4,14 @@ exports.PsFileViewerContent = void 0;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const ps_file_kind_1 = require("../constants/ps-file-kind");
|
|
6
6
|
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
7
|
+
const ps_html_viewer_1 = require("./ps-html-viewer");
|
|
7
8
|
const ps_image_viewer_1 = require("./ps-image-viewer");
|
|
8
9
|
const ps_media_viewer_1 = require("./ps-media-viewer");
|
|
9
10
|
const ps_pdf_viewer_1 = require("./ps-pdf-viewer");
|
|
11
|
+
const ps_sheet_viewer_1 = require("./ps-sheet-viewer");
|
|
10
12
|
const ps_text_viewer_1 = require("./ps-text-viewer");
|
|
11
13
|
const ps_unsupported_viewer_1 = require("./ps-unsupported-viewer");
|
|
14
|
+
const ps_word_viewer_1 = require("./ps-word-viewer");
|
|
12
15
|
const PsFileViewerContent = (props) => {
|
|
13
16
|
switch (ps_file_type_service_1.PsFileTypeService.kindOf(props.file.contentType, props.file.name)) {
|
|
14
17
|
case ps_file_kind_1.PsFileKind.image:
|
|
@@ -18,6 +21,12 @@ const PsFileViewerContent = (props) => {
|
|
|
18
21
|
case ps_file_kind_1.PsFileKind.csv:
|
|
19
22
|
case ps_file_kind_1.PsFileKind.text:
|
|
20
23
|
return (0, jsx_runtime_1.jsx)(ps_text_viewer_1.PsTextViewer, { ...props });
|
|
24
|
+
case ps_file_kind_1.PsFileKind.word:
|
|
25
|
+
return (0, jsx_runtime_1.jsx)(ps_word_viewer_1.PsWordViewer, { ...props });
|
|
26
|
+
case ps_file_kind_1.PsFileKind.sheet:
|
|
27
|
+
return (0, jsx_runtime_1.jsx)(ps_sheet_viewer_1.PsSheetViewer, { ...props });
|
|
28
|
+
case ps_file_kind_1.PsFileKind.html:
|
|
29
|
+
return (0, jsx_runtime_1.jsx)(ps_html_viewer_1.PsHtmlViewer, { ...props });
|
|
21
30
|
case ps_file_kind_1.PsFileKind.audio:
|
|
22
31
|
case ps_file_kind_1.PsFileKind.video:
|
|
23
32
|
return (0, jsx_runtime_1.jsx)(ps_media_viewer_1.PsMediaViewer, { ...props });
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PsFileViewerPaneProps } from '../models/ps-file-viewer-pane-props';
|
|
2
|
-
export declare const PsFileViewerPane: ({ contentType, headerActions, id, labels, name, pdfWorkerSrc, url, }: PsFileViewerPaneProps) => import("react").JSX.Element;
|
|
2
|
+
export declare const PsFileViewerPane: ({ contentType, headerActions, id, labels, name, pdfWorkerSrc, textLayer, url, }: PsFileViewerPaneProps) => import("react").JSX.Element;
|
|
@@ -7,19 +7,43 @@ const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
|
7
7
|
const ps_file_viewer_config_1 = require("../constants/ps-file-viewer-config");
|
|
8
8
|
const ps_file_viewer_kinds_1 = require("../constants/ps-file-viewer-kinds");
|
|
9
9
|
const ps_file_viewer_labels_1 = require("../constants/ps-file-viewer-labels");
|
|
10
|
+
const use_ps_file_search_1 = require("../hooks/use-ps-file-search");
|
|
11
|
+
const use_ps_find_shortcut_1 = require("../hooks/use-ps-find-shortcut");
|
|
12
|
+
const ps_file_search_service_1 = require("../services/ps-file-search-service");
|
|
10
13
|
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
14
|
+
const ps_file_search_bar_1 = require("./ps-file-search-bar");
|
|
11
15
|
const ps_file_viewer_button_1 = require("./ps-file-viewer-button");
|
|
12
16
|
const ps_file_viewer_content_1 = require("./ps-file-viewer-content");
|
|
13
|
-
const PsFileViewerPane = ({ contentType, headerActions, id, labels
|
|
17
|
+
const PsFileViewerPane = ({ contentType, headerActions, id, labels, name, pdfWorkerSrc, textLayer, url, }) => {
|
|
18
|
+
const text = { ...ps_file_viewer_labels_1.PsFileViewerLabels, ...labels };
|
|
14
19
|
const [zoom, setZoom] = (0, react_1.useState)(1);
|
|
15
20
|
const [rotation, setRotation] = (0, react_1.useState)(0);
|
|
21
|
+
const [isFindOpen, setIsFindOpen] = (0, react_1.useState)(false);
|
|
22
|
+
const root = (0, react_1.useRef)(null);
|
|
16
23
|
const kind = ps_file_type_service_1.PsFileTypeService.kindOf(contentType, name);
|
|
17
24
|
const canZoom = ps_file_viewer_kinds_1.PsFileViewerKinds.zoomable.includes(kind);
|
|
18
25
|
const canRotate = ps_file_viewer_kinds_1.PsFileViewerKinds.rotatable.includes(kind);
|
|
26
|
+
const canSearch = ps_file_search_service_1.PsFileSearchService.canSearch(kind, textLayer !== undefined);
|
|
19
27
|
const changeZoom = (step) => setZoom((current) => Math.min(ps_file_viewer_config_1.PsFileViewerConfig.maxZoom, Math.max(ps_file_viewer_config_1.PsFileViewerConfig.minZoom, current + step)));
|
|
20
28
|
const fileKey = id === undefined ? url : id;
|
|
29
|
+
const searchState = (0, use_ps_file_search_1.usePsFileSearch)(fileKey);
|
|
30
|
+
const { focus, reset } = searchState;
|
|
31
|
+
const openFind = (0, react_1.useCallback)(() => {
|
|
32
|
+
setIsFindOpen(true);
|
|
33
|
+
requestAnimationFrame(focus);
|
|
34
|
+
}, [focus]);
|
|
35
|
+
const closeFind = (0, react_1.useCallback)(() => {
|
|
36
|
+
setIsFindOpen(false);
|
|
37
|
+
reset();
|
|
38
|
+
}, [reset]);
|
|
39
|
+
(0, use_ps_find_shortcut_1.usePsFindShortcut)(root, canSearch, openFind);
|
|
21
40
|
const rotate = () => setRotation((current) => (current + ps_file_viewer_config_1.PsFileViewerConfig.rotateStep) % 360);
|
|
22
41
|
const onKeyDown = (event) => {
|
|
42
|
+
if (event.key === 'Escape' && isFindOpen) {
|
|
43
|
+
closeFind();
|
|
44
|
+
event.preventDefault();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
23
47
|
if (event.metaKey || event.ctrlKey || event.altKey)
|
|
24
48
|
return;
|
|
25
49
|
if (canZoom && (event.key === '+' || event.key === '='))
|
|
@@ -34,6 +58,6 @@ const PsFileViewerPane = ({ contentType, headerActions, id, labels = ps_file_vie
|
|
|
34
58
|
return;
|
|
35
59
|
event.preventDefault();
|
|
36
60
|
};
|
|
37
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-ps-panel bg-black/85', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 flex-wrap items-center justify-between gap-x-2 border-b border-white/10 px-2 py-1 text-white', children: [(0, jsx_runtime_1.jsx)("p", { className: 'min-w-0 flex-1 basis-32 truncate px-1 text-xs text-white/70', title: name, children: name }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-0.5', role: 'toolbar', children: [canZoom ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: zoom <= ps_file_viewer_config_1.PsFileViewerConfig.minZoom, label:
|
|
61
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-ps-panel bg-black/85', ref: root, children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 flex-wrap items-center justify-between gap-x-2 border-b border-white/10 px-2 py-1 text-white', children: [(0, jsx_runtime_1.jsx)("p", { className: 'min-w-0 flex-1 basis-32 truncate px-1 text-xs text-white/70', title: name, children: name }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-0.5', role: 'toolbar', children: [canSearch ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.search, onClick: isFindOpen ? closeFind : openFind, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Search }) })) : null, canZoom ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: zoom <= ps_file_viewer_config_1.PsFileViewerConfig.minZoom, label: text.zoomOut, onClick: () => changeZoom(-ps_file_viewer_config_1.PsFileViewerConfig.zoomStep), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.ZoomOut }) }), (0, jsx_runtime_1.jsxs)(ps_file_viewer_button_1.PsFileViewerButton, { className: 'w-14 text-xs', label: text.zoomReset, onClick: () => setZoom(1), children: [Math.round(zoom * 100), "%"] }), (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: zoom >= ps_file_viewer_config_1.PsFileViewerConfig.maxZoom, label: text.zoomIn, onClick: () => changeZoom(ps_file_viewer_config_1.PsFileViewerConfig.zoomStep), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.ZoomIn }) })] })) : null, canRotate ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.rotate, onClick: rotate, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Rotate }) })) : null, headerActions] })] }), canSearch && isFindOpen ? ((0, jsx_runtime_1.jsx)("div", { className: 'shrink-0 border-b border-white/10 px-2 py-1', children: (0, jsx_runtime_1.jsx)(ps_file_search_bar_1.PsFileSearchBar, { labels: text, onClose: closeFind, state: searchState }) })) : null, (0, jsx_runtime_1.jsx)("div", { className: 'ps-focus-ring flex min-h-0 flex-1 p-2', onKeyDown: onKeyDown, tabIndex: 0, children: (0, jsx_runtime_1.jsx)(ps_file_viewer_content_1.PsFileViewerContent, { activeMatch: searchState.activeMatch, file: { contentType, id: fileKey, name, textLayer, url }, labels: text, onMatchCount: searchState.setMatchCount, pdfWorkerSrc: pdfWorkerSrc, query: canSearch && isFindOpen ? searchState.query : '', rotation: rotation, zoom: zoom }, fileKey) })] }));
|
|
38
62
|
};
|
|
39
63
|
exports.PsFileViewerPane = PsFileViewerPane;
|
|
@@ -10,9 +10,13 @@ const react_dom_1 = require("react-dom");
|
|
|
10
10
|
const ps_file_viewer_config_1 = require("../constants/ps-file-viewer-config");
|
|
11
11
|
const ps_file_viewer_kinds_1 = require("../constants/ps-file-viewer-kinds");
|
|
12
12
|
const ps_file_viewer_labels_1 = require("../constants/ps-file-viewer-labels");
|
|
13
|
+
const use_ps_file_search_1 = require("../hooks/use-ps-file-search");
|
|
14
|
+
const use_ps_find_shortcut_1 = require("../hooks/use-ps-find-shortcut");
|
|
15
|
+
const ps_file_search_service_1 = require("../services/ps-file-search-service");
|
|
13
16
|
const ps_file_size_service_1 = require("../services/ps-file-size-service");
|
|
14
17
|
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
15
18
|
const ps_file_delete_confirm_1 = require("./ps-file-delete-confirm");
|
|
19
|
+
const ps_file_search_bar_1 = require("./ps-file-search-bar");
|
|
16
20
|
const ps_file_viewer_button_1 = require("./ps-file-viewer-button");
|
|
17
21
|
const ps_file_viewer_content_1 = require("./ps-file-viewer-content");
|
|
18
22
|
const PsFileViewer = ({ files, index, isOpen, labels, onClose, onDelete, onIndexChange, pdfWorkerSrc, }) => {
|
|
@@ -21,34 +25,24 @@ const PsFileViewer = ({ files, index, isOpen, labels, onClose, onDelete, onIndex
|
|
|
21
25
|
const zIndex = (0, ps_core_components_1.usePsLayerZIndex)(ps_design_system_1.PallasoftZIndex.Modal);
|
|
22
26
|
const [zoom, setZoom] = (0, react_1.useState)(1);
|
|
23
27
|
const [rotation, setRotation] = (0, react_1.useState)(0);
|
|
24
|
-
const [search, setSearch] = (0, react_1.useState)('');
|
|
25
|
-
const [activeMatch, setActiveMatch] = (0, react_1.useState)(0);
|
|
26
|
-
const [matchCount, setMatchCount] = (0, react_1.useState)(0);
|
|
27
28
|
const [isConfirming, setIsConfirming] = (0, react_1.useState)(false);
|
|
28
29
|
const [isDeleting, setIsDeleting] = (0, react_1.useState)(false);
|
|
29
|
-
const searchInput = (0, react_1.useRef)(null);
|
|
30
|
-
const query = (0, ps_core_components_1.usePsDebouncedValue)(search, ps_file_viewer_config_1.PsFileViewerConfig.searchDebounceMs);
|
|
31
30
|
const file = files.length === 0 ? null : files[Math.min(index, files.length - 1)];
|
|
32
31
|
const kind = file === null ? null : ps_file_type_service_1.PsFileTypeService.kindOf(file.contentType, file.name);
|
|
33
|
-
const
|
|
32
|
+
const searchState = (0, use_ps_file_search_1.usePsFileSearch)(file === null ? null : file.id);
|
|
33
|
+
const canSearch = kind !== null &&
|
|
34
|
+
file !== null &&
|
|
35
|
+
ps_file_search_service_1.PsFileSearchService.canSearch(kind, file.textLayer !== undefined);
|
|
34
36
|
const canZoom = kind !== null && ps_file_viewer_kinds_1.PsFileViewerKinds.zoomable.includes(kind);
|
|
35
37
|
const canRotate = kind !== null && ps_file_viewer_kinds_1.PsFileViewerKinds.rotatable.includes(kind);
|
|
36
38
|
(0, react_1.useEffect)(() => {
|
|
37
39
|
setZoom(1);
|
|
38
40
|
setRotation(0);
|
|
39
|
-
setSearch('');
|
|
40
|
-
setActiveMatch(0);
|
|
41
|
-
setMatchCount(0);
|
|
42
41
|
}, [file === null ? null : file.id]);
|
|
43
|
-
(0, react_1.useEffect)(() => setActiveMatch(0), [query]);
|
|
44
42
|
const go = (step) => {
|
|
45
43
|
if (files.length > 1)
|
|
46
44
|
onIndexChange((index + step + files.length) % files.length);
|
|
47
45
|
};
|
|
48
|
-
const stepMatch = (step) => {
|
|
49
|
-
if (matchCount > 0)
|
|
50
|
-
setActiveMatch((current) => (current + step + matchCount) % matchCount);
|
|
51
|
-
};
|
|
52
46
|
const changeZoom = (step) => setZoom((current) => Math.min(ps_file_viewer_config_1.PsFileViewerConfig.maxZoom, Math.max(ps_file_viewer_config_1.PsFileViewerConfig.minZoom, current + step)));
|
|
53
47
|
(0, react_1.useEffect)(() => {
|
|
54
48
|
if (!isOpen)
|
|
@@ -57,10 +51,6 @@ const PsFileViewer = ({ files, index, isOpen, labels, onClose, onDelete, onIndex
|
|
|
57
51
|
const isTyping = event.target instanceof HTMLInputElement;
|
|
58
52
|
if (event.key === 'Escape' && !isConfirming)
|
|
59
53
|
onClose();
|
|
60
|
-
else if ((event.metaKey || event.ctrlKey) && event.key === 'f' && canSearch) {
|
|
61
|
-
event.preventDefault();
|
|
62
|
-
searchInput.current?.focus();
|
|
63
|
-
}
|
|
64
54
|
else if (isTyping)
|
|
65
55
|
return;
|
|
66
56
|
else if (event.key === 'ArrowRight')
|
|
@@ -74,7 +64,8 @@ const PsFileViewer = ({ files, index, isOpen, labels, onClose, onDelete, onIndex
|
|
|
74
64
|
};
|
|
75
65
|
window.addEventListener('keydown', onKey);
|
|
76
66
|
return () => window.removeEventListener('keydown', onKey);
|
|
77
|
-
}, [isOpen, isConfirming,
|
|
67
|
+
}, [isOpen, isConfirming, index, files.length]);
|
|
68
|
+
(0, use_ps_find_shortcut_1.usePsFindShortcut)(null, isOpen && canSearch, searchState.focus);
|
|
78
69
|
if (!shouldRender || file === null || typeof document === 'undefined')
|
|
79
70
|
return null;
|
|
80
71
|
const confirmDelete = async () => {
|
|
@@ -94,9 +85,6 @@ const PsFileViewer = ({ files, index, isOpen, labels, onClose, onDelete, onIndex
|
|
|
94
85
|
file.sizeBytes === undefined ? null : ps_file_size_service_1.PsFileSizeService.format(file.sizeBytes),
|
|
95
86
|
]
|
|
96
87
|
.filter((value) => value !== null)
|
|
97
|
-
.join(' · ') })] }) }), canSearch ? ((0, jsx_runtime_1.
|
|
98
|
-
if (event.key === 'Enter')
|
|
99
|
-
stepMatch(event.shiftKey ? -1 : 1);
|
|
100
|
-
}, placeholder: text.search, ref: searchInput, type: 'search', value: search }), query.trim().length > 0 ? ((0, jsx_runtime_1.jsx)("span", { className: 'shrink-0 text-xs text-white/60', children: matchCount === 0 ? text.noMatches : `${activeMatch + 1} / ${matchCount}` })) : null, (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: matchCount === 0, label: text.previousMatch, onClick: () => stepMatch(-1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone -rotate-90', iconKey: ps_icons_1.PsIconKey.ChevronRight }) }), (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: matchCount === 0, label: text.nextMatch, onClick: () => stepMatch(1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone rotate-90', iconKey: ps_icons_1.PsIconKey.ChevronRight }) })] })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-0.5', children: [canZoom ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.zoomOut, onClick: () => changeZoom(-ps_file_viewer_config_1.PsFileViewerConfig.zoomStep), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.ZoomOut }) }), (0, jsx_runtime_1.jsxs)(ps_file_viewer_button_1.PsFileViewerButton, { className: 'w-14 text-xs', label: text.zoomReset, onClick: () => setZoom(1), children: [Math.round(zoom * 100), "%"] }), (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.zoomIn, onClick: () => changeZoom(ps_file_viewer_config_1.PsFileViewerConfig.zoomStep), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.ZoomIn }) })] })) : null, canRotate ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.rotate, onClick: () => setRotation((current) => (current + ps_file_viewer_config_1.PsFileViewerConfig.rotateStep) % 360), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Rotate }) })) : null, (0, jsx_runtime_1.jsx)("a", { "aria-label": text.download, className: 'ps-focus-ring ps-control-square ps-control-compact inline-flex items-center justify-center rounded-ps-control text-white/80 transition hover:bg-white/15 hover:text-white', download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, title: text.download, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Download }) }), onDelete ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.delete, onClick: () => setIsConfirming(true), tone: 'danger', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Trash }) })) : null, (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.close, onClick: onClose, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Close }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'relative flex min-h-0 flex-1', children: [files.length > 1 ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { className: 'absolute left-2 top-1/2 z-docked size-ps-control -translate-y-1/2 bg-black/40', label: text.previous, onClick: () => go(-1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone rotate-180', iconKey: ps_icons_1.PsIconKey.ChevronRight }) })) : null, (0, jsx_runtime_1.jsx)("div", { className: 'flex min-h-0 min-w-0 flex-1 p-3 sm:p-6', children: (0, jsx_runtime_1.jsx)(ps_file_viewer_content_1.PsFileViewerContent, { activeMatch: activeMatch, file: file, labels: text, onMatchCount: setMatchCount, pdfWorkerSrc: pdfWorkerSrc, query: canSearch ? query : '', rotation: rotation, zoom: zoom }, file.id) }), files.length > 1 ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { className: 'absolute right-2 top-1/2 z-docked size-ps-control -translate-y-1/2 bg-black/40', label: text.next, onClick: () => go(1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.ChevronRight }) })) : null] }), (0, jsx_runtime_1.jsx)(ps_file_delete_confirm_1.PsFileDeleteConfirm, { fileName: file.name, isDeleting: isDeleting, isOpen: isConfirming, labels: text, onCancel: () => setIsConfirming(false), onConfirm: () => void confirmDelete() })] }) }), document.body);
|
|
88
|
+
.join(' · ') })] }) }), canSearch ? ((0, jsx_runtime_1.jsx)(ps_file_search_bar_1.PsFileSearchBar, { className: 'order-last w-full sm:order-none sm:w-72', labels: text, state: searchState })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-0.5', children: [canZoom ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.zoomOut, onClick: () => changeZoom(-ps_file_viewer_config_1.PsFileViewerConfig.zoomStep), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.ZoomOut }) }), (0, jsx_runtime_1.jsxs)(ps_file_viewer_button_1.PsFileViewerButton, { className: 'w-14 text-xs', label: text.zoomReset, onClick: () => setZoom(1), children: [Math.round(zoom * 100), "%"] }), (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.zoomIn, onClick: () => changeZoom(ps_file_viewer_config_1.PsFileViewerConfig.zoomStep), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.ZoomIn }) })] })) : null, canRotate ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.rotate, onClick: () => setRotation((current) => (current + ps_file_viewer_config_1.PsFileViewerConfig.rotateStep) % 360), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Rotate }) })) : null, (0, jsx_runtime_1.jsx)("a", { "aria-label": text.download, className: 'ps-focus-ring ps-control-square ps-control-compact inline-flex items-center justify-center rounded-ps-control text-white/80 transition hover:bg-white/15 hover:text-white', download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, title: text.download, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Download }) }), onDelete ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.delete, onClick: () => setIsConfirming(true), tone: 'danger', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Trash }) })) : null, (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: text.close, onClick: onClose, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.Close }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'relative flex min-h-0 flex-1', children: [files.length > 1 ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { className: 'absolute left-2 top-1/2 z-docked size-ps-control -translate-y-1/2 bg-black/40', label: text.previous, onClick: () => go(-1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone rotate-180', iconKey: ps_icons_1.PsIconKey.ChevronRight }) })) : null, (0, jsx_runtime_1.jsx)("div", { className: 'flex min-h-0 min-w-0 flex-1 p-3 sm:p-6', children: (0, jsx_runtime_1.jsx)(ps_file_viewer_content_1.PsFileViewerContent, { activeMatch: searchState.activeMatch, file: file, labels: text, onMatchCount: searchState.setMatchCount, pdfWorkerSrc: pdfWorkerSrc, query: canSearch ? searchState.query : '', rotation: rotation, zoom: zoom }, file.id) }), files.length > 1 ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { className: 'absolute right-2 top-1/2 z-docked size-ps-control -translate-y-1/2 bg-black/40', label: text.next, onClick: () => go(1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: ps_icons_1.PsIconKey.ChevronRight }) })) : null] }), (0, jsx_runtime_1.jsx)(ps_file_delete_confirm_1.PsFileDeleteConfirm, { fileName: file.name, isDeleting: isDeleting, isOpen: isConfirming, labels: text, onCancel: () => setIsConfirming(false), onConfirm: () => void confirmDelete() })] }) }), document.body);
|
|
101
89
|
};
|
|
102
90
|
exports.PsFileViewer = PsFileViewer;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface PsHtmlDocumentProps {
|
|
2
|
+
activeMatch: number;
|
|
3
|
+
html: string;
|
|
4
|
+
onMatchCount: (count: number) => void;
|
|
5
|
+
query: string;
|
|
6
|
+
zoom: number;
|
|
7
|
+
}
|
|
8
|
+
export declare const PsHtmlDocument: ({ activeMatch, html, onMatchCount, query, zoom, }: PsHtmlDocumentProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsHtmlDocument = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const use_ps_active_match_scroll_1 = require("../hooks/use-ps-active-match-scroll");
|
|
7
|
+
const ps_html_service_1 = require("../services/ps-html-service");
|
|
8
|
+
const PsHtmlDocument = ({ activeMatch, html, onMatchCount, query, zoom, }) => {
|
|
9
|
+
const container = (0, react_1.useRef)(null);
|
|
10
|
+
const highlighted = (0, react_1.useMemo)(() => ps_html_service_1.PsHtmlService.highlight(html, query), [html, query]);
|
|
11
|
+
(0, react_1.useEffect)(() => onMatchCount(highlighted.count), [highlighted.count]);
|
|
12
|
+
(0, react_1.useEffect)(() => {
|
|
13
|
+
container.current?.querySelectorAll('mark[data-ps-match]').forEach((mark) => {
|
|
14
|
+
if (mark.getAttribute('data-ps-match') === String(activeMatch))
|
|
15
|
+
mark.setAttribute('data-ps-active', '');
|
|
16
|
+
else
|
|
17
|
+
mark.removeAttribute('data-ps-active');
|
|
18
|
+
});
|
|
19
|
+
}, [highlighted.html, activeMatch]);
|
|
20
|
+
(0, use_ps_active_match_scroll_1.usePsActiveMatchScroll)(container, activeMatch, highlighted.html);
|
|
21
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: 'h-full w-full overflow-auto', children: (0, jsx_runtime_1.jsx)("div", { className: 'ps-html-document mx-auto w-full max-w-4xl break-words rounded-lg bg-surface p-8 leading-relaxed text-ink shadow-2xl [&_a]:text-primary [&_a]:underline [&_blockquote]:border-l-4 [&_blockquote]:border-line [&_blockquote]:pl-4 [&_h1]:mb-3 [&_h1]:text-2xl [&_h1]:font-bold [&_h2]:mb-2 [&_h2]:text-xl [&_h2]:font-bold [&_h3]:mb-2 [&_h3]:text-lg [&_h3]:font-semibold [&_img]:h-auto [&_img]:max-w-full [&_li]:my-1 [&_mark]:rounded-sm [&_mark]:bg-warning/40 [&_mark]:text-inherit [&_mark[data-ps-active]]:bg-warning [&_mark[data-ps-active]]:text-ink [&_mark[data-ps-active]]:ring-2 [&_mark[data-ps-active]]:ring-warning [&_ol]:list-decimal [&_ol]:pl-6 [&_p]:mb-3 [&_table]:my-3 [&_table]:border-collapse [&_td]:border [&_td]:border-line [&_td]:px-2 [&_td]:py-1 [&_th]:border [&_th]:border-line [&_th]:px-2 [&_th]:py-1 [&_ul]:list-disc [&_ul]:pl-6', dangerouslySetInnerHTML: { __html: highlighted.html }, ref: container, style: { fontSize: `${zoom}rem` } }) }));
|
|
22
|
+
};
|
|
23
|
+
exports.PsHtmlDocument = PsHtmlDocument;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsHtmlViewer = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
+
const use_ps_loaded_file_1 = require("../hooks/use-ps-loaded-file");
|
|
7
|
+
const ps_html_file_service_1 = require("../services/ps-html-file-service");
|
|
8
|
+
const ps_html_document_1 = require("./ps-html-document");
|
|
9
|
+
const PsHtmlViewer = ({ activeMatch, file, labels, onMatchCount, query, zoom, }) => {
|
|
10
|
+
const { hasFailed, value } = (0, use_ps_loaded_file_1.usePsLoadedFile)(file.url, ps_html_file_service_1.PsHtmlFileService.load);
|
|
11
|
+
if (hasFailed)
|
|
12
|
+
return (0, jsx_runtime_1.jsx)("p", { className: 'm-auto text-sm text-white/70', children: labels.loadFailed });
|
|
13
|
+
if (value === null)
|
|
14
|
+
return (0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { className: 'm-auto', color: 'current', size: 'large' });
|
|
15
|
+
return ((0, jsx_runtime_1.jsx)(ps_html_document_1.PsHtmlDocument, { activeMatch: activeMatch, html: value, onMatchCount: onMatchCount, query: query, zoom: zoom }));
|
|
16
|
+
};
|
|
17
|
+
exports.PsHtmlViewer = PsHtmlViewer;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type { PsImageMatch } from '../models/ps-image-match';
|
|
3
|
+
export interface PsImageTextHighlightsProps {
|
|
4
|
+
activeIndex: number;
|
|
5
|
+
image: RefObject<HTMLImageElement>;
|
|
6
|
+
matches: PsImageMatch[];
|
|
7
|
+
transform: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const PsImageTextHighlights: ({ activeIndex, image, matches, transform, }: PsImageTextHighlightsProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsImageTextHighlights = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const PsImageTextHighlights = ({ activeIndex, image, matches, transform, }) => {
|
|
7
|
+
const [size, setSize] = (0, react_1.useState)({ height: 0, width: 0 });
|
|
8
|
+
(0, react_1.useEffect)(() => {
|
|
9
|
+
const node = image.current;
|
|
10
|
+
if (node === null)
|
|
11
|
+
return;
|
|
12
|
+
const measure = () => setSize({ height: node.clientHeight, width: node.clientWidth });
|
|
13
|
+
measure();
|
|
14
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
15
|
+
window.addEventListener('resize', measure);
|
|
16
|
+
return () => window.removeEventListener('resize', measure);
|
|
17
|
+
}
|
|
18
|
+
const observer = new ResizeObserver(measure);
|
|
19
|
+
observer.observe(node);
|
|
20
|
+
return () => observer.disconnect();
|
|
21
|
+
}, [image]);
|
|
22
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: 'pointer-events-none absolute inset-0 flex items-center justify-center', children: (0, jsx_runtime_1.jsx)("div", { className: 'relative shrink-0 transition-transform duration-150', style: { height: size.height, transform, width: size.width }, children: matches.map((match, index) => ((0, jsx_runtime_1.jsx)("span", { className: match.matchIndex === activeIndex
|
|
23
|
+
? 'absolute rounded-sm bg-warning/60 ring-2 ring-warning'
|
|
24
|
+
: 'absolute rounded-sm bg-warning/35', style: {
|
|
25
|
+
height: `${match.height * 100}%`,
|
|
26
|
+
left: `${match.left * 100}%`,
|
|
27
|
+
top: `${match.top * 100}%`,
|
|
28
|
+
width: `${match.width * 100}%`,
|
|
29
|
+
} }, index))) }) }));
|
|
30
|
+
};
|
|
31
|
+
exports.PsImageTextHighlights = PsImageTextHighlights;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PsFileViewerContentProps } from '../models/ps-file-viewer-content-props';
|
|
2
|
-
export declare const PsImageViewer: ({ file, labels, rotation, zoom }: PsFileViewerContentProps) => import("react").JSX.Element;
|
|
2
|
+
export declare const PsImageViewer: ({ activeMatch, file, labels, onMatchCount, query, rotation, zoom, }: PsFileViewerContentProps) => import("react").JSX.Element;
|
|
@@ -4,14 +4,23 @@ exports.PsImageViewer = void 0;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const use_ps_drag_pan_1 = require("../hooks/use-ps-drag-pan");
|
|
7
|
+
const ps_image_text_service_1 = require("../services/ps-image-text-service");
|
|
7
8
|
const ps_pan_service_1 = require("../services/ps-pan-service");
|
|
8
|
-
const
|
|
9
|
+
const ps_image_text_highlights_1 = require("./ps-image-text-highlights");
|
|
10
|
+
const PsImageViewer = ({ activeMatch, file, labels, onMatchCount, query, rotation, zoom, }) => {
|
|
9
11
|
const [offset, setOffset] = (0, react_1.useState)({ x: 0, y: 0 });
|
|
10
12
|
const [hasFailed, setHasFailed] = (0, react_1.useState)(false);
|
|
11
13
|
const pan = (0, react_1.useRef)(null);
|
|
12
14
|
const base = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
13
15
|
const frame = (0, react_1.useRef)(0);
|
|
14
16
|
const pending = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
17
|
+
const image = (0, react_1.useRef)(null);
|
|
18
|
+
const layer = file.textLayer;
|
|
19
|
+
const matches = (0, react_1.useMemo)(() => (layer === undefined ? [] : ps_image_text_service_1.PsImageTextService.matches(layer, query)), [layer, query]);
|
|
20
|
+
const count = matches.length === 0 ? 0 : matches[matches.length - 1].matchIndex + 1;
|
|
21
|
+
const activeIndex = count === 0 ? -1 : activeMatch % count;
|
|
22
|
+
const imageTransform = `scale(${zoom}) rotate(${rotation}deg)`;
|
|
23
|
+
(0, react_1.useEffect)(() => onMatchCount(count), [count]);
|
|
15
24
|
(0, react_1.useEffect)(() => {
|
|
16
25
|
setOffset({ x: 0, y: 0 });
|
|
17
26
|
setHasFailed(false);
|
|
@@ -47,6 +56,6 @@ const PsImageViewer = ({ file, labels, rotation, zoom }) => {
|
|
|
47
56
|
});
|
|
48
57
|
if (hasFailed)
|
|
49
58
|
return (0, jsx_runtime_1.jsx)("p", { className: 'm-auto text-sm text-white/70', children: labels.loadFailed });
|
|
50
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: `h-full w-full touch-none overflow-hidden ${zoom > 1 ? 'cursor-grab active:cursor-grabbing' : ''}`, ...handlers, children: (0, jsx_runtime_1.
|
|
59
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `h-full w-full touch-none overflow-hidden ${zoom > 1 ? 'cursor-grab active:cursor-grabbing' : ''}`, ...handlers, children: (0, jsx_runtime_1.jsxs)("div", { className: 'relative flex h-full w-full items-center justify-center', ref: pan, style: { transform: ps_pan_service_1.PsPanService.translate(offset) }, children: [(0, jsx_runtime_1.jsx)("img", { alt: file.name, className: 'max-h-full max-w-full select-none object-contain shadow-2xl transition-transform duration-150', draggable: false, onError: () => setHasFailed(true), ref: image, src: file.url, style: { transform: imageTransform } }), matches.length > 0 ? ((0, jsx_runtime_1.jsx)(ps_image_text_highlights_1.PsImageTextHighlights, { activeIndex: activeIndex, image: image, matches: matches, transform: imageTransform })) : null] }) }));
|
|
51
60
|
};
|
|
52
61
|
exports.PsImageViewer = PsImageViewer;
|
|
@@ -25,7 +25,7 @@ const PsPdfPage = ({ activeMatch, document, label, matches, pageNumber, rotation
|
|
|
25
25
|
task?.cancel();
|
|
26
26
|
};
|
|
27
27
|
}, [document, pageNumber, rotation, scale, isInView]);
|
|
28
|
-
return ((0, jsx_runtime_1.jsxs)("div", { "aria-label": `${label} ${pageNumber}`, className: 'relative mx-auto bg-white shadow-2xl', "data-ps-pdf-page": pageNumber, ref: container, style: size === null ? { height: 800, width: 600 } : { height: size.height, width: size.width }, children: [(0, jsx_runtime_1.jsx)("canvas", { className: 'block', ref: canvas }), matches.map((match, index) => ((0, jsx_runtime_1.jsx)("span", { className: match === activeMatch
|
|
28
|
+
return ((0, jsx_runtime_1.jsxs)("div", { "aria-label": `${label} ${pageNumber}`, className: 'relative mx-auto bg-white shadow-2xl', "data-ps-pdf-page": pageNumber, ref: container, style: size === null ? { height: 800, width: 600 } : { height: size.height, width: size.width }, children: [(0, jsx_runtime_1.jsx)("canvas", { className: 'block', ref: canvas }), matches.map((match, index) => ((0, jsx_runtime_1.jsx)("span", { className: activeMatch !== null && match.matchIndex === activeMatch.matchIndex
|
|
29
29
|
? 'pointer-events-none absolute rounded-sm bg-warning/60 ring-2 ring-warning'
|
|
30
30
|
: 'pointer-events-none absolute rounded-sm bg-warning/35', style: {
|
|
31
31
|
height: `${match.height * 100}%`,
|
|
@@ -79,13 +79,17 @@ const PsPdfViewer = ({ activeMatch, file, labels, onMatchCount, pdfWorkerSrc, qu
|
|
|
79
79
|
if (!isCurrent)
|
|
80
80
|
return;
|
|
81
81
|
setMatches(found);
|
|
82
|
-
onMatchCount(found.length);
|
|
82
|
+
onMatchCount(found.length === 0 ? 0 : found[found.length - 1].matchIndex + 1);
|
|
83
83
|
});
|
|
84
84
|
return () => {
|
|
85
85
|
isCurrent = false;
|
|
86
86
|
};
|
|
87
87
|
}, [document, query, rotation]);
|
|
88
|
-
const
|
|
88
|
+
const matchTotal = matches.length === 0 ? 0 : matches[matches.length - 1].matchIndex + 1;
|
|
89
|
+
const activeRect = matchTotal === 0
|
|
90
|
+
? undefined
|
|
91
|
+
: matches.find((match) => match.matchIndex === activeMatch % matchTotal);
|
|
92
|
+
const active = activeRect === undefined ? null : activeRect;
|
|
89
93
|
const scrollToPage = (pageNumber) => {
|
|
90
94
|
const node = scroller.current?.querySelector(`[data-ps-pdf-page='${pageNumber}']`);
|
|
91
95
|
if (node instanceof HTMLElement)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsSheetViewer = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
7
|
+
const ps_file_viewer_config_1 = require("../constants/ps-file-viewer-config");
|
|
8
|
+
const use_ps_loaded_file_1 = require("../hooks/use-ps-loaded-file");
|
|
9
|
+
const ps_label_service_1 = require("../services/ps-label-service");
|
|
10
|
+
const ps_sheet_service_1 = require("../services/ps-sheet-service");
|
|
11
|
+
const ps_table_view_1 = require("./ps-table-view");
|
|
12
|
+
const PsSheetViewer = ({ activeMatch, file, labels, onMatchCount, query, zoom, }) => {
|
|
13
|
+
const { hasFailed, value } = (0, use_ps_loaded_file_1.usePsLoadedFile)(file.url, ps_sheet_service_1.PsSheetService.load);
|
|
14
|
+
const [sheetIndex, setSheetIndex] = (0, react_1.useState)(0);
|
|
15
|
+
const sheet = value === null || value.length === 0 ? null : value[Math.min(sheetIndex, value.length - 1)];
|
|
16
|
+
const rows = (0, react_1.useMemo)(() => (sheet === null ? [] : sheet.rows.slice(0, ps_file_viewer_config_1.PsFileViewerConfig.maxSheetRows)), [sheet]);
|
|
17
|
+
(0, react_1.useEffect)(() => setSheetIndex(0), [file.id]);
|
|
18
|
+
if (hasFailed || (value !== null && value.length === 0))
|
|
19
|
+
return (0, jsx_runtime_1.jsx)("p", { className: 'm-auto text-sm text-white/70', children: labels.loadFailed });
|
|
20
|
+
if (value === null || sheet === null)
|
|
21
|
+
return (0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { className: 'm-auto', color: 'current', size: 'large' });
|
|
22
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full w-full min-w-0 flex-col gap-2', children: [(0, jsx_runtime_1.jsx)("div", { className: 'min-h-0 flex-1 overflow-auto', children: (0, jsx_runtime_1.jsx)("div", { className: 'mx-auto w-full max-w-6xl rounded-lg bg-surface p-5 text-ink shadow-2xl', style: { fontSize: `${zoom * 0.875}rem` }, children: (0, jsx_runtime_1.jsx)(ps_table_view_1.PsTableView, { activeMatch: activeMatch, onMatchCount: onMatchCount, query: query, rows: rows }, sheet.name) }) }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 flex-wrap items-center gap-2 text-xs text-white/70', children: [value.length > 1 ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex flex-wrap gap-1', role: 'tablist', children: value.map((entry, index) => ((0, jsx_runtime_1.jsx)("button", { "aria-selected": entry === sheet, className: entry === sheet
|
|
23
|
+
? 'ps-focus-ring ps-control-compact rounded-ps-control bg-white/20 px-3 text-white'
|
|
24
|
+
: 'ps-focus-ring ps-control-compact rounded-ps-control px-3 text-white/70 hover:bg-white/10', onClick: () => setSheetIndex(index), role: 'tab', type: 'button', children: entry.name }, `${entry.name}-${index}`))) })) : null, sheet.rows.length > rows.length ? ((0, jsx_runtime_1.jsx)("span", { children: ps_label_service_1.PsLabelService.fill(labels.sheetTruncated, { count: ps_file_viewer_config_1.PsFileViewerConfig.maxSheetRows }) })) : null] })] }));
|
|
25
|
+
};
|
|
26
|
+
exports.PsSheetViewer = PsSheetViewer;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface PsTableViewProps {
|
|
2
|
+
activeMatch: number;
|
|
3
|
+
onMatchCount: (count: number) => void;
|
|
4
|
+
query: string;
|
|
5
|
+
rows: string[][];
|
|
6
|
+
}
|
|
7
|
+
export declare const PsTableView: ({ activeMatch, onMatchCount, query, rows }: PsTableViewProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsTableView = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const use_ps_active_match_scroll_1 = require("../hooks/use-ps-active-match-scroll");
|
|
7
|
+
const ps_text_search_service_1 = require("../services/ps-text-search-service");
|
|
8
|
+
const ps_highlighted_text_1 = require("./ps-highlighted-text");
|
|
9
|
+
const PsTableView = ({ activeMatch, onMatchCount, query, rows }) => {
|
|
10
|
+
const container = (0, react_1.useRef)(null);
|
|
11
|
+
const cells = (0, react_1.useMemo)(() => ps_text_search_service_1.PsTextSearchService.tableSegments(rows, query), [rows, query]);
|
|
12
|
+
const count = (0, react_1.useMemo)(() => ps_text_search_service_1.PsTextSearchService.countSegments(cells), [cells]);
|
|
13
|
+
(0, react_1.useEffect)(() => onMatchCount(count), [count]);
|
|
14
|
+
(0, use_ps_active_match_scroll_1.usePsActiveMatchScroll)(container, activeMatch, query);
|
|
15
|
+
return ((0, jsx_runtime_1.jsx)("table", { className: 'w-full border-collapse text-left', ref: container, children: (0, jsx_runtime_1.jsx)("tbody", { children: cells.map((row, rowIndex) => ((0, jsx_runtime_1.jsx)("tr", { className: rowIndex === 0 ? 'bg-primary-soft/50 font-semibold' : 'border-t border-line', children: row.map((cell, cellIndex) => ((0, jsx_runtime_1.jsx)("td", { className: 'whitespace-nowrap px-3 py-1.5 align-top', children: (0, jsx_runtime_1.jsx)(ps_highlighted_text_1.PsHighlightedText, { activeMatch: activeMatch, segments: cell }) }, cellIndex))) }, rowIndex))) }) }));
|
|
16
|
+
};
|
|
17
|
+
exports.PsTableView = PsTableView;
|
|
@@ -11,32 +11,25 @@ const ps_csv_service_1 = require("../services/ps-csv-service");
|
|
|
11
11
|
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
12
12
|
const ps_text_search_service_1 = require("../services/ps-text-search-service");
|
|
13
13
|
const ps_highlighted_text_1 = require("./ps-highlighted-text");
|
|
14
|
+
const ps_table_view_1 = require("./ps-table-view");
|
|
14
15
|
const PsTextViewer = ({ activeMatch, file, labels, onMatchCount, query, zoom, }) => {
|
|
15
16
|
const { hasFailed, text } = (0, use_ps_file_text_1.usePsFileText)(file.url);
|
|
16
17
|
const container = (0, react_1.useRef)(null);
|
|
17
18
|
const isCsv = ps_file_type_service_1.PsFileTypeService.kindOf(file.contentType, file.name) === ps_file_kind_1.PsFileKind.csv;
|
|
18
19
|
const rows = (0, react_1.useMemo)(() => (isCsv && text !== null ? ps_csv_service_1.PsCsvService.parse(text) : []), [isCsv, text]);
|
|
19
|
-
const cells = (0, react_1.useMemo)(() => {
|
|
20
|
-
let offset = 0;
|
|
21
|
-
return rows.map((row) => row.map((cell) => {
|
|
22
|
-
const matches = ps_text_search_service_1.PsTextSearchService.find(cell, query);
|
|
23
|
-
const segments = ps_text_search_service_1.PsTextSearchService.segments(cell, matches, offset);
|
|
24
|
-
offset += matches.length;
|
|
25
|
-
return segments;
|
|
26
|
-
}));
|
|
27
|
-
}, [rows, query]);
|
|
28
20
|
const segments = (0, react_1.useMemo)(() => isCsv || text === null
|
|
29
21
|
? []
|
|
30
22
|
: ps_text_search_service_1.PsTextSearchService.segments(text, ps_text_search_service_1.PsTextSearchService.find(text, query)), [isCsv, text, query]);
|
|
31
|
-
const count =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
const count = segments.filter((part) => part.isMatch).length;
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
if (!isCsv)
|
|
26
|
+
onMatchCount(count);
|
|
27
|
+
}, [isCsv, count]);
|
|
35
28
|
(0, use_ps_active_match_scroll_1.usePsActiveMatchScroll)(container, activeMatch, query);
|
|
36
29
|
if (hasFailed)
|
|
37
30
|
return (0, jsx_runtime_1.jsx)("p", { className: 'm-auto text-sm text-white/70', children: labels.loadFailed });
|
|
38
31
|
if (text === null)
|
|
39
32
|
return (0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { className: 'm-auto', color: 'current', size: 'large' });
|
|
40
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: 'h-full w-full overflow-auto', ref: container, children: (0, jsx_runtime_1.jsx)("div", { className: 'mx-auto w-full max-w-5xl rounded-lg bg-surface p-5 text-ink shadow-2xl', style: { fontSize: `${zoom * 0.875}rem` }, children: isCsv ? ((0, jsx_runtime_1.jsx)(
|
|
33
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: 'h-full w-full overflow-auto', ref: container, children: (0, jsx_runtime_1.jsx)("div", { className: 'mx-auto w-full max-w-5xl rounded-lg bg-surface p-5 text-ink shadow-2xl', style: { fontSize: `${zoom * 0.875}rem` }, children: isCsv ? ((0, jsx_runtime_1.jsx)(ps_table_view_1.PsTableView, { activeMatch: activeMatch, onMatchCount: onMatchCount, query: query, rows: rows })) : ((0, jsx_runtime_1.jsx)("pre", { className: 'whitespace-pre-wrap break-words font-mono leading-relaxed', children: (0, jsx_runtime_1.jsx)(ps_highlighted_text_1.PsHighlightedText, { activeMatch: activeMatch, segments: segments }) })) }) }));
|
|
41
34
|
};
|
|
42
35
|
exports.PsTextViewer = PsTextViewer;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsWordViewer = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
+
const use_ps_loaded_file_1 = require("../hooks/use-ps-loaded-file");
|
|
7
|
+
const ps_word_service_1 = require("../services/ps-word-service");
|
|
8
|
+
const ps_html_document_1 = require("./ps-html-document");
|
|
9
|
+
const PsWordViewer = ({ activeMatch, file, labels, onMatchCount, query, zoom, }) => {
|
|
10
|
+
const { hasFailed, value } = (0, use_ps_loaded_file_1.usePsLoadedFile)(file.url, ps_word_service_1.PsWordService.load);
|
|
11
|
+
if (hasFailed)
|
|
12
|
+
return (0, jsx_runtime_1.jsx)("p", { className: 'm-auto text-sm text-white/70', children: labels.loadFailed });
|
|
13
|
+
if (value === null)
|
|
14
|
+
return (0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { className: 'm-auto', color: 'current', size: 'large' });
|
|
15
|
+
return ((0, jsx_runtime_1.jsx)(ps_html_document_1.PsHtmlDocument, { activeMatch: activeMatch, html: value, onMatchCount: onMatchCount, query: query, zoom: zoom }));
|
|
16
|
+
};
|
|
17
|
+
exports.PsWordViewer = PsWordViewer;
|
|
@@ -6,9 +6,12 @@ const ps_file_kind_1 = require("./ps-file-kind");
|
|
|
6
6
|
exports.PsFileKindIcons = {
|
|
7
7
|
[ps_file_kind_1.PsFileKind.audio]: ps_icons_1.PsIconKey.Audio,
|
|
8
8
|
[ps_file_kind_1.PsFileKind.csv]: ps_icons_1.PsIconKey.Sheet,
|
|
9
|
+
[ps_file_kind_1.PsFileKind.html]: ps_icons_1.PsIconKey.Text,
|
|
9
10
|
[ps_file_kind_1.PsFileKind.image]: ps_icons_1.PsIconKey.Image,
|
|
10
11
|
[ps_file_kind_1.PsFileKind.other]: ps_icons_1.PsIconKey.File,
|
|
11
12
|
[ps_file_kind_1.PsFileKind.pdf]: ps_icons_1.PsIconKey.Doc,
|
|
13
|
+
[ps_file_kind_1.PsFileKind.sheet]: ps_icons_1.PsIconKey.Sheet,
|
|
12
14
|
[ps_file_kind_1.PsFileKind.text]: ps_icons_1.PsIconKey.Text,
|
|
13
15
|
[ps_file_kind_1.PsFileKind.video]: ps_icons_1.PsIconKey.Video,
|
|
16
|
+
[ps_file_kind_1.PsFileKind.word]: ps_icons_1.PsIconKey.Doc,
|
|
14
17
|
};
|