@monolith-forensics/monolith-ui 1.2.19 → 1.2.21

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.
@@ -0,0 +1,12 @@
1
+ export type File = {
2
+ name: string;
3
+ ext: string;
4
+ size: number;
5
+ type?: string;
6
+ url: string;
7
+ };
8
+ export declare const FileViewer: React.FC<{
9
+ file: File;
10
+ open: boolean;
11
+ onClose: () => void;
12
+ }>;
@@ -0,0 +1,156 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import styled from "styled-components";
3
+ import { CodeViewer, ImageViewer, OfficeViewer, PdfViewer, VideoViewer, } from "./viewers";
4
+ import { FloatingPortal } from "@floating-ui/react";
5
+ import { DownloadIcon, XIcon, ZoomInIcon, ZoomOutIcon } from "lucide-react";
6
+ import Button from "../Button";
7
+ import { useState } from "react";
8
+ var ViewerTypes;
9
+ (function (ViewerTypes) {
10
+ ViewerTypes["Office"] = "office";
11
+ ViewerTypes["Image"] = "image";
12
+ ViewerTypes["Video"] = "video";
13
+ ViewerTypes["Audio"] = "audio";
14
+ ViewerTypes["PDF"] = "pdf";
15
+ ViewerTypes["Text"] = "text";
16
+ ViewerTypes["Code"] = "code";
17
+ })(ViewerTypes || (ViewerTypes = {}));
18
+ const ZoomableViewerTypes = [
19
+ ViewerTypes.Image,
20
+ ViewerTypes.PDF,
21
+ ViewerTypes.Code,
22
+ ViewerTypes.Text,
23
+ ];
24
+ const RotatableViewerTypes = [ViewerTypes.Image];
25
+ const OfficeExtensions = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"];
26
+ const ImageExtensions = ["jpg", "jpeg", "png", "gif"];
27
+ const VideoExtensions = ["mp4", "webm", "mov"];
28
+ const AudioExtensions = ["mp3", "wav"];
29
+ const PDFExtensions = ["pdf"];
30
+ const PlainTextExtensions = ["txt", "log", "csv"];
31
+ const CodeExtensions = [
32
+ "js",
33
+ "ts",
34
+ "jsx",
35
+ "tsx",
36
+ "json",
37
+ "html",
38
+ "css",
39
+ "xml",
40
+ "py",
41
+ "py3",
42
+ "dymo",
43
+ ];
44
+ const StyledMenu = styled.div `
45
+ height: 50px;
46
+ min-height: 50px;
47
+ width: 100%;
48
+
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: space-between;
52
+
53
+ font-size: 14px;
54
+
55
+ padding: 0 10px;
56
+
57
+ background-color: ${(props) => props.theme.palette.background.alt};
58
+ `;
59
+ const StyledActionsMenu = styled.div `
60
+ display: flex;
61
+ flex-direction: row;
62
+ gap: 10px;
63
+ align-items: center;
64
+ `;
65
+ const StyledContainer = styled.div `
66
+ position: fixed;
67
+ top: 0;
68
+ left: 0;
69
+
70
+ display: flex;
71
+ flex-direction: column;
72
+
73
+ height: 100%;
74
+ width: 100%;
75
+
76
+ background-color: rgba(0, 0, 0, 0.5);
77
+
78
+ // blur
79
+ backdrop-filter: blur(3px);
80
+
81
+ // scroll bar
82
+ ::-webkit-scrollbar {
83
+ width: 14px;
84
+ }
85
+
86
+ ::-webkit-scrollbar-track {
87
+ background: rgba(255, 255, 255, 0.075);
88
+ border-radius: 10px;
89
+ }
90
+
91
+ ::-webkit-scrollbar-thumb {
92
+ background: #888;
93
+ border-radius: 10px;
94
+ border: 4px solid rgba(0, 0, 0, 0);
95
+ background-clip: padding-box;
96
+ }
97
+ `;
98
+ const StyledInnerContainer = styled.div `
99
+ display: flex;
100
+ flex-direction: column;
101
+ flex: 1 1 auto;
102
+
103
+ height: 0px;
104
+ width: 100%;
105
+
106
+ overflow-y: auto;
107
+
108
+ &[data-type="office"] {
109
+ margin-bottom: 10px;
110
+ }
111
+ `;
112
+ const resolveViewerType = (file) => {
113
+ let ext = file.ext;
114
+ if (!ext) {
115
+ ext = file.name.split(".").pop();
116
+ }
117
+ if (!ext) {
118
+ return ViewerTypes.Code;
119
+ }
120
+ if (OfficeExtensions.includes(ext)) {
121
+ return ViewerTypes.Office;
122
+ }
123
+ if (ImageExtensions.includes(ext)) {
124
+ return ViewerTypes.Image;
125
+ }
126
+ if (VideoExtensions.includes(ext)) {
127
+ return ViewerTypes.Video;
128
+ }
129
+ if (AudioExtensions.includes(ext)) {
130
+ return ViewerTypes.Audio;
131
+ }
132
+ if (PDFExtensions.includes(ext)) {
133
+ return ViewerTypes.PDF;
134
+ }
135
+ if (PlainTextExtensions.includes(ext)) {
136
+ return ViewerTypes.Text;
137
+ }
138
+ if (CodeExtensions.includes(ext)) {
139
+ return ViewerTypes.Code;
140
+ }
141
+ return ViewerTypes.Code;
142
+ };
143
+ export const FileViewer = ({ file, open, onClose }) => {
144
+ const [zoomFactor, setZoomFactor] = useState(1);
145
+ if (!open)
146
+ return null;
147
+ const viewerType = resolveViewerType(file);
148
+ const handleBackgroundClick = (e) => {
149
+ onClose === null || onClose === void 0 ? void 0 : onClose();
150
+ setZoomFactor(1);
151
+ };
152
+ return (_jsx(FloatingPortal, { preserveTabOrder: true, children: _jsxs(StyledContainer, { className: "mfui-FileViewer", children: [_jsxs(StyledMenu, { className: "FileViewer-menu", children: [_jsx(Button, { variant: "text", onClick: () => {
153
+ onClose === null || onClose === void 0 ? void 0 : onClose();
154
+ setZoomFactor(1);
155
+ }, children: _jsx(XIcon, { size: 18 }) }), _jsx("div", { children: file.name }), _jsxs(StyledActionsMenu, { children: [ZoomableViewerTypes.includes(viewerType) && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "text", children: _jsx(ZoomInIcon, { size: 18, onClick: () => setZoomFactor((prev) => prev * 1.1) }) }), _jsx(Button, { variant: "text", children: _jsx(ZoomOutIcon, { size: 18, onClick: () => setZoomFactor((prev) => prev * 0.9) }) })] })), _jsx("a", { href: file.url, download: file.name, children: _jsx(Button, { variant: "text", children: _jsx(DownloadIcon, { size: 18 }) }) })] })] }), _jsxs(StyledInnerContainer, { onClick: handleBackgroundClick, "data-type": viewerType, children: [viewerType === ViewerTypes.PDF && (_jsx(PdfViewer, { file: file, zoomFactor: zoomFactor })), viewerType === ViewerTypes.Image && (_jsx(ImageViewer, { file: file, zoomFactor: zoomFactor })), viewerType === ViewerTypes.Video && (_jsx(VideoViewer, { file: file, zoomFactor: zoomFactor })), viewerType === ViewerTypes.Audio && (_jsx(VideoViewer, { file: file, zoomFactor: zoomFactor })), viewerType === ViewerTypes.Office && _jsx(OfficeViewer, { file: file }), viewerType === ViewerTypes.Text && (_jsx(CodeViewer, { file: file, zoomFactor: zoomFactor })), viewerType === ViewerTypes.Code && (_jsx(CodeViewer, { file: file, zoomFactor: zoomFactor }))] })] }) }));
156
+ };
@@ -0,0 +1 @@
1
+ export * from "./FileViewer";
@@ -0,0 +1 @@
1
+ export * from "./FileViewer";
@@ -0,0 +1,5 @@
1
+ import { File } from "../FileViewer";
2
+ export declare const CodeViewer: React.FC<{
3
+ file: File;
4
+ zoomFactor?: number;
5
+ }>;
@@ -0,0 +1,99 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import { useState } from "react";
12
+ import styled from "styled-components";
13
+ import Loader from "../../Loader";
14
+ const resolveLanguage = (file) => __awaiter(void 0, void 0, void 0, function* () {
15
+ const { languages } = yield import("@codemirror/language-data");
16
+ let ext = file.ext;
17
+ if (!ext) {
18
+ ext = file.name.split(".").pop();
19
+ }
20
+ if (!ext) {
21
+ return null;
22
+ }
23
+ const l = languages.find((lang) => lang.extensions.includes(ext));
24
+ if (l) {
25
+ return yield l.load();
26
+ }
27
+ return null;
28
+ });
29
+ const getFileData = (file) => __awaiter(void 0, void 0, void 0, function* () {
30
+ const response = yield fetch(file.url);
31
+ const text = yield response.text();
32
+ return text;
33
+ });
34
+ const loadCodemirror = (file) => __awaiter(void 0, void 0, void 0, function* () {
35
+ const language = yield resolveLanguage(file);
36
+ const CodeMirror = yield import("@uiw/react-codemirror");
37
+ const { vscodeDark } = yield import("@uiw/codemirror-theme-vscode");
38
+ return { language, CodeMirror, theme: vscodeDark };
39
+ });
40
+ const StyledContainer = styled.div `
41
+ display: flex;
42
+ justify-content: center;
43
+
44
+ flex: 1 1 auto;
45
+ height: 0px;
46
+ width: 100%;
47
+
48
+ .cm-theme {
49
+ font-size: ${(props) => props.zoomFactor ? `${props.zoomFactor * 12}px` : "12px"};
50
+ width: 100%;
51
+ }
52
+
53
+ .cm-editor {
54
+ border: 1px solid ${(props) => props.theme.palette.divider};
55
+ width: 100%;
56
+ }
57
+ `;
58
+ const StyledLoader = styled.div `
59
+ display: flex;
60
+ flex-direction: column;
61
+ justify-content: center;
62
+ align-items: center;
63
+ gap: 10px;
64
+ `;
65
+ export const CodeViewer = ({ file, zoomFactor }) => {
66
+ const [CodeMirror, setCodeMirror] = useState(null);
67
+ const [language, setLanguage] = useState(null);
68
+ const [theme, setTheme] = useState(null);
69
+ const [query, setQuery] = useState({
70
+ data: null,
71
+ isLoading: false,
72
+ });
73
+ if (!query.data) {
74
+ if (!query.isLoading) {
75
+ setQuery({ data: null, isLoading: true });
76
+ getFileData(file).then((text) => {
77
+ setQuery({ data: text, isLoading: false });
78
+ });
79
+ }
80
+ }
81
+ if (!CodeMirror) {
82
+ loadCodemirror(file).then((cm) => {
83
+ setCodeMirror(cm.CodeMirror);
84
+ setLanguage(cm.language);
85
+ setTheme(cm.theme);
86
+ });
87
+ }
88
+ const loading = query.isLoading || !CodeMirror;
89
+ const extensions = [];
90
+ if (language) {
91
+ extensions.push(language);
92
+ }
93
+ if (CodeMirror) {
94
+ extensions.push(CodeMirror.EditorView.lineWrapping);
95
+ }
96
+ return (_jsxs(StyledContainer, { zoomFactor: zoomFactor, children: [loading && (_jsxs(StyledLoader, { children: [_jsx(Loader, { size: 50 }), _jsx("div", { children: "Loading File..." })] })), !loading && (_jsx(CodeMirror.default, { value: query.data || "", theme: theme, extensions: extensions, readOnly: true, editable: false, onClick: (event) => {
97
+ event.stopPropagation();
98
+ } }))] }));
99
+ };
@@ -0,0 +1,5 @@
1
+ import { File } from "../FileViewer";
2
+ export declare const ImageViewer: React.FC<{
3
+ file: File;
4
+ zoomFactor?: number;
5
+ }>;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import styled from "styled-components";
4
+ import Loader from "../../Loader";
5
+ const ImageViewerContainer = styled.div `
6
+ display: flex;
7
+ justify-content: center;
8
+
9
+ flex: 1 1 auto;
10
+ height: 0px;
11
+ `;
12
+ // Updated Image styled-component to remove max-width and max-height
13
+ const Image = styled.img `
14
+ object-fit: contain;
15
+
16
+ // set orientation
17
+ image-orientation: from-image;
18
+
19
+ border: 1px solid ${(props) => props.theme.palette.divider};
20
+ `;
21
+ const StyledLoader = styled.div `
22
+ display: flex;
23
+ flex-direction: column;
24
+ justify-content: center;
25
+ align-items: center;
26
+ gap: 10px;
27
+ `;
28
+ export const ImageViewer = ({ file, zoomFactor = 1, }) => {
29
+ const [imageLoaded, setImageLoaded] = useState(false);
30
+ const [originalDimensions, setOriginalDimensions] = useState({
31
+ naturalWidth: 0,
32
+ naturalHeight: 0,
33
+ width: 0,
34
+ height: 0,
35
+ });
36
+ const handleImageLoad = (e) => {
37
+ setOriginalDimensions((prev) => (Object.assign(Object.assign({}, prev), { width: e.target.clientWidth, height: e.target.offsetHeight })));
38
+ };
39
+ const loadImage = () => {
40
+ const img = document.createElement("img");
41
+ img.src = file.url;
42
+ img.onload = () => {
43
+ setOriginalDimensions((prev) => (Object.assign(Object.assign({}, prev), { naturalWidth: img.naturalWidth, naturalHeight: img.naturalHeight })));
44
+ setImageLoaded(true);
45
+ };
46
+ };
47
+ useEffect(() => {
48
+ loadImage();
49
+ }, [file.url]);
50
+ return (_jsxs(ImageViewerContainer, { className: "mfui-ImageViewer", children: [!imageLoaded && (_jsxs(StyledLoader, { children: [_jsx(Loader, { size: 50 }), _jsx("div", { children: "Loading Image..." })] })), imageLoaded && (_jsx(Image, { src: file.url, alt: file.name ? file.name : "Image", zoomFactor: zoomFactor, onLoad: handleImageLoad, height: zoomFactor === 1 ? "100%" : originalDimensions.height * zoomFactor, onClick: (e) => {
51
+ e.stopPropagation();
52
+ } }))] }));
53
+ };
@@ -0,0 +1,4 @@
1
+ import { File } from "../FileViewer";
2
+ export declare const OfficeViewer: React.FC<{
3
+ file: File;
4
+ }>;
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import styled from "styled-components";
3
+ import Loader from "../../Loader";
4
+ const StyledContainer = styled.div `
5
+ display: flex;
6
+ flex-direction: column;
7
+ justify-content: center;
8
+ align-items: center;
9
+
10
+ flex: 1 1 auto;
11
+ height: 0px;
12
+ width: 100%;
13
+ `;
14
+ const StyledInnerContainer = styled.div `
15
+ display: flex;
16
+ flex-direction: column;
17
+ flex: 1 1 auto;
18
+ justify-content: center;
19
+ align-items: center;
20
+
21
+ width: 100%;
22
+ max-width: 100%;
23
+
24
+ #office-iframe {
25
+ width: 100%;
26
+ height: 100%;
27
+ border: none;
28
+ }
29
+ `;
30
+ const StyledIframe = styled.iframe `
31
+ width: 100%;
32
+ height: 100%;
33
+ border: none;
34
+ `;
35
+ const resolveOfficeType = (file) => {
36
+ let ext = file.ext;
37
+ if (!ext) {
38
+ ext = file.name.split(".").pop();
39
+ }
40
+ if (!ext) {
41
+ return null;
42
+ }
43
+ switch (ext) {
44
+ case "doc":
45
+ case "docx":
46
+ return "word";
47
+ case "xls":
48
+ case "xlsx":
49
+ return "excel";
50
+ case "ppt":
51
+ case "pptx":
52
+ return "powerpoint";
53
+ default:
54
+ return null;
55
+ }
56
+ };
57
+ export const OfficeViewer = ({ file }) => {
58
+ const type = resolveOfficeType(file);
59
+ return (_jsx(StyledContainer, { children: _jsx(StyledInnerContainer, { "data-type": type, children: !(file === null || file === void 0 ? void 0 : file.url) ? (_jsx(Loader, {})) : (_jsx(StyledIframe, { id: "office-iframe", title: "office-iframe", src: `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(file.url)}` })) }) }));
60
+ };
@@ -0,0 +1,7 @@
1
+ import "react-pdf/dist/esm/Page/AnnotationLayer.css";
2
+ import "react-pdf/dist/esm/Page/TextLayer.css";
3
+ import { File } from "../FileViewer";
4
+ export declare const PdfViewer: React.FC<{
5
+ file: File;
6
+ zoomFactor?: number;
7
+ }>;
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Document, Page,
4
+ // pdfjs
5
+ } from "react-pdf";
6
+ import "react-pdf/dist/esm/Page/AnnotationLayer.css";
7
+ import "react-pdf/dist/esm/Page/TextLayer.css";
8
+ import styled from "styled-components";
9
+ import Loader from "../../Loader";
10
+ // pdfjs.GlobalWorkerOptions.workerSrc = new URL(
11
+ // "pdfjs-dist/build/pdf.worker.min.mjs",
12
+ // import.meta.url
13
+ // ).toString();
14
+ const StyledContainer = styled.div `
15
+ display: flex;
16
+ justify-content: center;
17
+
18
+ flex: 1 1 auto;
19
+ height: 0px;
20
+
21
+ &[data-loading="true"] > * {
22
+ justify-content: center;
23
+ }
24
+ `;
25
+ const StyledDocument = styled(Document) `
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: 10px;
29
+
30
+ background-color: transparent;
31
+
32
+ // width: 100%;
33
+ `;
34
+ const StyledPage = styled(Page) `
35
+ width: fit-content;
36
+ background-color: transparent;
37
+ `;
38
+ const StyledLoader = styled.div `
39
+ display: flex;
40
+ flex-direction: column;
41
+ justify-content: center;
42
+ align-items: center;
43
+ gap: 10px;
44
+ `;
45
+ export const PdfViewer = ({ file, zoomFactor = 1, }) => {
46
+ const [loading, setLoading] = useState(true);
47
+ const [numPages, setNumPages] = useState();
48
+ const [pageNumber, setPageNumber] = useState(1);
49
+ function onDocumentLoadSuccess({ numPages }) {
50
+ setLoading(false);
51
+ setNumPages(numPages);
52
+ }
53
+ // viewport width
54
+ const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) - 20;
55
+ return (_jsx(StyledContainer, { className: "PdfViewer", "data-loading": loading, children: _jsx(StyledDocument, { file: file.url, onLoadSuccess: onDocumentLoadSuccess, loading: _jsxs(StyledLoader, { children: [_jsx(Loader, { size: 50 }), _jsx("div", { children: "Loading PDF..." })] }), onClick: (e) => {
56
+ e.stopPropagation();
57
+ }, children: Array.from(new Array(numPages), (el, index) => (_jsx(StyledPage, { pageNumber: index + 1, scale: zoomFactor, width: vw }, `page_${index + 1}`))) }) }));
58
+ };
@@ -0,0 +1,4 @@
1
+ import { File } from "../FileViewer";
2
+ export declare const PlainTextViewer: React.FC<{
3
+ file: File;
4
+ }>;
@@ -0,0 +1,60 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import styled from "styled-components";
12
+ import { useState } from "react";
13
+ import Loader from "../../Loader";
14
+ const getFileData = (file) => __awaiter(void 0, void 0, void 0, function* () {
15
+ const response = yield fetch(file.url);
16
+ const text = yield response.text();
17
+ return text;
18
+ });
19
+ const StyledContainer = styled.div `
20
+ display: flex;
21
+ justify-content: center;
22
+
23
+ flex: 1 1 auto;
24
+ height: 0px;
25
+ `;
26
+ const StyledTextData = styled.div `
27
+ width: 100%;
28
+ height: fit-content;
29
+ max-width: 8.5in;
30
+ padding: 25px;
31
+
32
+ background-color: ${(props) => props.theme.palette.background.alt};
33
+ white-space: pre-wrap !important;
34
+ word-wrap: break-word !important;
35
+
36
+ font-family: monospace;
37
+ font-size: 10px;
38
+ `;
39
+ const StyledLoader = styled.div `
40
+ display: flex;
41
+ flex-direction: column;
42
+ justify-content: center;
43
+ align-items: center;
44
+ gap: 10px;
45
+ `;
46
+ export const PlainTextViewer = ({ file }) => {
47
+ const [query, setQuery] = useState({
48
+ data: null,
49
+ isLoading: false,
50
+ });
51
+ if (!query.data) {
52
+ if (!query.isLoading) {
53
+ setQuery({ data: null, isLoading: true });
54
+ getFileData(file).then((text) => {
55
+ setQuery({ data: text, isLoading: false });
56
+ });
57
+ }
58
+ }
59
+ return (_jsxs(StyledContainer, { className: "mfui-PlainTextViewer", children: [!query.data && (_jsxs(StyledLoader, { children: [_jsx(Loader, { size: 50 }), _jsx("div", { children: "Loading File..." })] })), query.data && (_jsx(StyledTextData, { className: "text-doc", children: query.data }))] }));
60
+ };
@@ -0,0 +1,5 @@
1
+ import { File } from "../FileViewer";
2
+ export declare const VideoViewer: React.FC<{
3
+ file: File;
4
+ zoomFactor?: number;
5
+ }>;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import styled from "styled-components";
3
+ const StyledContainer = styled.div `
4
+ display: flex;
5
+ justify-content: center;
6
+
7
+ flex: 1 1 auto;
8
+ height: 0px;
9
+ `;
10
+ const StyledVideo = styled.video `
11
+ max-width: 100%;
12
+ max-height: 100%;
13
+ object-fit: contain;
14
+ `;
15
+ export const VideoViewer = ({ file }) => {
16
+ return (_jsx(StyledContainer, { className: "mfui-VideoViewer", children: _jsx(StyledVideo, { src: file.url, controls: true, autoPlay: true, onClick: (e) => {
17
+ e.stopPropagation();
18
+ } }) }));
19
+ };
@@ -0,0 +1,6 @@
1
+ export * from "./ImageViewer";
2
+ export * from "./VideoViewer";
3
+ export * from "./PdfViewer";
4
+ export * from "./OfficeViewer";
5
+ export * from "./PlainTextViewer";
6
+ export * from "./CodeViewer";
@@ -0,0 +1,6 @@
1
+ export * from "./ImageViewer";
2
+ export * from "./VideoViewer";
3
+ export * from "./PdfViewer";
4
+ export * from "./OfficeViewer";
5
+ export * from "./PlainTextViewer";
6
+ export * from "./CodeViewer";
@@ -9,6 +9,7 @@ interface RichTextEditorProps {
9
9
  extensions?: ExtensionType[];
10
10
  slashCommands?: any[];
11
11
  defaultValue?: string;
12
+ value?: string;
12
13
  readOnly?: boolean;
13
14
  height?: string;
14
15
  font?: string;
@@ -21,7 +22,5 @@ interface RichTextEditorProps {
21
22
  autoFocus?: boolean;
22
23
  style?: React.CSSProperties;
23
24
  }
24
- declare const RichTextEditor: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<RichTextEditorProps & import("react").RefAttributes<unknown>, "ref"> & {
25
- ref?: ((instance: unknown) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<unknown> | null | undefined;
26
- }, never>> & string & Omit<import("react").ForwardRefExoticComponent<RichTextEditorProps & import("react").RefAttributes<unknown>>, keyof import("react").Component<any, {}, any>>;
25
+ declare const RichTextEditor: import("react").ForwardRefExoticComponent<RichTextEditorProps & import("react").RefAttributes<unknown>>;
27
26
  export default RichTextEditor;
@@ -9,88 +9,7 @@ import { startImageUpload, } from "./Plugins/UploadImagesPlugin";
9
9
  import SaveBadge from "./Components/SaveBadge";
10
10
  import Fonts from "./Enums/Fonts";
11
11
  import RichTextEditorContext from "./Contexts/RichTextEditorContext";
12
- const RichTextEditor = styled(forwardRef(({ className, editorInstanceRef, defaultValue = "", readOnly = false, font, showToolbar = true, saving = false, extensions = [], slashCommands = [], bubbleMenuOptions, toolbarOptions, autoFocus, onChange, handleImageUpload, style, }, ref) => {
13
- const [fontState, setFontState] = useState(font || Fonts.DEFAULT);
14
- // check if image extension is included
15
- if (extensions === null || extensions === void 0 ? void 0 : extensions.includes(Extensions.Image)) {
16
- // Ensure that handleImageUpload is provided
17
- if (!handleImageUpload) {
18
- throw new Error("handleImageUpload is required when using the image extension.");
19
- }
20
- }
21
- const editor = useEditor({
22
- content: defaultValue,
23
- editable: !readOnly,
24
- extensions: getTipTapExtensions({
25
- extensions,
26
- slashCommands,
27
- bubbleMenuOptions,
28
- handleImageUpload,
29
- }),
30
- editorProps: {
31
- handlePaste: (view, event) => {
32
- if (!handleImageUpload)
33
- return false;
34
- if (!(event === null || event === void 0 ? void 0 : event.clipboardData))
35
- return false;
36
- if (event.clipboardData.types.includes("text/html") ||
37
- event.clipboardData.types.includes("text/plain") ||
38
- event.clipboardData.types.includes("text/rtf"))
39
- return false;
40
- if (event.clipboardData &&
41
- event.clipboardData.files &&
42
- event.clipboardData.files[0]) {
43
- event.preventDefault();
44
- const file = event.clipboardData.files[0];
45
- const pos = view.state.selection.from;
46
- startImageUpload(file, view, pos, handleImageUpload);
47
- return true;
48
- }
49
- return false;
50
- },
51
- handleDrop: (view, event, _slice, moved) => {
52
- if (!handleImageUpload)
53
- return false;
54
- if (!moved &&
55
- event.dataTransfer &&
56
- event.dataTransfer.files &&
57
- event.dataTransfer.files[0]) {
58
- event.preventDefault();
59
- const file = event.dataTransfer.files[0];
60
- const coordinates = view.posAtCoords({
61
- left: event.clientX,
62
- top: event.clientY,
63
- });
64
- if (!coordinates)
65
- return false;
66
- // here we deduct 1 from the pos or else the image will create an extra node
67
- startImageUpload(file, view, coordinates.pos - 1, handleImageUpload);
68
- return true;
69
- }
70
- return false;
71
- },
72
- },
73
- onUpdate: ({ editor }) => {
74
- onChange === null || onChange === void 0 ? void 0 : onChange(editor.getHTML());
75
- },
76
- });
77
- useEffect(() => {
78
- const _ref = editorInstanceRef;
79
- if (editorInstanceRef) {
80
- _ref.current = editor;
81
- }
82
- }, [editor]);
83
- // AutoFocus on the editor
84
- useEffect(() => {
85
- if (autoFocus && editor) {
86
- editor.view.focus();
87
- }
88
- }, [autoFocus, editor]);
89
- return (_jsx("div", { className: className, children: _jsxs(RichTextEditorContext.Provider, { value: {
90
- font: fontState,
91
- setFont: setFontState,
92
- }, children: [showToolbar && (_jsx(Toolbar, { editor: editor, toolbarOptions: toolbarOptions })), saving && _jsx(SaveBadge, {}), _jsx(EditorContent, { className: "editor-content", editor: editor, "data-font": fontState || null, style: style })] }) }));
93
- })) `
12
+ const StyledContent = styled.div `
94
13
  position: relative;
95
14
  display: flex;
96
15
  flex-direction: column;
@@ -363,4 +282,89 @@ const RichTextEditor = styled(forwardRef(({ className, editorInstanceRef, defaul
363
282
  margin: 0 0.125rem;
364
283
  }
365
284
  `;
285
+ const RichTextEditor = forwardRef(({ className, editorInstanceRef, defaultValue = "", value = "", readOnly = false, font, showToolbar = true, saving = false, extensions = [], slashCommands = [], bubbleMenuOptions, toolbarOptions, autoFocus, onChange, handleImageUpload, style, }, ref) => {
286
+ const [fontState, setFontState] = useState(font || Fonts.DEFAULT);
287
+ // check if image extension is included
288
+ if (extensions === null || extensions === void 0 ? void 0 : extensions.includes(Extensions.Image)) {
289
+ // Ensure that handleImageUpload is provided
290
+ if (!handleImageUpload) {
291
+ throw new Error("handleImageUpload is required when using the image extension.");
292
+ }
293
+ }
294
+ const editor = useEditor({
295
+ content: defaultValue,
296
+ editable: !readOnly,
297
+ extensions: getTipTapExtensions({
298
+ extensions,
299
+ slashCommands,
300
+ bubbleMenuOptions,
301
+ handleImageUpload,
302
+ }),
303
+ editorProps: {
304
+ handlePaste: (view, event) => {
305
+ if (!handleImageUpload)
306
+ return false;
307
+ if (!(event === null || event === void 0 ? void 0 : event.clipboardData))
308
+ return false;
309
+ if (event.clipboardData.types.includes("text/html") ||
310
+ event.clipboardData.types.includes("text/plain") ||
311
+ event.clipboardData.types.includes("text/rtf"))
312
+ return false;
313
+ if (event.clipboardData &&
314
+ event.clipboardData.files &&
315
+ event.clipboardData.files[0]) {
316
+ event.preventDefault();
317
+ const file = event.clipboardData.files[0];
318
+ const pos = view.state.selection.from;
319
+ startImageUpload(file, view, pos, handleImageUpload);
320
+ return true;
321
+ }
322
+ return false;
323
+ },
324
+ handleDrop: (view, event, _slice, moved) => {
325
+ if (!handleImageUpload)
326
+ return false;
327
+ if (!moved &&
328
+ event.dataTransfer &&
329
+ event.dataTransfer.files &&
330
+ event.dataTransfer.files[0]) {
331
+ event.preventDefault();
332
+ const file = event.dataTransfer.files[0];
333
+ const coordinates = view.posAtCoords({
334
+ left: event.clientX,
335
+ top: event.clientY,
336
+ });
337
+ if (!coordinates)
338
+ return false;
339
+ // here we deduct 1 from the pos or else the image will create an extra node
340
+ startImageUpload(file, view, coordinates.pos - 1, handleImageUpload);
341
+ return true;
342
+ }
343
+ return false;
344
+ },
345
+ },
346
+ onUpdate: ({ editor }) => {
347
+ onChange === null || onChange === void 0 ? void 0 : onChange(editor.getHTML());
348
+ },
349
+ });
350
+ useEffect(() => {
351
+ const _ref = editorInstanceRef;
352
+ if (editorInstanceRef) {
353
+ _ref.current = editor;
354
+ }
355
+ }, [editor]);
356
+ // AutoFocus on the editor
357
+ useEffect(() => {
358
+ if (autoFocus && editor) {
359
+ editor.view.focus();
360
+ }
361
+ }, [autoFocus, editor]);
362
+ useEffect(() => {
363
+ editor === null || editor === void 0 ? void 0 : editor.commands.setContent(defaultValue);
364
+ }, [defaultValue]);
365
+ return (_jsx(StyledContent, { className: className, children: _jsxs(RichTextEditorContext.Provider, { value: {
366
+ font: fontState,
367
+ setFont: setFontState,
368
+ }, children: [showToolbar && (_jsx(Toolbar, { editor: editor, toolbarOptions: toolbarOptions })), saving && _jsx(SaveBadge, {}), _jsx(EditorContent, { className: "editor-content", editor: editor, "data-font": fontState || null, style: style })] }) }));
369
+ });
366
370
  export default RichTextEditor;
package/dist/index.d.ts CHANGED
@@ -35,3 +35,4 @@ export * from "./hooks";
35
35
  export { QueryFilter, useQueryFilter } from "./QueryFilter";
36
36
  export { Table } from "./Table";
37
37
  export type { ColumnProps, TableProps } from "./Table";
38
+ export * from "./FileViewer";
package/dist/index.js CHANGED
@@ -28,3 +28,4 @@ export { default as Loader } from "./Loader";
28
28
  export * from "./hooks";
29
29
  export { QueryFilter, useQueryFilter } from "./QueryFilter";
30
30
  export { Table } from "./Table";
31
+ export * from "./FileViewer";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monolith-forensics/monolith-ui",
3
- "version": "1.2.19",
3
+ "version": "1.2.21",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "author": "Matt Danner (Monolith Forensics LLC)",
@@ -25,6 +25,7 @@
25
25
  "release-major": "yarn version --major --deferred && yarn build && npm publish && yarn clean"
26
26
  },
27
27
  "dependencies": {
28
+ "@codemirror/language-data": "^6.5.1",
28
29
  "@floating-ui/react": "^0.26.16",
29
30
  "@mantine/hooks": "^7.13.0",
30
31
  "@radix-ui/react-switch": "^1.0.7",
@@ -46,6 +47,8 @@
46
47
  "@tiptap/react": "^2.5.8",
47
48
  "@tiptap/starter-kit": "^2.5.8",
48
49
  "@tiptap/suggestion": "^2.5.9",
50
+ "@uiw/codemirror-theme-vscode": "^4.23.6",
51
+ "@uiw/react-codemirror": "^4.23.6",
49
52
  "deepmerge": "^4.3.1",
50
53
  "lucide-react": "^0.378.0",
51
54
  "moment": "^2.29.1",
@@ -54,6 +57,7 @@
54
57
  "react-dom": "^18.3.1",
55
58
  "react-dropzone": "^14.2.3",
56
59
  "react-icons": "^5.2.1",
60
+ "react-pdf": "^9.1.1",
57
61
  "react-textarea-autosize": "^8.5.3",
58
62
  "react-virtualized-auto-sizer": "^1.0.24",
59
63
  "react-window": "^1.8.10",
@@ -72,6 +76,5 @@
72
76
  "@types/react-window": "^1.8.8",
73
77
  "@types/spark-md5": "^3.0.4",
74
78
  "typescript": "^5.5.4"
75
- },
76
- "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
79
+ }
77
80
  }