@pallasoft/ps-file-components 1.0.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/LICENSE +32 -0
- package/README.md +10 -0
- package/dist/components/ps-file-delete-confirm.d.ts +11 -0
- package/dist/components/ps-file-delete-confirm.js +8 -0
- package/dist/components/ps-file-kind-icon.d.ts +6 -0
- package/dist/components/ps-file-kind-icon.js +10 -0
- package/dist/components/ps-file-list.d.ts +12 -0
- package/dist/components/ps-file-list.js +27 -0
- package/dist/components/ps-file-picker.d.ts +18 -0
- package/dist/components/ps-file-picker.js +48 -0
- package/dist/components/ps-file-viewer-button.d.ts +10 -0
- package/dist/components/ps-file-viewer-button.js +7 -0
- package/dist/components/ps-file-viewer-content.d.ts +2 -0
- package/dist/components/ps-file-viewer-content.js +28 -0
- package/dist/components/ps-file-viewer.d.ts +14 -0
- package/dist/components/ps-file-viewer.js +102 -0
- package/dist/components/ps-progress-bar.d.ts +7 -0
- package/dist/components/ps-progress-bar.js +15 -0
- package/dist/components/ps-upload-modal.d.ts +10 -0
- package/dist/components/ps-upload-modal.js +19 -0
- package/dist/components/ps-upload-row.d.ts +11 -0
- package/dist/components/ps-upload-row.js +28 -0
- package/dist/components/viewers/ps-highlighted-text.d.ts +6 -0
- package/dist/components/viewers/ps-highlighted-text.js +8 -0
- package/dist/components/viewers/ps-image-viewer.d.ts +2 -0
- package/dist/components/viewers/ps-image-viewer.js +33 -0
- package/dist/components/viewers/ps-media-viewer.d.ts +2 -0
- package/dist/components/viewers/ps-media-viewer.js +8 -0
- package/dist/components/viewers/ps-pdf-page.d.ts +12 -0
- package/dist/components/viewers/ps-pdf-page.js +37 -0
- package/dist/components/viewers/ps-pdf-thumbnail.d.ts +11 -0
- package/dist/components/viewers/ps-pdf-thumbnail.js +28 -0
- package/dist/components/viewers/ps-pdf-viewer.d.ts +2 -0
- package/dist/components/viewers/ps-pdf-viewer.js +100 -0
- package/dist/components/viewers/ps-text-viewer.d.ts +2 -0
- package/dist/components/viewers/ps-text-viewer.js +42 -0
- package/dist/components/viewers/ps-unsupported-viewer.d.ts +2 -0
- package/dist/components/viewers/ps-unsupported-viewer.js +8 -0
- package/dist/constants/ps-file-kind-icons.d.ts +3 -0
- package/dist/constants/ps-file-kind-icons.js +14 -0
- package/dist/constants/ps-file-kind.d.ts +10 -0
- package/dist/constants/ps-file-kind.js +12 -0
- package/dist/constants/ps-file-list-labels.d.ts +6 -0
- package/dist/constants/ps-file-list-labels.js +9 -0
- package/dist/constants/ps-file-picker-labels.d.ts +7 -0
- package/dist/constants/ps-file-picker-labels.js +10 -0
- package/dist/constants/ps-file-type-config.d.ts +7 -0
- package/dist/constants/ps-file-type-config.js +30 -0
- package/dist/constants/ps-file-viewer-config.d.ts +9 -0
- package/dist/constants/ps-file-viewer-config.js +12 -0
- package/dist/constants/ps-file-viewer-kinds.d.ts +5 -0
- package/dist/constants/ps-file-viewer-kinds.js +9 -0
- package/dist/constants/ps-file-viewer-labels.d.ts +21 -0
- package/dist/constants/ps-file-viewer-labels.js +24 -0
- package/dist/constants/ps-upload-labels.d.ts +14 -0
- package/dist/constants/ps-upload-labels.js +17 -0
- package/dist/constants/ps-upload-messages.d.ts +4 -0
- package/dist/constants/ps-upload-messages.js +7 -0
- package/dist/constants/ps-upload-queue-config.d.ts +4 -0
- package/dist/constants/ps-upload-queue-config.js +7 -0
- package/dist/constants/ps-upload-status-tones.d.ts +2 -0
- package/dist/constants/ps-upload-status-tones.js +12 -0
- package/dist/constants/ps-upload-status.d.ts +9 -0
- package/dist/constants/ps-upload-status.js +11 -0
- package/dist/esm/components/ps-file-delete-confirm.js +4 -0
- package/dist/esm/components/ps-file-kind-icon.js +6 -0
- package/dist/esm/components/ps-file-list.js +23 -0
- package/dist/esm/components/ps-file-picker.js +44 -0
- package/dist/esm/components/ps-file-viewer-button.js +3 -0
- package/dist/esm/components/ps-file-viewer-content.js +24 -0
- package/dist/esm/components/ps-file-viewer.js +98 -0
- package/dist/esm/components/ps-progress-bar.js +11 -0
- package/dist/esm/components/ps-upload-modal.js +15 -0
- package/dist/esm/components/ps-upload-row.js +24 -0
- package/dist/esm/components/viewers/ps-highlighted-text.js +4 -0
- package/dist/esm/components/viewers/ps-image-viewer.js +29 -0
- package/dist/esm/components/viewers/ps-media-viewer.js +4 -0
- package/dist/esm/components/viewers/ps-pdf-page.js +33 -0
- package/dist/esm/components/viewers/ps-pdf-thumbnail.js +24 -0
- package/dist/esm/components/viewers/ps-pdf-viewer.js +96 -0
- package/dist/esm/components/viewers/ps-text-viewer.js +38 -0
- package/dist/esm/components/viewers/ps-unsupported-viewer.js +4 -0
- package/dist/esm/constants/ps-file-kind-icons.js +11 -0
- package/dist/esm/constants/ps-file-kind.js +9 -0
- package/dist/esm/constants/ps-file-list-labels.js +6 -0
- package/dist/esm/constants/ps-file-picker-labels.js +7 -0
- package/dist/esm/constants/ps-file-type-config.js +27 -0
- package/dist/esm/constants/ps-file-viewer-config.js +9 -0
- package/dist/esm/constants/ps-file-viewer-kinds.js +6 -0
- package/dist/esm/constants/ps-file-viewer-labels.js +21 -0
- package/dist/esm/constants/ps-upload-labels.js +14 -0
- package/dist/esm/constants/ps-upload-messages.js +4 -0
- package/dist/esm/constants/ps-upload-queue-config.js +4 -0
- package/dist/esm/constants/ps-upload-status-tones.js +9 -0
- package/dist/esm/constants/ps-upload-status.js +8 -0
- package/dist/esm/hooks/use-ps-active-match-scroll.js +8 -0
- package/dist/esm/hooks/use-ps-file-text.js +21 -0
- package/dist/esm/hooks/use-ps-in-view.js +17 -0
- package/dist/esm/hooks/use-ps-upload-queue.js +67 -0
- package/dist/esm/index.js +62 -0
- package/dist/esm/models/ps-file-rejection.js +1 -0
- package/dist/esm/models/ps-file-selection-rules.js +1 -0
- package/dist/esm/models/ps-file-selection.js +1 -0
- package/dist/esm/models/ps-file-text-state.js +1 -0
- package/dist/esm/models/ps-file-viewer-content-props.js +1 -0
- package/dist/esm/models/ps-label-set.js +1 -0
- package/dist/esm/models/ps-page-size.js +1 -0
- package/dist/esm/models/ps-pdf-match.js +1 -0
- package/dist/esm/models/ps-point.js +1 -0
- package/dist/esm/models/ps-text-match.js +1 -0
- package/dist/esm/models/ps-text-segment.js +1 -0
- package/dist/esm/models/ps-upload-handler-options.js +1 -0
- package/dist/esm/models/ps-upload-item.js +1 -0
- package/dist/esm/models/ps-upload-queue-options.js +1 -0
- package/dist/esm/models/ps-upload-queue-state.js +1 -0
- package/dist/esm/models/ps-upload-summary.js +1 -0
- package/dist/esm/models/ps-viewer-file.js +1 -0
- package/dist/esm/models/ps-xhr-upload-request.js +1 -0
- package/dist/esm/models/ps-xhr-upload-response.js +1 -0
- package/dist/esm/services/ps-csv-service.js +42 -0
- package/dist/esm/services/ps-file-selection-service.js +16 -0
- package/dist/esm/services/ps-file-size-service.js +14 -0
- package/dist/esm/services/ps-file-type-service.js +38 -0
- package/dist/esm/services/ps-pdf-service.js +59 -0
- package/dist/esm/services/ps-text-search-service.js +32 -0
- package/dist/esm/services/ps-upload-queue-service.js +37 -0
- package/dist/esm/services/ps-xhr-upload-service.js +52 -0
- package/dist/hooks/use-ps-active-match-scroll.d.ts +2 -0
- package/dist/hooks/use-ps-active-match-scroll.js +12 -0
- package/dist/hooks/use-ps-file-text.d.ts +2 -0
- package/dist/hooks/use-ps-file-text.js +25 -0
- package/dist/hooks/use-ps-in-view.d.ts +2 -0
- package/dist/hooks/use-ps-in-view.js +21 -0
- package/dist/hooks/use-ps-upload-queue.d.ts +3 -0
- package/dist/hooks/use-ps-upload-queue.js +71 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +78 -0
- package/dist/models/ps-file-rejection.d.ts +4 -0
- package/dist/models/ps-file-rejection.js +2 -0
- package/dist/models/ps-file-selection-rules.d.ts +6 -0
- package/dist/models/ps-file-selection-rules.js +2 -0
- package/dist/models/ps-file-selection.d.ts +5 -0
- package/dist/models/ps-file-selection.js +2 -0
- package/dist/models/ps-file-text-state.d.ts +4 -0
- package/dist/models/ps-file-text-state.js +2 -0
- package/dist/models/ps-file-viewer-content-props.d.ts +13 -0
- package/dist/models/ps-file-viewer-content-props.js +2 -0
- package/dist/models/ps-label-set.d.ts +3 -0
- package/dist/models/ps-label-set.js +2 -0
- package/dist/models/ps-page-size.d.ts +4 -0
- package/dist/models/ps-page-size.js +2 -0
- package/dist/models/ps-pdf-match.d.ts +7 -0
- package/dist/models/ps-pdf-match.js +2 -0
- package/dist/models/ps-point.d.ts +4 -0
- package/dist/models/ps-point.js +2 -0
- package/dist/models/ps-text-match.d.ts +4 -0
- package/dist/models/ps-text-match.js +2 -0
- package/dist/models/ps-text-segment.d.ts +5 -0
- package/dist/models/ps-text-segment.js +2 -0
- package/dist/models/ps-upload-handler-options.d.ts +4 -0
- package/dist/models/ps-upload-handler-options.js +2 -0
- package/dist/models/ps-upload-item.d.ts +8 -0
- package/dist/models/ps-upload-item.js +2 -0
- package/dist/models/ps-upload-queue-options.d.ts +6 -0
- package/dist/models/ps-upload-queue-options.js +2 -0
- package/dist/models/ps-upload-queue-state.d.ts +10 -0
- package/dist/models/ps-upload-queue-state.js +2 -0
- package/dist/models/ps-upload-summary.d.ts +5 -0
- package/dist/models/ps-upload-summary.js +2 -0
- package/dist/models/ps-viewer-file.d.ts +9 -0
- package/dist/models/ps-viewer-file.js +2 -0
- package/dist/models/ps-xhr-upload-request.d.ts +8 -0
- package/dist/models/ps-xhr-upload-request.js +2 -0
- package/dist/models/ps-xhr-upload-response.d.ts +4 -0
- package/dist/models/ps-xhr-upload-response.js +2 -0
- package/dist/services/ps-csv-service.d.ts +3 -0
- package/dist/services/ps-csv-service.js +46 -0
- package/dist/services/ps-file-selection-service.d.ts +5 -0
- package/dist/services/ps-file-selection-service.js +20 -0
- package/dist/services/ps-file-size-service.d.ts +3 -0
- package/dist/services/ps-file-size-service.js +18 -0
- package/dist/services/ps-file-type-service.d.ts +6 -0
- package/dist/services/ps-file-type-service.js +42 -0
- package/dist/services/ps-pdf-service.d.ts +11 -0
- package/dist/services/ps-pdf-service.js +96 -0
- package/dist/services/ps-text-search-service.d.ts +6 -0
- package/dist/services/ps-text-search-service.js +36 -0
- package/dist/services/ps-upload-queue-service.d.ts +10 -0
- package/dist/services/ps-upload-queue-service.js +41 -0
- package/dist/services/ps-xhr-upload-service.d.ts +7 -0
- package/dist/services/ps-xhr-upload-service.js +56 -0
- package/package.json +85 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Pallasoft Proprietary License
|
|
2
|
+
|
|
3
|
+
Version 1.0, 23-Jan-2024
|
|
4
|
+
|
|
5
|
+
1. Grant of License:
|
|
6
|
+
Pallasoft grants you a non-exclusive, non-transferable, revocable license to use the software, solely for internal business purposes.
|
|
7
|
+
|
|
8
|
+
2. Restrictions:
|
|
9
|
+
You may not:
|
|
10
|
+
a. Distribute or sublicense the software to third parties.
|
|
11
|
+
b. Reverse engineer, decompile, or disassemble the software.
|
|
12
|
+
c. Remove or alter any proprietary notices or labels on the software.
|
|
13
|
+
|
|
14
|
+
3. Ownership:
|
|
15
|
+
Pallasoft retains all right, title, and interest in and to the software, including all intellectual property rights.
|
|
16
|
+
|
|
17
|
+
4. Confidentiality:
|
|
18
|
+
You agree to keep the software confidential and not disclose it to third parties.
|
|
19
|
+
|
|
20
|
+
5. Support and Updates:
|
|
21
|
+
This license does not entitle you to support or updates. Pallasoft may, at its discretion, provide support and updates.
|
|
22
|
+
|
|
23
|
+
6. Termination:
|
|
24
|
+
This license is effective until terminated. Pallasoft may terminate the license at any time if you breach any terms. Upon termination, you must cease using the software.
|
|
25
|
+
|
|
26
|
+
7. Governing Law:
|
|
27
|
+
This license is governed by the Canadian Laws. Any disputes arising out of this license shall be resolved in the courts of Canada.
|
|
28
|
+
|
|
29
|
+
8. Contact Information:
|
|
30
|
+
For inquiries regarding this license, contact operations@pallasoft.com.
|
|
31
|
+
|
|
32
|
+
[End of License]
|
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# @pallasoft/ps-file-components
|
|
2
|
+
|
|
3
|
+
Shared Pallasoft file components: a file picker, an upload queue with live progress, a file list and a viewer for images, PDFs (with search), text, CSV, audio and video.
|
|
4
|
+
|
|
5
|
+
## Scripts
|
|
6
|
+
|
|
7
|
+
- `npm run build` compiles the package into `dist`.
|
|
8
|
+
- `npm run lint` runs ESLint using the shared package baseline.
|
|
9
|
+
- `npm run format:check` checks Prettier formatting.
|
|
10
|
+
- `npm run release` publishes through semantic-release.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PsFileViewerLabels } from '../constants/ps-file-viewer-labels';
|
|
2
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
export interface PsFileDeleteConfirmProps {
|
|
4
|
+
fileName: string;
|
|
5
|
+
isDeleting: boolean;
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
labels: PsLabelSet<typeof PsFileViewerLabels>;
|
|
8
|
+
onCancel: () => void;
|
|
9
|
+
onConfirm: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const PsFileDeleteConfirm: ({ fileName, isDeleting, isOpen, labels, onCancel, onConfirm, }: PsFileDeleteConfirmProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFileDeleteConfirm = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
+
const ps_popovers_1 = require("@pallasoft/ps-popovers");
|
|
7
|
+
const PsFileDeleteConfirm = ({ fileName, isDeleting, isOpen, labels, onCancel, onConfirm, }) => ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsModal, { eyebrow: fileName, isOpen: isOpen, onClose: onCancel, size: 'sm', title: labels.deleteConfirm, children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-5', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/70', children: labels.deleteConfirmBody }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex justify-end gap-2', children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { disabled: isDeleting, onClick: onCancel, type: 'button', variant: 'ghost', children: labels.deleteCancel }), (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { isLoading: isDeleting, onClick: onConfirm, type: 'button', variant: 'danger', children: labels.delete })] })] }) }));
|
|
8
|
+
exports.PsFileDeleteConfirm = PsFileDeleteConfirm;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFileKindIcon = 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_file_kind_icons_1 = require("../constants/ps-file-kind-icons");
|
|
8
|
+
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
9
|
+
const PsFileKindIcon = ({ className, contentType, name }) => ((0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-ps-control bg-primary-soft/60 text-primary', className), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'h-5 w-5', iconKey: ps_file_kind_icons_1.PsFileKindIcons[ps_file_type_service_1.PsFileTypeService.kindOf(contentType, name)] }) }));
|
|
10
|
+
exports.PsFileKindIcon = PsFileKindIcon;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PsFileListLabels } from '../constants/ps-file-list-labels';
|
|
2
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
import type { PsViewerFile } from '../models/ps-viewer-file';
|
|
4
|
+
export interface PsFileListProps {
|
|
5
|
+
busyIds?: string[];
|
|
6
|
+
files: PsViewerFile[];
|
|
7
|
+
formatDate?: (value: string) => string;
|
|
8
|
+
labels?: Partial<PsLabelSet<typeof PsFileListLabels>>;
|
|
9
|
+
onDelete?: (file: PsViewerFile) => void;
|
|
10
|
+
onOpen: (index: number) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const PsFileList: ({ busyIds, files, formatDate, labels, onDelete, onOpen, }: PsFileListProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFileList = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
7
|
+
const ps_file_kind_1 = require("../constants/ps-file-kind");
|
|
8
|
+
const ps_file_list_labels_1 = require("../constants/ps-file-list-labels");
|
|
9
|
+
const ps_file_size_service_1 = require("../services/ps-file-size-service");
|
|
10
|
+
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
11
|
+
const ps_file_kind_icon_1 = require("./ps-file-kind-icon");
|
|
12
|
+
const PsFileList = ({ busyIds = [], files, formatDate, labels, onDelete, onOpen, }) => {
|
|
13
|
+
const text = { ...ps_file_list_labels_1.PsFileListLabels, ...labels };
|
|
14
|
+
if (files.length === 0)
|
|
15
|
+
return (0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/60', children: text.empty });
|
|
16
|
+
return ((0, jsx_runtime_1.jsx)("ul", { className: 'grid gap-2 sm:grid-cols-2', children: files.map((file, index) => {
|
|
17
|
+
const isImage = ps_file_type_service_1.PsFileTypeService.kindOf(file.contentType, file.name) === ps_file_kind_1.PsFileKind.image;
|
|
18
|
+
const meta = [
|
|
19
|
+
file.sizeBytes === undefined ? null : ps_file_size_service_1.PsFileSizeService.format(file.sizeBytes),
|
|
20
|
+
file.createdAt === undefined || formatDate === undefined
|
|
21
|
+
? null
|
|
22
|
+
: formatDate(file.createdAt),
|
|
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.jsx)("a", { "aria-label": `${text.download} ${file.name}`, className: 'ps-focus-ring inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-ink/55 transition hover:bg-primary-soft/60 hover:text-primary', download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'h-4 w-4', iconKey: ps_icons_1.PsIconKey.Download }) }), onDelete ? ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { "aria-label": `${text.delete} ${file.name}`, className: 'h-9 w-9 shrink-0 rounded-full p-0', disabled: busyIds.includes(file.id), onClick: () => onDelete(file), size: 'compact', type: 'button', variant: 'ghost', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'h-4 w-4', iconKey: ps_icons_1.PsIconKey.Trash }) })) : null] }, file.id));
|
|
25
|
+
}) }));
|
|
26
|
+
};
|
|
27
|
+
exports.PsFileList = PsFileList;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PsFilePickerLabels } from '../constants/ps-file-picker-labels';
|
|
2
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
export interface PsFilePickerProps {
|
|
4
|
+
accept: string[];
|
|
5
|
+
acceptLabel: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
dropLabel: string;
|
|
9
|
+
isLoading?: boolean;
|
|
10
|
+
labels?: Partial<PsLabelSet<typeof PsFilePickerLabels>>;
|
|
11
|
+
maxSizeBytes?: number;
|
|
12
|
+
maxSizeLabel?: string;
|
|
13
|
+
multiple?: boolean;
|
|
14
|
+
onError?: (message: string) => void;
|
|
15
|
+
onFileLoad?: (file: File) => void;
|
|
16
|
+
onFilesSelect?: (files: File[]) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare const PsFilePicker: ({ accept, acceptLabel, className, disabled, dropLabel, isLoading, labels, maxSizeBytes, maxSizeLabel, multiple, onError, onFileLoad, onFilesSelect, }: PsFilePickerProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFilePicker = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
8
|
+
const ps_file_picker_labels_1 = require("../constants/ps-file-picker-labels");
|
|
9
|
+
const ps_file_selection_service_1 = require("../services/ps-file-selection-service");
|
|
10
|
+
const PsFilePicker = ({ accept, acceptLabel, className, disabled = false, dropLabel, isLoading = false, labels, maxSizeBytes, maxSizeLabel, multiple = false, onError, onFileLoad, onFilesSelect, }) => {
|
|
11
|
+
const [isDragging, setIsDragging] = (0, react_1.useState)(false);
|
|
12
|
+
const acceptAttr = (0, react_1.useMemo)(() => accept.join(','), [accept]);
|
|
13
|
+
const text = { ...ps_file_picker_labels_1.PsFilePickerLabels, ...labels };
|
|
14
|
+
if (isLoading)
|
|
15
|
+
return ((0, jsx_runtime_1.jsx)("span", { "aria-busy": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('ps-skeleton block min-h-32 w-full rounded-ps-panel', className), role: 'status' }));
|
|
16
|
+
const select = (list) => {
|
|
17
|
+
if (list === null || disabled)
|
|
18
|
+
return;
|
|
19
|
+
const files = multiple ? Array.from(list) : Array.from(list).slice(0, 1);
|
|
20
|
+
const { accepted, rejected } = ps_file_selection_service_1.PsFileSelectionService.split(files, {
|
|
21
|
+
accept,
|
|
22
|
+
invalidTypeMessage: text.invalidType,
|
|
23
|
+
maxSizeBytes,
|
|
24
|
+
tooLargeMessage: maxSizeLabel ? `${text.tooLarge} ${maxSizeLabel}.` : text.tooLarge,
|
|
25
|
+
});
|
|
26
|
+
rejected.forEach((rejection) => onError?.(rejection.message));
|
|
27
|
+
if (accepted.length === 0)
|
|
28
|
+
return;
|
|
29
|
+
if (onFilesSelect)
|
|
30
|
+
onFilesSelect(accepted);
|
|
31
|
+
else
|
|
32
|
+
onFileLoad?.(accepted[0]);
|
|
33
|
+
};
|
|
34
|
+
const onChange = (event) => {
|
|
35
|
+
select(event.target.files);
|
|
36
|
+
event.target.value = '';
|
|
37
|
+
};
|
|
38
|
+
const onDrag = (event, isOver) => {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
if (!disabled)
|
|
41
|
+
setIsDragging(isOver);
|
|
42
|
+
};
|
|
43
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('relative flex h-48 w-full flex-col items-center justify-center rounded-lg border-2 border-dashed border-line p-4 transition-all hover:border-primary/50 hover:bg-primary/5 sm:h-64', isDragging ? 'border-primary bg-primary/10' : '', disabled ? 'pointer-events-none opacity-60' : '', className), onDragEnter: (event) => onDrag(event, true), onDragLeave: (event) => onDrag(event, false), onDragOver: (event) => onDrag(event, true), onDrop: (event) => {
|
|
44
|
+
onDrag(event, false);
|
|
45
|
+
select(event.dataTransfer.files);
|
|
46
|
+
}, children: [(0, jsx_runtime_1.jsxs)("div", { className: 'pointer-events-none flex flex-col items-center gap-3 text-center', children: [(0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { className: 'h-8 w-8 text-primary', iconKey: ps_icons_1.PsIconKey.Upload }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-body-2 font-medium text-ink sm:text-body-1', children: isDragging ? text.dropActive : dropLabel }), (0, jsx_runtime_1.jsx)("p", { className: 'mt-1 text-body-3 text-ink/60 sm:text-body-2', children: text.browse }), maxSizeLabel ? (0, jsx_runtime_1.jsx)("p", { className: 'mt-3 text-body-3 text-ink/60', children: maxSizeLabel }) : null, (0, jsx_runtime_1.jsxs)("p", { className: 'text-body-3 text-ink/60', children: [text.formats, " ", acceptLabel] })] })] }), (0, jsx_runtime_1.jsx)("input", { accept: acceptAttr, "aria-disabled": disabled, "aria-label": dropLabel, className: 'absolute inset-0 h-full w-full cursor-pointer opacity-0', disabled: disabled, multiple: multiple, onChange: onChange, type: 'file' })] }));
|
|
47
|
+
};
|
|
48
|
+
exports.PsFilePicker = PsFilePicker;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface PsFileViewerButtonProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
className?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
label: string;
|
|
7
|
+
onClick: () => void;
|
|
8
|
+
tone?: 'danger' | 'default';
|
|
9
|
+
}
|
|
10
|
+
export declare const PsFileViewerButton: ({ children, className, disabled, label, onClick, tone, }: PsFileViewerButtonProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFileViewerButton = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
6
|
+
const PsFileViewerButton = ({ children, className, disabled, label, onClick, tone = 'default', }) => ((0, jsx_runtime_1.jsx)("button", { "aria-label": label, className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring inline-flex h-9 min-w-9 shrink-0 items-center justify-center gap-1.5 rounded-full px-2 text-sm text-white/80 transition hover:bg-white/15 hover:text-white disabled:pointer-events-none disabled:opacity-40', tone === 'danger' ? 'hover:bg-danger/80' : '', className), disabled: disabled, onClick: onClick, title: label, type: 'button', children: children }));
|
|
7
|
+
exports.PsFileViewerButton = PsFileViewerButton;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFileViewerContent = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_file_kind_1 = require("../constants/ps-file-kind");
|
|
6
|
+
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
7
|
+
const ps_image_viewer_1 = require("./viewers/ps-image-viewer");
|
|
8
|
+
const ps_media_viewer_1 = require("./viewers/ps-media-viewer");
|
|
9
|
+
const ps_pdf_viewer_1 = require("./viewers/ps-pdf-viewer");
|
|
10
|
+
const ps_text_viewer_1 = require("./viewers/ps-text-viewer");
|
|
11
|
+
const ps_unsupported_viewer_1 = require("./viewers/ps-unsupported-viewer");
|
|
12
|
+
const PsFileViewerContent = (props) => {
|
|
13
|
+
switch (ps_file_type_service_1.PsFileTypeService.kindOf(props.file.contentType, props.file.name)) {
|
|
14
|
+
case ps_file_kind_1.PsFileKind.image:
|
|
15
|
+
return (0, jsx_runtime_1.jsx)(ps_image_viewer_1.PsImageViewer, { ...props });
|
|
16
|
+
case ps_file_kind_1.PsFileKind.pdf:
|
|
17
|
+
return (0, jsx_runtime_1.jsx)(ps_pdf_viewer_1.PsPdfViewer, { ...props });
|
|
18
|
+
case ps_file_kind_1.PsFileKind.csv:
|
|
19
|
+
case ps_file_kind_1.PsFileKind.text:
|
|
20
|
+
return (0, jsx_runtime_1.jsx)(ps_text_viewer_1.PsTextViewer, { ...props });
|
|
21
|
+
case ps_file_kind_1.PsFileKind.audio:
|
|
22
|
+
case ps_file_kind_1.PsFileKind.video:
|
|
23
|
+
return (0, jsx_runtime_1.jsx)(ps_media_viewer_1.PsMediaViewer, { ...props });
|
|
24
|
+
default:
|
|
25
|
+
return (0, jsx_runtime_1.jsx)(ps_unsupported_viewer_1.PsUnsupportedViewer, { ...props });
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.PsFileViewerContent = PsFileViewerContent;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PsFileViewerLabels } from '../constants/ps-file-viewer-labels';
|
|
2
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
import type { PsViewerFile } from '../models/ps-viewer-file';
|
|
4
|
+
export interface PsFileViewerProps {
|
|
5
|
+
files: PsViewerFile[];
|
|
6
|
+
index: number;
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
labels?: Partial<PsLabelSet<typeof PsFileViewerLabels>>;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
onDelete?: (file: PsViewerFile) => Promise<void> | void;
|
|
11
|
+
onIndexChange: (index: number) => void;
|
|
12
|
+
pdfWorkerSrc?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const PsFileViewer: ({ files, index, isOpen, labels, onClose, onDelete, onIndexChange, pdfWorkerSrc, }: PsFileViewerProps) => import("react").ReactPortal | null;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFileViewer = 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_design_system_1 = require("@pallasoft/ps-design-system");
|
|
8
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
9
|
+
const react_dom_1 = require("react-dom");
|
|
10
|
+
const ps_file_viewer_config_1 = require("../constants/ps-file-viewer-config");
|
|
11
|
+
const ps_file_viewer_kinds_1 = require("../constants/ps-file-viewer-kinds");
|
|
12
|
+
const ps_file_viewer_labels_1 = require("../constants/ps-file-viewer-labels");
|
|
13
|
+
const ps_file_size_service_1 = require("../services/ps-file-size-service");
|
|
14
|
+
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
15
|
+
const ps_file_delete_confirm_1 = require("./ps-file-delete-confirm");
|
|
16
|
+
const ps_file_viewer_button_1 = require("./ps-file-viewer-button");
|
|
17
|
+
const ps_file_viewer_content_1 = require("./ps-file-viewer-content");
|
|
18
|
+
const PsFileViewer = ({ files, index, isOpen, labels, onClose, onDelete, onIndexChange, pdfWorkerSrc, }) => {
|
|
19
|
+
const text = { ...ps_file_viewer_labels_1.PsFileViewerLabels, ...labels };
|
|
20
|
+
const { isClosing, shouldRender } = (0, ps_core_components_1.usePsExitTransition)(isOpen);
|
|
21
|
+
const zIndex = (0, ps_core_components_1.usePsLayerZIndex)(ps_design_system_1.PallasoftZIndex.Modal);
|
|
22
|
+
const [zoom, setZoom] = (0, react_1.useState)(1);
|
|
23
|
+
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
|
+
const [isConfirming, setIsConfirming] = (0, react_1.useState)(false);
|
|
28
|
+
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
|
+
const file = files.length === 0 ? null : files[Math.min(index, files.length - 1)];
|
|
32
|
+
const kind = file === null ? null : ps_file_type_service_1.PsFileTypeService.kindOf(file.contentType, file.name);
|
|
33
|
+
const canSearch = kind !== null && ps_file_viewer_kinds_1.PsFileViewerKinds.searchable.includes(kind);
|
|
34
|
+
const canZoom = kind !== null && ps_file_viewer_kinds_1.PsFileViewerKinds.zoomable.includes(kind);
|
|
35
|
+
const canRotate = kind !== null && ps_file_viewer_kinds_1.PsFileViewerKinds.rotatable.includes(kind);
|
|
36
|
+
(0, react_1.useEffect)(() => {
|
|
37
|
+
setZoom(1);
|
|
38
|
+
setRotation(0);
|
|
39
|
+
setSearch('');
|
|
40
|
+
setActiveMatch(0);
|
|
41
|
+
setMatchCount(0);
|
|
42
|
+
}, [file === null ? null : file.id]);
|
|
43
|
+
(0, react_1.useEffect)(() => setActiveMatch(0), [query]);
|
|
44
|
+
const go = (step) => {
|
|
45
|
+
if (files.length > 1)
|
|
46
|
+
onIndexChange((index + step + files.length) % files.length);
|
|
47
|
+
};
|
|
48
|
+
const stepMatch = (step) => {
|
|
49
|
+
if (matchCount > 0)
|
|
50
|
+
setActiveMatch((current) => (current + step + matchCount) % matchCount);
|
|
51
|
+
};
|
|
52
|
+
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
|
+
(0, react_1.useEffect)(() => {
|
|
54
|
+
if (!isOpen)
|
|
55
|
+
return;
|
|
56
|
+
const onKey = (event) => {
|
|
57
|
+
const isTyping = event.target instanceof HTMLInputElement;
|
|
58
|
+
if (event.key === 'Escape' && !isConfirming)
|
|
59
|
+
onClose();
|
|
60
|
+
else if ((event.metaKey || event.ctrlKey) && event.key === 'f' && canSearch) {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
searchInput.current?.focus();
|
|
63
|
+
}
|
|
64
|
+
else if (isTyping)
|
|
65
|
+
return;
|
|
66
|
+
else if (event.key === 'ArrowRight')
|
|
67
|
+
go(1);
|
|
68
|
+
else if (event.key === 'ArrowLeft')
|
|
69
|
+
go(-1);
|
|
70
|
+
else if (event.key === '+' || event.key === '=')
|
|
71
|
+
changeZoom(ps_file_viewer_config_1.PsFileViewerConfig.zoomStep);
|
|
72
|
+
else if (event.key === '-')
|
|
73
|
+
changeZoom(-ps_file_viewer_config_1.PsFileViewerConfig.zoomStep);
|
|
74
|
+
};
|
|
75
|
+
window.addEventListener('keydown', onKey);
|
|
76
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
77
|
+
}, [isOpen, isConfirming, canSearch, index, files.length]);
|
|
78
|
+
if (!shouldRender || file === null || typeof document === 'undefined')
|
|
79
|
+
return null;
|
|
80
|
+
const confirmDelete = async () => {
|
|
81
|
+
if (!onDelete)
|
|
82
|
+
return;
|
|
83
|
+
setIsDeleting(true);
|
|
84
|
+
try {
|
|
85
|
+
await onDelete(file);
|
|
86
|
+
setIsConfirming(false);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
setIsDeleting(false);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { "aria-label": file.name, "aria-modal": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('fixed inset-0 flex flex-col bg-slate-950/95 text-white backdrop-blur-md', isClosing ? 'animate-ps-fade-out' : 'animate-ps-fade-in'), role: 'dialog', style: { zIndex }, children: (0, jsx_runtime_1.jsxs)(ps_core_components_1.PsLayer, { zIndex: zIndex, children: [(0, jsx_runtime_1.jsxs)("header", { className: 'flex shrink-0 flex-wrap items-center gap-2 border-b border-white/10 px-3 py-2 sm:px-4', children: [(0, jsx_runtime_1.jsx)("div", { className: 'flex min-w-0 flex-1 items-center gap-3', children: (0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0', children: [(0, jsx_runtime_1.jsx)("p", { className: 'truncate text-sm font-semibold', children: file.name }), (0, jsx_runtime_1.jsx)("p", { className: 'truncate text-xs text-white/55', children: [
|
|
93
|
+
files.length > 1 ? `${index + 1} / ${files.length}` : null,
|
|
94
|
+
file.sizeBytes === undefined ? null : ps_file_size_service_1.PsFileSizeService.format(file.sizeBytes),
|
|
95
|
+
]
|
|
96
|
+
.filter((value) => value !== null)
|
|
97
|
+
.join(' · ') })] }) }), canSearch ? ((0, jsx_runtime_1.jsxs)("div", { className: 'order-last flex w-full items-center gap-1 rounded-full bg-white/10 px-3 sm:order-none sm:w-72', 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": text.search, className: 'h-9 min-w-0 flex-1 bg-transparent text-sm text-white placeholder:text-white/45 focus:outline-none', onChange: (event) => setSearch(event.target.value), onKeyDown: (event) => {
|
|
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: 'h-4 w-4 -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: 'h-4 w-4 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: 'h-4 w-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, { 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: 'h-4 w-4', 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: 'h-4 w-4', iconKey: ps_icons_1.PsIconKey.Rotate }) })) : null, (0, jsx_runtime_1.jsx)("a", { "aria-label": text.download, className: 'ps-focus-ring inline-flex h-9 w-9 items-center justify-center rounded-full 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: 'h-4 w-4', 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: 'h-4 w-4', 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: 'h-5 w-5', 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-10 h-11 w-11 -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: 'h-5 w-5 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-10 h-11 w-11 -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: 'h-5 w-5', 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
|
+
};
|
|
102
|
+
exports.PsFileViewer = PsFileViewer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsProgressBar = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
6
|
+
const toneClassNames = {
|
|
7
|
+
danger: 'bg-danger',
|
|
8
|
+
primary: 'bg-primary',
|
|
9
|
+
success: 'bg-success',
|
|
10
|
+
};
|
|
11
|
+
const PsProgressBar = ({ className, label, tone = 'primary', value, }) => {
|
|
12
|
+
const percent = Math.round(Math.min(1, Math.max(0, value)) * 100);
|
|
13
|
+
return ((0, jsx_runtime_1.jsx)("div", { "aria-label": label, "aria-valuemax": 100, "aria-valuemin": 0, "aria-valuenow": percent, className: ps_design_system_1.PallasoftClassNameService.merge('h-1.5 w-full overflow-hidden rounded-full bg-line/70', className), role: 'progressbar', children: (0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('h-full rounded-full transition-[width] duration-200 ease-out', toneClassNames[tone]), style: { width: `${percent}%` } }) }));
|
|
14
|
+
};
|
|
15
|
+
exports.PsProgressBar = PsProgressBar;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PsUploadLabels } from '../constants/ps-upload-labels';
|
|
2
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
import type { PsUploadQueueState } from '../models/ps-upload-queue-state';
|
|
4
|
+
export interface PsUploadModalProps {
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
labels?: Partial<PsLabelSet<typeof PsUploadLabels>>;
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
queue: PsUploadQueueState;
|
|
9
|
+
}
|
|
10
|
+
export declare const PsUploadModal: ({ isOpen, labels, onClose, queue }: PsUploadModalProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsUploadModal = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
+
const ps_popovers_1 = require("@pallasoft/ps-popovers");
|
|
7
|
+
const ps_upload_labels_1 = require("../constants/ps-upload-labels");
|
|
8
|
+
const ps_upload_queue_service_1 = require("../services/ps-upload-queue-service");
|
|
9
|
+
const ps_progress_bar_1 = require("./ps-progress-bar");
|
|
10
|
+
const ps_upload_row_1 = require("./ps-upload-row");
|
|
11
|
+
const PsUploadModal = ({ isOpen, labels, onClose, queue }) => {
|
|
12
|
+
const text = { ...ps_upload_labels_1.PsUploadLabels, ...labels };
|
|
13
|
+
const summary = ps_upload_queue_service_1.PsUploadQueueService.summary(queue.items);
|
|
14
|
+
const overall = queue.items.length === 0
|
|
15
|
+
? 0
|
|
16
|
+
: queue.items.reduce((total, item) => total + item.progress, 0) / queue.items.length;
|
|
17
|
+
return ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsModal, { eyebrow: `${summary.done} / ${summary.total}`, isOpen: isOpen, onClose: onClose, size: 'md', title: queue.isBusy ? text.title : text.titleDone, children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-4', children: [(0, jsx_runtime_1.jsx)(ps_progress_bar_1.PsProgressBar, { label: text.title, tone: summary.failed > 0 ? 'danger' : queue.isBusy ? 'primary' : 'success', value: overall }), (0, jsx_runtime_1.jsx)("ul", { className: 'flex max-h-[50vh] flex-col divide-y divide-line/70 overflow-y-auto', children: queue.items.map((item) => ((0, jsx_runtime_1.jsx)(ps_upload_row_1.PsUploadRow, { item: item, labels: text, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) }), (0, jsx_runtime_1.jsx)("div", { className: 'flex justify-end', children: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { onClick: onClose, type: 'button', variant: queue.isBusy ? 'ghost' : 'primary', children: text.close }) })] }) }));
|
|
18
|
+
};
|
|
19
|
+
exports.PsUploadModal = PsUploadModal;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PsUploadLabels } from '../constants/ps-upload-labels';
|
|
2
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
import type { PsUploadItem } from '../models/ps-upload-item';
|
|
4
|
+
export interface PsUploadRowProps {
|
|
5
|
+
item: PsUploadItem;
|
|
6
|
+
labels: PsLabelSet<typeof PsUploadLabels>;
|
|
7
|
+
onCancel: () => void;
|
|
8
|
+
onRemove: () => void;
|
|
9
|
+
onRetry: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const PsUploadRow: ({ item, labels, onCancel, onRemove, onRetry }: PsUploadRowProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsUploadRow = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
7
|
+
const ps_upload_status_1 = require("../constants/ps-upload-status");
|
|
8
|
+
const ps_upload_status_tones_1 = require("../constants/ps-upload-status-tones");
|
|
9
|
+
const ps_file_size_service_1 = require("../services/ps-file-size-service");
|
|
10
|
+
const ps_upload_queue_service_1 = require("../services/ps-upload-queue-service");
|
|
11
|
+
const ps_file_kind_icon_1 = require("./ps-file-kind-icon");
|
|
12
|
+
const ps_progress_bar_1 = require("./ps-progress-bar");
|
|
13
|
+
const PsUploadRow = ({ item, labels, onCancel, onRemove, onRetry }) => {
|
|
14
|
+
const isActive = ps_upload_queue_service_1.PsUploadQueueService.isActive(item);
|
|
15
|
+
const canRetry = item.status === ps_upload_status_1.PsUploadStatus.failed || item.status === ps_upload_status_1.PsUploadStatus.cancelled;
|
|
16
|
+
const percent = Math.round(item.progress * 100);
|
|
17
|
+
const statusText = item.status === ps_upload_status_1.PsUploadStatus.uploading
|
|
18
|
+
? `${labels.uploading} ${percent}%`
|
|
19
|
+
: item.status === ps_upload_status_1.PsUploadStatus.failed && item.error
|
|
20
|
+
? item.error
|
|
21
|
+
: labels[item.status];
|
|
22
|
+
return ((0, jsx_runtime_1.jsxs)("li", { className: 'flex items-center gap-3 py-3', children: [(0, jsx_runtime_1.jsx)(ps_file_kind_icon_1.PsFileKindIcon, { contentType: item.file.type, name: item.file.name }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 flex-1 flex-col gap-1.5', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex items-baseline justify-between gap-3', children: [(0, jsx_runtime_1.jsx)("p", { className: 'truncate text-sm font-medium text-ink', children: item.file.name }), (0, jsx_runtime_1.jsx)("span", { className: 'shrink-0 text-xs text-ink/55', children: ps_file_size_service_1.PsFileSizeService.format(item.file.size) })] }), (0, jsx_runtime_1.jsx)(ps_progress_bar_1.PsProgressBar, { label: item.file.name, tone: ps_upload_status_tones_1.PsUploadStatusTones[item.status], value: item.status === ps_upload_status_1.PsUploadStatus.failed ? 1 : item.progress }), (0, jsx_runtime_1.jsxs)("p", { className: item.status === ps_upload_status_1.PsUploadStatus.failed || item.status === ps_upload_status_1.PsUploadStatus.cancelled
|
|
23
|
+
? 'text-xs text-danger'
|
|
24
|
+
: item.status === ps_upload_status_1.PsUploadStatus.done
|
|
25
|
+
? 'flex items-center gap-1 text-xs text-success'
|
|
26
|
+
: 'text-xs text-ink/60', role: item.status === ps_upload_status_1.PsUploadStatus.failed ? 'alert' : undefined, children: [item.status === ps_upload_status_1.PsUploadStatus.done ? ((0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'h-3.5 w-3.5', iconKey: ps_icons_1.PsIconKey.Check })) : null, statusText] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 items-center gap-1', children: [canRetry ? ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'h-4 w-4', iconKey: ps_icons_1.PsIconKey.Refresh }), onClick: onRetry, size: 'compact', type: 'button', variant: 'ghost', children: labels.retry })) : null, (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { "aria-label": `${isActive ? labels.cancel : labels.remove} ${item.file.name}`, onClick: isActive ? onCancel : onRemove, size: 'compact', type: 'button', variant: 'ghost', children: isActive ? labels.cancel : labels.remove })] })] }));
|
|
27
|
+
};
|
|
28
|
+
exports.PsUploadRow = PsUploadRow;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PsTextSegment } from '../../models/ps-text-segment';
|
|
2
|
+
export interface PsHighlightedTextProps {
|
|
3
|
+
activeMatch: number;
|
|
4
|
+
segments: PsTextSegment[];
|
|
5
|
+
}
|
|
6
|
+
export declare const PsHighlightedText: ({ activeMatch, segments }: PsHighlightedTextProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsHighlightedText = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const PsHighlightedText = ({ activeMatch, segments }) => ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: segments.map((segment, index) => segment.isMatch ? ((0, jsx_runtime_1.jsx)("mark", { className: segment.matchIndex === activeMatch
|
|
6
|
+
? 'rounded-sm bg-warning px-0.5 text-ink ring-2 ring-warning'
|
|
7
|
+
: 'rounded-sm bg-warning/40 px-0.5 text-inherit', "data-ps-match": segment.matchIndex === null ? undefined : segment.matchIndex, children: segment.text }, index)) : ((0, jsx_runtime_1.jsx)("span", { children: segment.text }, index))) }));
|
|
8
|
+
exports.PsHighlightedText = PsHighlightedText;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsImageViewer = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const PsImageViewer = ({ file, labels, rotation, zoom }) => {
|
|
7
|
+
const [offset, setOffset] = (0, react_1.useState)({ x: 0, y: 0 });
|
|
8
|
+
const [hasFailed, setHasFailed] = (0, react_1.useState)(false);
|
|
9
|
+
const drag = (0, react_1.useRef)(null);
|
|
10
|
+
(0, react_1.useEffect)(() => {
|
|
11
|
+
setOffset({ x: 0, y: 0 });
|
|
12
|
+
setHasFailed(false);
|
|
13
|
+
}, [file.id, zoom === 1]);
|
|
14
|
+
const onPointerDown = (event) => {
|
|
15
|
+
if (zoom <= 1)
|
|
16
|
+
return;
|
|
17
|
+
drag.current = { x: event.clientX - offset.x, y: event.clientY - offset.y };
|
|
18
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
19
|
+
};
|
|
20
|
+
const onPointerMove = (event) => {
|
|
21
|
+
if (drag.current === null)
|
|
22
|
+
return;
|
|
23
|
+
setOffset({ x: event.clientX - drag.current.x, y: event.clientY - drag.current.y });
|
|
24
|
+
};
|
|
25
|
+
if (hasFailed)
|
|
26
|
+
return (0, jsx_runtime_1.jsx)("p", { className: 'm-auto text-sm text-white/70', children: labels.loadFailed });
|
|
27
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `flex h-full w-full touch-none items-center justify-center overflow-hidden ${zoom > 1 ? 'cursor-grab active:cursor-grabbing' : ''}`, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: () => {
|
|
28
|
+
drag.current = null;
|
|
29
|
+
}, 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), src: file.url, style: {
|
|
30
|
+
transform: `translate(${offset.x}px, ${offset.y}px) scale(${zoom}) rotate(${rotation}deg)`,
|
|
31
|
+
} }) }));
|
|
32
|
+
};
|
|
33
|
+
exports.PsImageViewer = PsImageViewer;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsMediaViewer = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_file_kind_1 = require("../../constants/ps-file-kind");
|
|
6
|
+
const ps_file_type_service_1 = require("../../services/ps-file-type-service");
|
|
7
|
+
const PsMediaViewer = ({ file }) => ps_file_type_service_1.PsFileTypeService.kindOf(file.contentType, file.name) === ps_file_kind_1.PsFileKind.video ? ((0, jsx_runtime_1.jsx)("video", { className: 'm-auto max-h-full max-w-full rounded-lg shadow-2xl', controls: true, src: file.url })) : ((0, jsx_runtime_1.jsx)("audio", { className: 'm-auto w-full max-w-xl', controls: true, src: file.url }));
|
|
8
|
+
exports.PsMediaViewer = PsMediaViewer;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PDFDocumentProxy } from 'pdfjs-dist';
|
|
2
|
+
import type { PsPdfMatch } from '../../models/ps-pdf-match';
|
|
3
|
+
export interface PsPdfPageProps {
|
|
4
|
+
activeMatch: PsPdfMatch | null;
|
|
5
|
+
document: PDFDocumentProxy;
|
|
6
|
+
label: string;
|
|
7
|
+
matches: PsPdfMatch[];
|
|
8
|
+
pageNumber: number;
|
|
9
|
+
rotation: number;
|
|
10
|
+
scale: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const PsPdfPage: ({ activeMatch, document, label, matches, pageNumber, rotation, scale, }: PsPdfPageProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsPdfPage = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const use_ps_in_view_1 = require("../../hooks/use-ps-in-view");
|
|
7
|
+
const ps_pdf_service_1 = require("../../services/ps-pdf-service");
|
|
8
|
+
const PsPdfPage = ({ activeMatch, document, label, matches, pageNumber, rotation, scale, }) => {
|
|
9
|
+
const canvas = (0, react_1.useRef)(null);
|
|
10
|
+
const [container, isInView] = (0, use_ps_in_view_1.usePsInView)('600px');
|
|
11
|
+
const [size, setSize] = (0, react_1.useState)(null);
|
|
12
|
+
(0, react_1.useEffect)(() => {
|
|
13
|
+
let isCurrent = true;
|
|
14
|
+
let task = null;
|
|
15
|
+
document.getPage(pageNumber).then((page) => {
|
|
16
|
+
if (!isCurrent)
|
|
17
|
+
return;
|
|
18
|
+
const viewport = page.getViewport({ rotation: page.rotate + rotation, scale });
|
|
19
|
+
setSize({ height: viewport.height, width: viewport.width });
|
|
20
|
+
if (isInView && canvas.current)
|
|
21
|
+
task = ps_pdf_service_1.PsPdfService.render(page, canvas.current, scale, rotation);
|
|
22
|
+
});
|
|
23
|
+
return () => {
|
|
24
|
+
isCurrent = false;
|
|
25
|
+
task?.cancel();
|
|
26
|
+
};
|
|
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
|
|
29
|
+
? 'pointer-events-none absolute rounded-sm bg-warning/60 ring-2 ring-warning'
|
|
30
|
+
: 'pointer-events-none absolute rounded-sm bg-warning/35', style: {
|
|
31
|
+
height: `${match.height * 100}%`,
|
|
32
|
+
left: `${match.left * 100}%`,
|
|
33
|
+
top: `${match.top * 100}%`,
|
|
34
|
+
width: `${match.width * 100}%`,
|
|
35
|
+
} }, index)))] }));
|
|
36
|
+
};
|
|
37
|
+
exports.PsPdfPage = PsPdfPage;
|