@powerhousedao/connect 1.1.2 → 2.5.0-test.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/.env +2 -1
- package/dist/assets/{app-D1OgCILX.css → app-Djvyy0EQ.css} +145 -2
- package/dist/assets/{app-DJhQA-n0.js → app-DnlszXoG.js} +49870 -42678
- package/dist/assets/app-DnlszXoG.js.map +1 -0
- package/dist/assets/{app-loader-4GyeoNTc.css → app-loader-CDW9U8zV.css} +179 -7
- package/dist/assets/{app-loader-_ujGNJEV.js → app-loader-LF_aPRFS.js} +3441 -3977
- package/dist/assets/app-loader-LF_aPRFS.js.map +1 -0
- package/dist/assets/browser-Bl-r87ld.js +27234 -0
- package/dist/assets/browser-Bl-r87ld.js.map +1 -0
- package/dist/assets/{ccip-CCI6GbPl.js → ccip-5fOGBE-2.js} +4 -3
- package/dist/assets/ccip-5fOGBE-2.js.map +1 -0
- package/dist/assets/{content-9ycjllj5.js → content-ChtzAUHq.js} +1762 -1049
- package/dist/assets/content-ChtzAUHq.js.map +1 -0
- package/dist/assets/{index-3G4_JsnT.js → index-B8Ch0VYb.js} +4 -3
- package/dist/assets/index-B8Ch0VYb.js.map +1 -0
- package/dist/assets/{index-C2x6Y6L5.js → index-C9SvsNTs.js} +23 -171
- package/dist/assets/index-C9SvsNTs.js.map +1 -0
- package/dist/assets/index-ttu2Jrkl.js +208 -0
- package/dist/assets/index-ttu2Jrkl.js.map +1 -0
- package/dist/assets/{index-8c1XCqhj.js → index-wgNrNkzN.js} +466 -309
- package/dist/assets/index-wgNrNkzN.js.map +1 -0
- package/dist/assets/{main.DXf9Xuwr.js → main.B-rBx6n2.js} +2 -1
- package/dist/assets/main.B-rBx6n2.js.map +1 -0
- package/dist/assets/reactor-analytics-C42Sftjd.js +42 -0
- package/dist/assets/reactor-analytics-C42Sftjd.js.map +1 -0
- package/dist/assets/router-DocwpreM.js +1791 -0
- package/dist/assets/router-DocwpreM.js.map +1 -0
- package/dist/assets/{style-U7Kx3_hE.css → style-C0GVbPTL.css} +6 -3
- package/dist/external-packages.js +1 -0
- package/dist/external-packages.js.map +1 -0
- package/dist/hmr.js +1 -0
- package/dist/hmr.js.map +1 -0
- package/dist/index.html +2 -1
- package/dist/service-worker.js +1 -0
- package/dist/service-worker.js.map +1 -0
- package/dist/swEnv.js +1 -0
- package/dist/vite-envs.sh +10 -1
- package/package.json +21 -16
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
|
|
2
|
-
import { t as twMerge, B as Button, m as mergeClassNameProps, E as ERROR, M as MISSING, C as CONFLICT, S as SUCCESS, k as SYNCING, I as INITIAL_SYNC, l as ConnectDropdownMenu, u as useOnClickOutside, n as useEventListener, o as useCopyToClipboard, p as Select, q as ENSAvatar, P as Provider, v as Root3, T as Trigger, w as Portal, x as Content2, y as validateInitialState, z as validateStateSchemaName, A as validateModules, D as useDocumentDrives, F as useUiNodesContext, G as useDriveContext, H as FILE$1, J as useUnwrappedReactor, K as useConnectDid, L as useConnectCrypto, N as useTranslation, O as useModal, Q as useAtomValue, U as themeAtom, V as useUser, W as useUserPermissions$1, X as useUiNodes, Y as exportFile, Z as useGetDocumentModelModule, _ as addActionContext$1, $ as signOperation$1, a0 as useDocumentDriveServer, a1 as useHotkeys, a2 as useGetEditor, a3 as isSameDocument, a4 as useNavigate, a5 as ErrorBoundary, a6 as DriveLayout, a7 as DriveContextProvider, a8 as SearchBar, a9 as useAsyncReactor, aa as useFilteredDocumentModels, ab as useDriveEditor, ac as useDocumentDriveById, ad as useParams, ae as useDocumentDrives$1, af as toast } from "./app-DJhQA-n0.js";
|
|
3
2
|
import * as React from "react";
|
|
4
3
|
import React__default, { useState, useCallback, useMemo, useEffect, Fragment, useRef, useLayoutEffect, memo as memo$1, createElement, useSyncExternalStore, Suspense } from "react";
|
|
5
|
-
import {
|
|
4
|
+
import { t as twMerge, B as Button, aU as mergeClassNameProps, I as Icon, aV as getDimensions, aW as useDocumentDrives, h as useUiNodesContext, aX as useDriveContext, aY as FILE, H as ERROR, aZ as MISSING, G as CONFLICT, S as SUCCESS, a_ as SYNCING, a$ as INITIAL_SYNC, b0 as READ, b1 as nodeOptionsMap, b2 as defaultFileOptions, b3 as ConnectDropdownMenu, b4 as DELETE, b5 as RENAME, b6 as WRITE, b7 as DUPLICATE, b8 as defaultFolderOptions, b9 as useOnClickOutside, ba as useEventListener, bb as useCopyToClipboard, bc as Select, bd as Provider, be as Root3, bf as Trigger, bg as Portal, bh as Content2, bi as UI_NODE, D as DRIVE, F as FILE$1, bj as validateInitialState, bk as validateStateSchemaName, bl as validateModules, e as useDocumentDriveServer, k as useUnwrappedReactor, bm as useConnectDid, bn as useConnectCrypto, x as useTranslation, f as useModal, bo as useAtomValue, bp as themeAtom, bq as useUser, br as useUserPermissions$1, bs as useFileNodeDocument, bt as useGetDocumentModelModule, bu as exportFile, bv as addActionContext$1, bw as signOperation$1, bx as useHotkeys, by as FOLDER, bz as DriveLayout, bA as DriveContextProvider, bB as SearchBar, V as useAnalyticsStore, bC as useQueryClient, u as useQuery, a8 as AnalyticsPath, bD as useAnalyticsEngine, a7 as DateTime, bE as AnalyticsGranularity, bF as useGetEditor, bG as isSameDocument, w as useNavigate, A as useAsyncReactor, bH as useFilteredDocumentModels, bI as useDriveEditor, K as useParams, y as useDocumentDrives$1 } from "./app-DnlszXoG.js";
|
|
5
|
+
import { ab as buildSignedOperation, am as generateId$1, aw as isFolderNode, aA as generateNodesCopy, aB as copyNode, az as moveNode, ay as updateNode, ax as deleteNode, ar as generateAddNodeAction, as as isFileNode, av as addFolder, bU as garbageCollect, bV as sortOperations, bW as undo, bX as redo, _ as logger, bY as openUrl, bZ as useDocumentDispatch$1, aj as module, af as connectConfig } from "./app-loader-LF_aPRFS.js";
|
|
6
|
+
import { E as ENSAvatar, u as useSwitchboard, a as ErrorBoundary, t as toast } from "./router-DocwpreM.js";
|
|
6
7
|
import { flushSync } from "react-dom";
|
|
7
|
-
import "./main.
|
|
8
|
+
import "./main.B-rBx6n2.js";
|
|
8
9
|
const PaginationButton = ({ active = false, ...props }) => {
|
|
9
10
|
const className = twMerge("h-8 min-w-8 border border-solid border-gray-300 bg-white px-3 py-1 text-xs text-gray-900 hover:bg-gray-100", !active && "border-0");
|
|
10
11
|
return jsx(Button, { color: "light", size: "small", ...mergeClassNameProps(props, className), children: props.children });
|
|
@@ -151,131 +152,12 @@ function DefaultEditorLoader(props) {
|
|
|
151
152
|
const { message: message2 = "Loading editor", ...divProps } = props;
|
|
152
153
|
return jsx("div", { className: "grid h-full place-items-center", ...divProps, children: jsxs("div", { className: "-mt-20 grid place-items-center", children: [jsx("h3", { className: "mb-4 text-xl", children: message2 }), jsx(AnimatedLoader, {})] }) });
|
|
153
154
|
}
|
|
154
|
-
const DocumentToolbar = (props) => {
|
|
155
|
-
const { undo: undo2, canUndo, redo: redo2, canRedo, title, onClose, onExport, className, onShowRevisionHistory, onSwitchboardLinkClick, onShowTimeline } = props;
|
|
156
|
-
const isUndoDisabled = !canUndo || !undo2;
|
|
157
|
-
const isRedoDisabled = !canRedo || !redo2;
|
|
158
|
-
const isExportDisabled = !onExport;
|
|
159
|
-
const isSwitchboardLinkDisabled = !onSwitchboardLinkClick;
|
|
160
|
-
const isRevisionHistoryDisabled = !onShowRevisionHistory;
|
|
161
|
-
const isTimelineDisabled = !onShowTimeline;
|
|
162
|
-
return jsxs("div", { className: twMerge("flex h-12 w-full items-center justify-between rounded-xl border border-gray-200 bg-slate-50 px-4", className), children: [jsxs("div", { className: "flex items-center gap-x-2", children: [jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isUndoDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: undo2, disabled: isUndoDisabled, children: jsx(Icon, { name: "ArrowCouterclockwise", size: 16, className: isUndoDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isRedoDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: redo2, disabled: isRedoDisabled, children: jsx("div", { className: "-scale-x-100", children: jsx(Icon, { name: "ArrowCouterclockwise", size: 16, className: isRedoDisabled ? "text-gray-500" : "text-gray-900" }) }) }), jsx("button", { className: twMerge("flex h-8 items-center rounded-lg border border-gray-200 bg-white px-3 text-sm", isExportDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onExport, disabled: isExportDisabled, children: jsx("span", { className: isExportDisabled ? "text-gray-500" : "text-gray-900", children: "Export" }) })] }), jsx("div", { className: "flex items-center", children: jsx("h1", { className: "text-sm font-medium text-gray-500", children: title }) }), jsxs("div", { className: "flex items-center gap-x-2", children: [jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isSwitchboardLinkDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onSwitchboardLinkClick, disabled: isSwitchboardLinkDisabled, children: jsx(Icon, { name: "Drive", size: 16, className: isSwitchboardLinkDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isRevisionHistoryDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onShowRevisionHistory, disabled: isRevisionHistoryDisabled, children: jsx(Icon, { name: "History", size: 16, className: isRevisionHistoryDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isTimelineDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onShowTimeline, disabled: isTimelineDisabled, children: jsx(Icon, { name: "Timeline", size: 16, className: twMerge("text-gray-900", isTimelineDisabled && "opacity-50") }) }), jsx("button", { className: "grid size-8 cursor-pointer place-items-center rounded-lg border border-gray-200 bg-white active:opacity-70", onClick: onClose, children: jsx(Icon, { name: "XmarkLight", size: 16, className: "text-gray-900" }) })] })] });
|
|
163
|
-
};
|
|
164
|
-
const syncIcons = {
|
|
165
|
-
SYNCING: "Syncing",
|
|
166
|
-
SUCCESS: "Synced",
|
|
167
|
-
CONFLICT: "Error",
|
|
168
|
-
MISSING: "Circle",
|
|
169
|
-
ERROR: "Error",
|
|
170
|
-
INITIAL_SYNC: "Syncing"
|
|
171
|
-
};
|
|
172
|
-
function SyncStatusIcon(props) {
|
|
173
|
-
const { syncStatus, className, overrideSyncIcons = {}, ...iconProps } = props;
|
|
174
|
-
const icons = { ...syncIcons, ...overrideSyncIcons };
|
|
175
|
-
const syncStatusIcons = {
|
|
176
|
-
[INITIAL_SYNC]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[INITIAL_SYNC] }),
|
|
177
|
-
[SYNCING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[SYNCING] }),
|
|
178
|
-
[SUCCESS]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-green-900", className), name: icons[SUCCESS] }),
|
|
179
|
-
[CONFLICT]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-orange-900", className), name: icons[CONFLICT] }),
|
|
180
|
-
[MISSING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[MISSING] }),
|
|
181
|
-
[ERROR]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[ERROR] })
|
|
182
|
-
};
|
|
183
|
-
return syncStatusIcons[syncStatus];
|
|
184
|
-
}
|
|
185
|
-
function FileItem(props) {
|
|
186
|
-
const { uiNode, className, customDocumentIconSrc, onSelectNode, onRenameNode, onDuplicateNode, onDeleteNode, isAllowedToCreateDocuments } = props;
|
|
187
|
-
const [mode, setMode] = useState(READ);
|
|
188
|
-
const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
|
|
189
|
-
const { dragProps } = useDrag({ uiNode });
|
|
190
|
-
const isReadMode = mode === READ;
|
|
191
|
-
const dropdownMenuHandlers = {
|
|
192
|
-
[DUPLICATE]: () => onDuplicateNode(uiNode),
|
|
193
|
-
[RENAME]: () => setMode(WRITE),
|
|
194
|
-
[DELETE]: () => onDeleteNode(uiNode)
|
|
195
|
-
};
|
|
196
|
-
const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
|
|
197
|
-
...option,
|
|
198
|
-
id
|
|
199
|
-
})).filter((option) => defaultFileOptions.includes(option.id));
|
|
200
|
-
function onSubmit(name) {
|
|
201
|
-
onRenameNode(name, uiNode);
|
|
202
|
-
setMode(READ);
|
|
203
|
-
}
|
|
204
|
-
function onCancel() {
|
|
205
|
-
setMode(READ);
|
|
206
|
-
}
|
|
207
|
-
function onClick() {
|
|
208
|
-
onSelectNode(uiNode);
|
|
209
|
-
}
|
|
210
|
-
function onDropdownMenuOptionClick(itemId) {
|
|
211
|
-
const handler = dropdownMenuHandlers[itemId];
|
|
212
|
-
if (!handler) {
|
|
213
|
-
console.error(`No handler found for dropdown menu item: ${itemId}`);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
handler();
|
|
217
|
-
setIsDropdownMenuOpen(false);
|
|
218
|
-
}
|
|
219
|
-
const iconSrc = getDocumentIconSrc(uiNode.documentType, customDocumentIconSrc);
|
|
220
|
-
const iconNode = jsxs("div", { className: "relative", children: [jsx("img", { alt: "file icon", className: "max-w-none", height: 34, src: iconSrc, width: 32 }), isReadMode && uiNode.syncStatus && jsx("div", { className: "absolute bottom-[-2px] right-0 size-3 rounded-full bg-white", children: jsx("div", { className: "absolute left-[-2px] top-[-2px]", children: jsx(SyncStatusIcon, { overrideSyncIcons: { SUCCESS: "CheckCircleFill" }, syncStatus: uiNode.syncStatus }) }) })] });
|
|
221
|
-
const containerStyles = twMerge("group flex h-12 cursor-pointer select-none items-center rounded-lg bg-gray-200 px-2 text-gray-600 hover:text-gray-800", className);
|
|
222
|
-
const content = isReadMode ? jsxs("div", { className: "flex w-52 items-center justify-between", children: [jsxs("div", { className: "mr-2 truncate group-hover:mr-0", children: [jsx("div", { className: "max-h-6 truncate text-sm font-medium group-hover:text-gray-800", children: uiNode.name }), jsx("div", { className: "max-h-6 truncate text-xs font-medium text-gray-600 group-hover:text-gray-800", children: uiNode.documentType })] }), isAllowedToCreateDocuments ? jsx(ConnectDropdownMenu, { items: dropdownMenuOptions, onItemClick: onDropdownMenuOptionClick, onOpenChange: setIsDropdownMenuOpen, open: isDropdownMenuOpen, children: jsx("button", { className: twMerge("hidden group-hover:block", isDropdownMenuOpen && "block"), onClick: (e) => {
|
|
223
|
-
e.stopPropagation();
|
|
224
|
-
setIsDropdownMenuOpen(true);
|
|
225
|
-
}, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) : jsx(NodeInput, { className: "ml-3 flex-1 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
|
|
226
|
-
return jsx("div", { className: "relative w-64", onClick, children: jsx("div", { ...dragProps, className: containerStyles, children: jsxs("div", { className: "flex items-center", children: [jsx("div", { className: "mr-1.5", children: iconNode }), content] }) }) });
|
|
227
|
-
}
|
|
228
|
-
function FolderItem(props) {
|
|
229
|
-
const { uiNode, isAllowedToCreateDocuments, className, onRenameNode, onDuplicateNode, onDeleteNode, onSelectNode, onAddFile, onCopyNode, onMoveNode } = props;
|
|
230
|
-
const [mode, setMode] = useState(READ);
|
|
231
|
-
const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
|
|
232
|
-
const { dragProps } = useDrag({ ...props, uiNode });
|
|
233
|
-
const { isDropTarget, dropProps } = useDrop({
|
|
234
|
-
uiNode,
|
|
235
|
-
onAddFile,
|
|
236
|
-
onCopyNode,
|
|
237
|
-
onMoveNode
|
|
238
|
-
});
|
|
239
|
-
const isReadMode = mode === READ;
|
|
240
|
-
function onCancel() {
|
|
241
|
-
setMode(READ);
|
|
242
|
-
}
|
|
243
|
-
function onSubmit(name) {
|
|
244
|
-
onRenameNode(name, uiNode);
|
|
245
|
-
setMode(READ);
|
|
246
|
-
}
|
|
247
|
-
function onClick() {
|
|
248
|
-
onSelectNode(uiNode);
|
|
249
|
-
}
|
|
250
|
-
const dropdownMenuHandlers = {
|
|
251
|
-
[DUPLICATE]: () => onDuplicateNode(uiNode),
|
|
252
|
-
[RENAME]: () => setMode(WRITE),
|
|
253
|
-
[DELETE]: () => onDeleteNode(uiNode)
|
|
254
|
-
};
|
|
255
|
-
const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
|
|
256
|
-
...option,
|
|
257
|
-
id
|
|
258
|
-
})).filter((option) => defaultFolderOptions.includes(option.id));
|
|
259
|
-
function onDropdownMenuOptionClick(itemId) {
|
|
260
|
-
const handler = dropdownMenuHandlers[itemId];
|
|
261
|
-
if (!handler) {
|
|
262
|
-
console.error(`No handler found for dropdown menu item: ${itemId}`);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
handler();
|
|
266
|
-
setIsDropdownMenuOpen(false);
|
|
267
|
-
}
|
|
268
|
-
const content = isReadMode || !isAllowedToCreateDocuments ? jsx("div", { className: "ml-3 max-h-6 truncate font-medium text-gray-600 group-hover:text-gray-800", children: uiNode.name }) : jsx(NodeInput, { className: "ml-3 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
|
|
269
|
-
const containerStyles = twMerge("group flex h-12 cursor-pointer select-none items-center rounded-lg bg-gray-200 px-2", className, isDropTarget && "bg-blue-100");
|
|
270
|
-
return jsx("div", { className: "relative w-64", onClick, children: jsxs("div", { ...dragProps, ...dropProps, className: containerStyles, children: [jsxs("div", { className: "flex items-center overflow-hidden", children: [jsx("div", { className: "p-1", children: jsxs("div", { className: "relative", children: [jsx(Icon, { name: "FolderClose", size: 24 }), isReadMode && uiNode.syncStatus ? jsx("div", { className: "absolute bottom-[-3px] right-[-2px] size-3 rounded-full bg-white", children: jsx("div", { className: "absolute left-[-2px] top-[-2px]", children: jsx(SyncStatusIcon, { overrideSyncIcons: {
|
|
271
|
-
SUCCESS: "CheckCircleFill"
|
|
272
|
-
}, syncStatus: uiNode.syncStatus }) }) }) : null] }) }), content] }), isReadMode && isAllowedToCreateDocuments ? jsx(ConnectDropdownMenu, { items: dropdownMenuOptions, onItemClick: onDropdownMenuOptionClick, onOpenChange: setIsDropdownMenuOpen, open: isDropdownMenuOpen, children: jsx("button", { className: twMerge("ml-auto hidden group-hover:block", isDropdownMenuOpen && "block"), onClick: (e) => {
|
|
273
|
-
e.stopPropagation();
|
|
274
|
-
setIsDropdownMenuOpen(true);
|
|
275
|
-
}, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) });
|
|
276
|
-
}
|
|
277
155
|
const millisecondsInWeek = 6048e5;
|
|
278
156
|
const millisecondsInDay = 864e5;
|
|
157
|
+
const millisecondsInMinute = 6e4;
|
|
158
|
+
const millisecondsInHour = 36e5;
|
|
159
|
+
const minutesInMonth = 43200;
|
|
160
|
+
const minutesInDay = 1440;
|
|
279
161
|
const constructFromSymbol = Symbol.for("constructDateFrom");
|
|
280
162
|
function constructFrom(date, value) {
|
|
281
163
|
if (typeof date === "function") return date(value);
|
|
@@ -343,7 +225,7 @@ function getTimezoneOffsetInMilliseconds(date) {
|
|
|
343
225
|
function normalizeDates(context, ...dates) {
|
|
344
226
|
const normalize = constructFrom.bind(
|
|
345
227
|
null,
|
|
346
|
-
dates.find((date) => typeof date === "object")
|
|
228
|
+
context || dates.find((date) => typeof date === "object")
|
|
347
229
|
);
|
|
348
230
|
return dates.map(normalize);
|
|
349
231
|
}
|
|
@@ -371,12 +253,83 @@ function startOfISOWeekYear(date, options) {
|
|
|
371
253
|
fourthOfJanuary.setHours(0, 0, 0, 0);
|
|
372
254
|
return startOfISOWeek(fourthOfJanuary);
|
|
373
255
|
}
|
|
256
|
+
function compareAsc(dateLeft, dateRight) {
|
|
257
|
+
const diff = +toDate(dateLeft) - +toDate(dateRight);
|
|
258
|
+
if (diff < 0) return -1;
|
|
259
|
+
else if (diff > 0) return 1;
|
|
260
|
+
return diff;
|
|
261
|
+
}
|
|
262
|
+
function constructNow(date) {
|
|
263
|
+
return constructFrom(date, Date.now());
|
|
264
|
+
}
|
|
374
265
|
function isDate(value) {
|
|
375
266
|
return value instanceof Date || typeof value === "object" && Object.prototype.toString.call(value) === "[object Date]";
|
|
376
267
|
}
|
|
377
268
|
function isValid(date) {
|
|
378
269
|
return !(!isDate(date) && typeof date !== "number" || isNaN(+toDate(date)));
|
|
379
270
|
}
|
|
271
|
+
function differenceInCalendarMonths(laterDate, earlierDate, options) {
|
|
272
|
+
const [laterDate_, earlierDate_] = normalizeDates(
|
|
273
|
+
options == null ? void 0 : options.in,
|
|
274
|
+
laterDate,
|
|
275
|
+
earlierDate
|
|
276
|
+
);
|
|
277
|
+
const yearsDiff = laterDate_.getFullYear() - earlierDate_.getFullYear();
|
|
278
|
+
const monthsDiff = laterDate_.getMonth() - earlierDate_.getMonth();
|
|
279
|
+
return yearsDiff * 12 + monthsDiff;
|
|
280
|
+
}
|
|
281
|
+
function getRoundingMethod(method) {
|
|
282
|
+
return (number) => {
|
|
283
|
+
const round = method ? Math[method] : Math.trunc;
|
|
284
|
+
const result = round(number);
|
|
285
|
+
return result === 0 ? 0 : result;
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function differenceInMilliseconds(laterDate, earlierDate) {
|
|
289
|
+
return +toDate(laterDate) - +toDate(earlierDate);
|
|
290
|
+
}
|
|
291
|
+
function endOfDay(date, options) {
|
|
292
|
+
const _date = toDate(date, options == null ? void 0 : options.in);
|
|
293
|
+
_date.setHours(23, 59, 59, 999);
|
|
294
|
+
return _date;
|
|
295
|
+
}
|
|
296
|
+
function endOfMonth(date, options) {
|
|
297
|
+
const _date = toDate(date, options == null ? void 0 : options.in);
|
|
298
|
+
const month = _date.getMonth();
|
|
299
|
+
_date.setFullYear(_date.getFullYear(), month + 1, 0);
|
|
300
|
+
_date.setHours(23, 59, 59, 999);
|
|
301
|
+
return _date;
|
|
302
|
+
}
|
|
303
|
+
function isLastDayOfMonth(date, options) {
|
|
304
|
+
const _date = toDate(date, options == null ? void 0 : options.in);
|
|
305
|
+
return +endOfDay(_date, options) === +endOfMonth(_date, options);
|
|
306
|
+
}
|
|
307
|
+
function differenceInMonths(laterDate, earlierDate, options) {
|
|
308
|
+
const [laterDate_, workingLaterDate, earlierDate_] = normalizeDates(
|
|
309
|
+
options == null ? void 0 : options.in,
|
|
310
|
+
laterDate,
|
|
311
|
+
laterDate,
|
|
312
|
+
earlierDate
|
|
313
|
+
);
|
|
314
|
+
const sign = compareAsc(workingLaterDate, earlierDate_);
|
|
315
|
+
const difference = Math.abs(
|
|
316
|
+
differenceInCalendarMonths(workingLaterDate, earlierDate_)
|
|
317
|
+
);
|
|
318
|
+
if (difference < 1) return 0;
|
|
319
|
+
if (workingLaterDate.getMonth() === 1 && workingLaterDate.getDate() > 27)
|
|
320
|
+
workingLaterDate.setDate(30);
|
|
321
|
+
workingLaterDate.setMonth(workingLaterDate.getMonth() - sign * difference);
|
|
322
|
+
let isLastMonthNotFull = compareAsc(workingLaterDate, earlierDate_) === -sign;
|
|
323
|
+
if (isLastDayOfMonth(laterDate_) && difference === 1 && compareAsc(laterDate_, earlierDate_) === 1) {
|
|
324
|
+
isLastMonthNotFull = false;
|
|
325
|
+
}
|
|
326
|
+
const result = sign * (difference - +isLastMonthNotFull);
|
|
327
|
+
return result === 0 ? 0 : result;
|
|
328
|
+
}
|
|
329
|
+
function differenceInSeconds(laterDate, earlierDate, options) {
|
|
330
|
+
const diff = differenceInMilliseconds(laterDate, earlierDate) / 1e3;
|
|
331
|
+
return getRoundingMethod(options == null ? void 0 : options.roundingMethod)(diff);
|
|
332
|
+
}
|
|
380
333
|
function startOfYear(date, options) {
|
|
381
334
|
const date_ = toDate(date, options == null ? void 0 : options.in);
|
|
382
335
|
date_.setFullYear(date_.getFullYear(), 0, 1);
|
|
@@ -446,7 +399,7 @@ const formatDistanceLocale = {
|
|
|
446
399
|
other: "almost {{count}} years"
|
|
447
400
|
}
|
|
448
401
|
};
|
|
449
|
-
const formatDistance = (token, count, options) => {
|
|
402
|
+
const formatDistance$1 = (token, count, options) => {
|
|
450
403
|
let result;
|
|
451
404
|
const tokenValue = formatDistanceLocale[token];
|
|
452
405
|
if (typeof tokenValue === "string") {
|
|
@@ -862,7 +815,7 @@ const match = {
|
|
|
862
815
|
};
|
|
863
816
|
const enUS = {
|
|
864
817
|
code: "en-US",
|
|
865
|
-
formatDistance,
|
|
818
|
+
formatDistance: formatDistance$1,
|
|
866
819
|
formatLong,
|
|
867
820
|
formatRelative,
|
|
868
821
|
localize,
|
|
@@ -1764,281 +1717,1123 @@ function cleanEscapedString(input) {
|
|
|
1764
1717
|
}
|
|
1765
1718
|
return matched[1].replace(doubleQuoteRegExp, "'");
|
|
1766
1719
|
}
|
|
1767
|
-
function
|
|
1768
|
-
const
|
|
1769
|
-
const
|
|
1770
|
-
const
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1720
|
+
function formatDistance(laterDate, earlierDate, options) {
|
|
1721
|
+
const defaultOptions2 = getDefaultOptions();
|
|
1722
|
+
const locale = (options == null ? void 0 : options.locale) ?? defaultOptions2.locale ?? enUS;
|
|
1723
|
+
const minutesInAlmostTwoDays = 2520;
|
|
1724
|
+
const comparison = compareAsc(laterDate, earlierDate);
|
|
1725
|
+
if (isNaN(comparison)) throw new RangeError("Invalid time value");
|
|
1726
|
+
const localizeOptions = Object.assign({}, options, {
|
|
1727
|
+
addSuffix: options == null ? void 0 : options.addSuffix,
|
|
1728
|
+
comparison
|
|
1729
|
+
});
|
|
1730
|
+
const [laterDate_, earlierDate_] = normalizeDates(
|
|
1731
|
+
options == null ? void 0 : options.in,
|
|
1732
|
+
...comparison > 0 ? [earlierDate, laterDate] : [laterDate, earlierDate]
|
|
1733
|
+
);
|
|
1734
|
+
const seconds = differenceInSeconds(earlierDate_, laterDate_);
|
|
1735
|
+
const offsetInSeconds = (getTimezoneOffsetInMilliseconds(earlierDate_) - getTimezoneOffsetInMilliseconds(laterDate_)) / 1e3;
|
|
1736
|
+
const minutes = Math.round((seconds - offsetInSeconds) / 60);
|
|
1737
|
+
let months;
|
|
1738
|
+
if (minutes < 2) {
|
|
1739
|
+
if (options == null ? void 0 : options.includeSeconds) {
|
|
1740
|
+
if (seconds < 5) {
|
|
1741
|
+
return locale.formatDistance("lessThanXSeconds", 5, localizeOptions);
|
|
1742
|
+
} else if (seconds < 10) {
|
|
1743
|
+
return locale.formatDistance("lessThanXSeconds", 10, localizeOptions);
|
|
1744
|
+
} else if (seconds < 20) {
|
|
1745
|
+
return locale.formatDistance("lessThanXSeconds", 20, localizeOptions);
|
|
1746
|
+
} else if (seconds < 40) {
|
|
1747
|
+
return locale.formatDistance("halfAMinute", 0, localizeOptions);
|
|
1748
|
+
} else if (seconds < 60) {
|
|
1749
|
+
return locale.formatDistance("lessThanXMinutes", 1, localizeOptions);
|
|
1750
|
+
} else {
|
|
1751
|
+
return locale.formatDistance("xMinutes", 1, localizeOptions);
|
|
1752
|
+
}
|
|
1753
|
+
} else {
|
|
1754
|
+
if (minutes === 0) {
|
|
1755
|
+
return locale.formatDistance("lessThanXMinutes", 1, localizeOptions);
|
|
1756
|
+
} else {
|
|
1757
|
+
return locale.formatDistance("xMinutes", minutes, localizeOptions);
|
|
1758
|
+
}
|
|
1775
1759
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1760
|
+
} else if (minutes < 45) {
|
|
1761
|
+
return locale.formatDistance("xMinutes", minutes, localizeOptions);
|
|
1762
|
+
} else if (minutes < 90) {
|
|
1763
|
+
return locale.formatDistance("aboutXHours", 1, localizeOptions);
|
|
1764
|
+
} else if (minutes < minutesInDay) {
|
|
1765
|
+
const hours = Math.round(minutes / 60);
|
|
1766
|
+
return locale.formatDistance("aboutXHours", hours, localizeOptions);
|
|
1767
|
+
} else if (minutes < minutesInAlmostTwoDays) {
|
|
1768
|
+
return locale.formatDistance("xDays", 1, localizeOptions);
|
|
1769
|
+
} else if (minutes < minutesInMonth) {
|
|
1770
|
+
const days = Math.round(minutes / minutesInDay);
|
|
1771
|
+
return locale.formatDistance("xDays", days, localizeOptions);
|
|
1772
|
+
} else if (minutes < minutesInMonth * 2) {
|
|
1773
|
+
months = Math.round(minutes / minutesInMonth);
|
|
1774
|
+
return locale.formatDistance("aboutXMonths", months, localizeOptions);
|
|
1775
|
+
}
|
|
1776
|
+
months = differenceInMonths(earlierDate_, laterDate_);
|
|
1777
|
+
if (months < 12) {
|
|
1778
|
+
const nearestMonth = Math.round(minutes / minutesInMonth);
|
|
1779
|
+
return locale.formatDistance("xMonths", nearestMonth, localizeOptions);
|
|
1780
|
+
} else {
|
|
1781
|
+
const monthsSinceStartOfYear = months % 12;
|
|
1782
|
+
const years = Math.trunc(months / 12);
|
|
1783
|
+
if (monthsSinceStartOfYear < 3) {
|
|
1784
|
+
return locale.formatDistance("aboutXYears", years, localizeOptions);
|
|
1785
|
+
} else if (monthsSinceStartOfYear < 9) {
|
|
1786
|
+
return locale.formatDistance("overXYears", years, localizeOptions);
|
|
1787
|
+
} else {
|
|
1788
|
+
return locale.formatDistance("almostXYears", years + 1, localizeOptions);
|
|
1778
1789
|
}
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
function formatDistanceToNow(date, options) {
|
|
1793
|
+
return formatDistance(date, constructNow(date), options);
|
|
1794
|
+
}
|
|
1795
|
+
function parseISO(argument, options) {
|
|
1796
|
+
const invalidDate = () => constructFrom(options == null ? void 0 : options.in, NaN);
|
|
1797
|
+
const additionalDigits = 2;
|
|
1798
|
+
const dateStrings = splitDateString(argument);
|
|
1799
|
+
let date;
|
|
1800
|
+
if (dateStrings.date) {
|
|
1801
|
+
const parseYearResult = parseYear(dateStrings.date, additionalDigits);
|
|
1802
|
+
date = parseDate(parseYearResult.restDateString, parseYearResult.year);
|
|
1803
|
+
}
|
|
1804
|
+
if (!date || isNaN(+date)) return invalidDate();
|
|
1805
|
+
const timestamp = +date;
|
|
1806
|
+
let time = 0;
|
|
1807
|
+
let offset;
|
|
1808
|
+
if (dateStrings.time) {
|
|
1809
|
+
time = parseTime(dateStrings.time);
|
|
1810
|
+
if (isNaN(time)) return invalidDate();
|
|
1811
|
+
}
|
|
1812
|
+
if (dateStrings.timezone) {
|
|
1813
|
+
offset = parseTimezone(dateStrings.timezone);
|
|
1814
|
+
if (isNaN(offset)) return invalidDate();
|
|
1815
|
+
} else {
|
|
1816
|
+
const tmpDate = new Date(timestamp + time);
|
|
1817
|
+
const result = toDate(0, options == null ? void 0 : options.in);
|
|
1818
|
+
result.setFullYear(
|
|
1819
|
+
tmpDate.getUTCFullYear(),
|
|
1820
|
+
tmpDate.getUTCMonth(),
|
|
1821
|
+
tmpDate.getUTCDate()
|
|
1822
|
+
);
|
|
1823
|
+
result.setHours(
|
|
1824
|
+
tmpDate.getUTCHours(),
|
|
1825
|
+
tmpDate.getUTCMinutes(),
|
|
1826
|
+
tmpDate.getUTCSeconds(),
|
|
1827
|
+
tmpDate.getUTCMilliseconds()
|
|
1828
|
+
);
|
|
1829
|
+
return result;
|
|
1830
|
+
}
|
|
1831
|
+
return toDate(timestamp + time + offset, options == null ? void 0 : options.in);
|
|
1832
|
+
}
|
|
1833
|
+
const patterns = {
|
|
1834
|
+
dateTimeDelimiter: /[T ]/,
|
|
1835
|
+
timeZoneDelimiter: /[Z ]/i,
|
|
1836
|
+
timezone: /([Z+-].*)$/
|
|
1837
|
+
};
|
|
1838
|
+
const dateRegex = /^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/;
|
|
1839
|
+
const timeRegex = /^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/;
|
|
1840
|
+
const timezoneRegex = /^([+-])(\d{2})(?::?(\d{2}))?$/;
|
|
1841
|
+
function splitDateString(dateString) {
|
|
1842
|
+
const dateStrings = {};
|
|
1843
|
+
const array = dateString.split(patterns.dateTimeDelimiter);
|
|
1844
|
+
let timeString;
|
|
1845
|
+
if (array.length > 2) {
|
|
1846
|
+
return dateStrings;
|
|
1847
|
+
}
|
|
1848
|
+
if (/:/.test(array[0])) {
|
|
1849
|
+
timeString = array[0];
|
|
1850
|
+
} else {
|
|
1851
|
+
dateStrings.date = array[0];
|
|
1852
|
+
timeString = array[1];
|
|
1853
|
+
if (patterns.timeZoneDelimiter.test(dateStrings.date)) {
|
|
1854
|
+
dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0];
|
|
1855
|
+
timeString = dateString.substr(
|
|
1856
|
+
dateStrings.date.length,
|
|
1857
|
+
dateString.length
|
|
1858
|
+
);
|
|
1791
1859
|
}
|
|
1792
1860
|
}
|
|
1793
|
-
|
|
1861
|
+
if (timeString) {
|
|
1862
|
+
const token = patterns.timezone.exec(timeString);
|
|
1863
|
+
if (token) {
|
|
1864
|
+
dateStrings.time = timeString.replace(token[1], "");
|
|
1865
|
+
dateStrings.timezone = token[1];
|
|
1866
|
+
} else {
|
|
1867
|
+
dateStrings.time = timeString;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
return dateStrings;
|
|
1794
1871
|
}
|
|
1795
|
-
function
|
|
1796
|
-
const
|
|
1797
|
-
|
|
1872
|
+
function parseYear(dateString, additionalDigits) {
|
|
1873
|
+
const regex = new RegExp(
|
|
1874
|
+
"^(?:(\\d{4}|[+-]\\d{" + (4 + additionalDigits) + "})|(\\d{2}|[+-]\\d{" + (2 + additionalDigits) + "})$)"
|
|
1875
|
+
);
|
|
1876
|
+
const captures = dateString.match(regex);
|
|
1877
|
+
if (!captures) return { year: NaN, restDateString: "" };
|
|
1878
|
+
const year = captures[1] ? parseInt(captures[1]) : null;
|
|
1879
|
+
const century = captures[2] ? parseInt(captures[2]) : null;
|
|
1880
|
+
return {
|
|
1881
|
+
year: century === null ? year : century * 100,
|
|
1882
|
+
restDateString: dateString.slice((captures[1] || captures[2]).length)
|
|
1883
|
+
};
|
|
1798
1884
|
}
|
|
1799
|
-
function
|
|
1800
|
-
|
|
1801
|
-
const
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1885
|
+
function parseDate(dateString, year) {
|
|
1886
|
+
if (year === null) return /* @__PURE__ */ new Date(NaN);
|
|
1887
|
+
const captures = dateString.match(dateRegex);
|
|
1888
|
+
if (!captures) return /* @__PURE__ */ new Date(NaN);
|
|
1889
|
+
const isWeekDate = !!captures[4];
|
|
1890
|
+
const dayOfYear = parseDateUnit(captures[1]);
|
|
1891
|
+
const month = parseDateUnit(captures[2]) - 1;
|
|
1892
|
+
const day = parseDateUnit(captures[3]);
|
|
1893
|
+
const week = parseDateUnit(captures[4]);
|
|
1894
|
+
const dayOfWeek = parseDateUnit(captures[5]) - 1;
|
|
1895
|
+
if (isWeekDate) {
|
|
1896
|
+
if (!validateWeekDate(year, week, dayOfWeek)) {
|
|
1897
|
+
return /* @__PURE__ */ new Date(NaN);
|
|
1898
|
+
}
|
|
1899
|
+
return dayOfISOWeekYear(year, week, dayOfWeek);
|
|
1900
|
+
} else {
|
|
1901
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
1902
|
+
if (!validateDate(year, month, day) || !validateDayOfYearDate(year, dayOfYear)) {
|
|
1903
|
+
return /* @__PURE__ */ new Date(NaN);
|
|
1904
|
+
}
|
|
1905
|
+
date.setUTCFullYear(year, month, Math.max(dayOfYear, day));
|
|
1906
|
+
return date;
|
|
1808
1907
|
}
|
|
1809
|
-
return jsxs("button", { className: "flex h-8 items-center gap-1 rounded-lg bg-slate-50 pl-1 pr-2 text-xs text-slate-100", onClick: handleCopy(docId), children: [jsx(Icon, { name: "Link" }), "DOC ID", jsx("span", { className: "text-gray-900", children: docId })] });
|
|
1810
1908
|
}
|
|
1811
|
-
function
|
|
1812
|
-
|
|
1813
|
-
const items = [
|
|
1814
|
-
{ displayValue: "Global scope", value: "global" },
|
|
1815
|
-
{ displayValue: "Local scope", value: "local" }
|
|
1816
|
-
];
|
|
1817
|
-
return jsx(Select, { absolutePositionMenu: true, containerClassName: "bg-slate-50 text-gray-500 rounded-lg w-fit text-xs z-10", id: "scope select", itemClassName: "py-2 text-gray-500 grid grid-cols-[auto,auto] gap-1", items, menuClassName: "min-w-0 text-gray-500", onChange, value });
|
|
1909
|
+
function parseDateUnit(value) {
|
|
1910
|
+
return value ? parseInt(value) : 1;
|
|
1818
1911
|
}
|
|
1819
|
-
function
|
|
1820
|
-
const
|
|
1821
|
-
|
|
1912
|
+
function parseTime(timeString) {
|
|
1913
|
+
const captures = timeString.match(timeRegex);
|
|
1914
|
+
if (!captures) return NaN;
|
|
1915
|
+
const hours = parseTimeUnit(captures[1]);
|
|
1916
|
+
const minutes = parseTimeUnit(captures[2]);
|
|
1917
|
+
const seconds = parseTimeUnit(captures[3]);
|
|
1918
|
+
if (!validateTime(hours, minutes, seconds)) {
|
|
1919
|
+
return NaN;
|
|
1920
|
+
}
|
|
1921
|
+
return hours * millisecondsInHour + minutes * millisecondsInMinute + seconds * 1e3;
|
|
1822
1922
|
}
|
|
1823
|
-
function
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1923
|
+
function parseTimeUnit(value) {
|
|
1924
|
+
return value && parseFloat(value.replace(",", ".")) || 0;
|
|
1925
|
+
}
|
|
1926
|
+
function parseTimezone(timezoneString) {
|
|
1927
|
+
if (timezoneString === "Z") return 0;
|
|
1928
|
+
const captures = timezoneString.match(timezoneRegex);
|
|
1929
|
+
if (!captures) return 0;
|
|
1930
|
+
const sign = captures[1] === "+" ? -1 : 1;
|
|
1931
|
+
const hours = parseInt(captures[2]);
|
|
1932
|
+
const minutes = captures[3] && parseInt(captures[3]) || 0;
|
|
1933
|
+
if (!validateTimezone(hours, minutes)) {
|
|
1934
|
+
return NaN;
|
|
1935
|
+
}
|
|
1936
|
+
return sign * (hours * millisecondsInHour + minutes * millisecondsInMinute);
|
|
1937
|
+
}
|
|
1938
|
+
function dayOfISOWeekYear(isoWeekYear, week, day) {
|
|
1939
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
1940
|
+
date.setUTCFullYear(isoWeekYear, 0, 4);
|
|
1941
|
+
const fourthOfJanuaryDay = date.getUTCDay() || 7;
|
|
1942
|
+
const diff = (week - 1) * 7 + day + 1 - fourthOfJanuaryDay;
|
|
1943
|
+
date.setUTCDate(date.getUTCDate() + diff);
|
|
1944
|
+
return date;
|
|
1945
|
+
}
|
|
1946
|
+
const daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
1947
|
+
function isLeapYearIndex(year) {
|
|
1948
|
+
return year % 400 === 0 || year % 4 === 0 && year % 100 !== 0;
|
|
1949
|
+
}
|
|
1950
|
+
function validateDate(year, month, date) {
|
|
1951
|
+
return month >= 0 && month <= 11 && date >= 1 && date <= (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28));
|
|
1952
|
+
}
|
|
1953
|
+
function validateDayOfYearDate(year, dayOfYear) {
|
|
1954
|
+
return dayOfYear >= 1 && dayOfYear <= (isLeapYearIndex(year) ? 366 : 365);
|
|
1955
|
+
}
|
|
1956
|
+
function validateWeekDate(_year, week, day) {
|
|
1957
|
+
return week >= 1 && week <= 53 && day >= 0 && day <= 6;
|
|
1958
|
+
}
|
|
1959
|
+
function validateTime(hours, minutes, seconds) {
|
|
1960
|
+
if (hours === 24) {
|
|
1961
|
+
return minutes === 0 && seconds === 0;
|
|
1962
|
+
}
|
|
1963
|
+
return seconds >= 0 && seconds < 60 && minutes >= 0 && minutes < 60 && hours >= 0 && hours < 25;
|
|
1964
|
+
}
|
|
1965
|
+
function validateTimezone(_hours, minutes) {
|
|
1966
|
+
return minutes >= 0 && minutes <= 59;
|
|
1967
|
+
}
|
|
1968
|
+
const HDivider = (props) => {
|
|
1969
|
+
const { className, timestamp, title, subtitle, onClick, isSelected = false } = props;
|
|
1970
|
+
const [open, setOpen] = useState(false);
|
|
1971
|
+
const hasContent = !!title || !!subtitle || !!timestamp;
|
|
1972
|
+
useEffect(() => {
|
|
1973
|
+
if (open) {
|
|
1974
|
+
setOpen(false);
|
|
1975
|
+
setTimeout(() => hasContent && setOpen(true), 50);
|
|
1834
1976
|
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
if (
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
const pad = (str, num) => {
|
|
1844
|
-
str = String(str);
|
|
1845
|
-
while (str.length < num) {
|
|
1846
|
-
str = " " + str;
|
|
1847
|
-
}
|
|
1848
|
-
return str;
|
|
1849
|
-
};
|
|
1850
|
-
console.info(
|
|
1851
|
-
`%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
|
|
1852
|
-
`
|
|
1853
|
-
font-size: .6rem;
|
|
1854
|
-
font-weight: bold;
|
|
1855
|
-
color: hsl(${Math.max(
|
|
1856
|
-
0,
|
|
1857
|
-
Math.min(120 - 120 * resultFpsPercentage, 120)
|
|
1858
|
-
)}deg 100% 31%);`,
|
|
1859
|
-
opts == null ? void 0 : opts.key
|
|
1860
|
-
);
|
|
1977
|
+
}, [title, subtitle, timestamp, hasContent]);
|
|
1978
|
+
const formatTimestamp2 = (isoString) => {
|
|
1979
|
+
if (!isoString)
|
|
1980
|
+
return "";
|
|
1981
|
+
try {
|
|
1982
|
+
return formatDistanceToNow(new Date(isoString), { addSuffix: true });
|
|
1983
|
+
} catch {
|
|
1984
|
+
return isoString;
|
|
1861
1985
|
}
|
|
1862
|
-
(_d = opts == null ? void 0 : opts.onChange) == null ? void 0 : _d.call(opts, result);
|
|
1863
|
-
return result;
|
|
1864
|
-
}
|
|
1865
|
-
memoizedFunction.updateDeps = (newDeps) => {
|
|
1866
|
-
deps = newDeps;
|
|
1867
1986
|
};
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
} else {
|
|
1874
|
-
return value;
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
const approxEqual = (a, b) => Math.abs(a - b) <= 1;
|
|
1878
|
-
const debounce = (targetWindow, fn, ms) => {
|
|
1879
|
-
let timeoutId;
|
|
1880
|
-
return function(...args) {
|
|
1881
|
-
targetWindow.clearTimeout(timeoutId);
|
|
1882
|
-
timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
|
|
1987
|
+
const tooltipContent = jsxs("div", { className: "flex flex-col text-xs", children: [!!title && jsx("div", { children: title }), !!subtitle && jsx("div", { className: "text-gray-300", children: subtitle }), !!timestamp && jsx("div", { children: formatTimestamp2(timestamp) })] });
|
|
1988
|
+
const handleMouseEnter = () => {
|
|
1989
|
+
if (hasContent) {
|
|
1990
|
+
setOpen(true);
|
|
1991
|
+
}
|
|
1883
1992
|
};
|
|
1993
|
+
const handleMouseLeave = () => {
|
|
1994
|
+
setOpen(false);
|
|
1995
|
+
};
|
|
1996
|
+
return jsxs("div", { className: "relative", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [isSelected && jsx(Icon, { name: "TimelineCaret", color: "#4EA9FF", size: 10, className: "absolute top-[-11px] z-40" }), jsx(Tooltip, { className: "rounded-md bg-gray-900 text-white", content: tooltipContent, open: open && hasContent, onOpenChange: setOpen, delayDuration: 0, side: "bottom", sideOffset: 5, children: jsx("div", { className: twMerge("mx-0.5 flex h-[25px] w-1.5 cursor-pointer flex-col items-center justify-center rounded-[2px] hover:bg-blue-300", isSelected && "bg-blue-300", className), onClick, "data-title": title, "data-subtitle": subtitle, "data-timestamp": timestamp, children: jsx("div", { className: "h-0.5 w-1 rounded-full bg-gray-500" }) }) })] });
|
|
1884
1997
|
};
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1998
|
+
const getBarHeight = (size = 0) => {
|
|
1999
|
+
switch (true) {
|
|
2000
|
+
case size <= 0:
|
|
2001
|
+
return "h-[1px]";
|
|
2002
|
+
case size === 1:
|
|
2003
|
+
return "h-[3px]";
|
|
2004
|
+
case size === 2:
|
|
2005
|
+
return "h-[6px]";
|
|
2006
|
+
case size === 3:
|
|
2007
|
+
return "h-[9px]";
|
|
2008
|
+
case size >= 4:
|
|
2009
|
+
return "h-[12px]";
|
|
2010
|
+
default:
|
|
2011
|
+
return "h-[1px]";
|
|
1896
2012
|
}
|
|
1897
|
-
return arr;
|
|
1898
2013
|
};
|
|
1899
|
-
const
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2014
|
+
const formatTimestamp = (isoString) => {
|
|
2015
|
+
if (!isoString)
|
|
2016
|
+
return "";
|
|
2017
|
+
try {
|
|
2018
|
+
const date = parseISO(isoString);
|
|
2019
|
+
return format(date, "HH:mm, dd, MMMM");
|
|
2020
|
+
} catch {
|
|
2021
|
+
return isoString;
|
|
1903
2022
|
}
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
const
|
|
1909
|
-
|
|
1910
|
-
|
|
2023
|
+
};
|
|
2024
|
+
const TimelineBar = ({ onClick, className, timestamp, additions, deletions, addSize = 0, delSize = 0, isSelected = false }) => {
|
|
2025
|
+
const [open, setOpen] = useState(false);
|
|
2026
|
+
const noChanges = addSize === 0 && delSize === 0;
|
|
2027
|
+
const addBarHeight = getBarHeight(addSize);
|
|
2028
|
+
const delBarHeight = getBarHeight(delSize);
|
|
2029
|
+
const tooltipContent = jsxs("div", { className: "flex flex-col text-xs", children: [jsx("div", { children: formatTimestamp(timestamp) }), jsx("div", { className: "text-green-900", children: `${additions} additions +` }), jsx("div", { className: "text-red-700", children: `${deletions} deletions -` })] });
|
|
2030
|
+
const handleMouseEnter = () => {
|
|
2031
|
+
if (!noChanges) {
|
|
2032
|
+
setOpen(true);
|
|
2033
|
+
}
|
|
1911
2034
|
};
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
return () => {
|
|
1915
|
-
};
|
|
1916
|
-
}
|
|
1917
|
-
const observer = new targetWindow.ResizeObserver((entries) => {
|
|
1918
|
-
const run = () => {
|
|
1919
|
-
const entry = entries[0];
|
|
1920
|
-
if (entry == null ? void 0 : entry.borderBoxSize) {
|
|
1921
|
-
const box = entry.borderBoxSize[0];
|
|
1922
|
-
if (box) {
|
|
1923
|
-
handler({ width: box.inlineSize, height: box.blockSize });
|
|
1924
|
-
return;
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
handler(getRect(element));
|
|
1928
|
-
};
|
|
1929
|
-
instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
1930
|
-
});
|
|
1931
|
-
observer.observe(element, { box: "border-box" });
|
|
1932
|
-
return () => {
|
|
1933
|
-
observer.unobserve(element);
|
|
2035
|
+
const handleMouseLeave = () => {
|
|
2036
|
+
setOpen(false);
|
|
1934
2037
|
};
|
|
2038
|
+
return jsxs("div", { className: "relative", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [isSelected && jsx(Icon, { name: "TimelineCaret", color: "#4EA9FF", size: 10, className: "absolute left-[-2px] top-[-11px] z-40" }), noChanges ? jsx("div", { className: twMerge("flex h-[25px] w-1.5 cursor-pointer flex-col items-center justify-center rounded-[2px] hover:bg-blue-300", className), "data-timestamp": timestamp, onClick, children: jsx("div", { className: "size-[3px] rounded-full bg-gray-500" }) }) : jsx(Tooltip, { className: "rounded-md bg-gray-900 text-white", content: tooltipContent, open, onOpenChange: setOpen, delayDuration: 0, side: "bottom", sideOffset: 5, children: jsxs("div", { className: twMerge("flex h-[25px] w-1.5 cursor-pointer flex-col items-center justify-center rounded-[2px] hover:bg-blue-300", className, isSelected && "bg-blue-300"), "data-timestamp": timestamp, onClick, children: [jsx("div", { className: "flex h-3 w-0.5 items-end", children: jsx("div", { className: twMerge("h-3 w-0.5 rounded-t-full bg-green-600", addBarHeight) }) }), jsx("div", { className: "flex h-3 w-0.5 items-start", children: jsx("div", { className: twMerge("h-3 w-0.5 rounded-b-full bg-red-600", delBarHeight) }) })] }) })] });
|
|
1935
2039
|
};
|
|
1936
|
-
const
|
|
1937
|
-
|
|
2040
|
+
const defaultTimeLineItem = {
|
|
2041
|
+
id: "default",
|
|
2042
|
+
type: "bar",
|
|
2043
|
+
addSize: 0,
|
|
2044
|
+
delSize: 0
|
|
1938
2045
|
};
|
|
1939
|
-
const
|
|
1940
|
-
const
|
|
1941
|
-
const
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
targetWindow,
|
|
1952
|
-
() => {
|
|
1953
|
-
cb(offset, false);
|
|
1954
|
-
},
|
|
1955
|
-
instance.options.isScrollingResetDelay
|
|
1956
|
-
);
|
|
1957
|
-
const createHandler = (isScrolling) => () => {
|
|
1958
|
-
const { horizontal, isRtl } = instance.options;
|
|
1959
|
-
offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
|
|
1960
|
-
fallback();
|
|
1961
|
-
cb(offset, isScrolling);
|
|
2046
|
+
const DocumentTimeline = (props) => {
|
|
2047
|
+
const { timeline = [], onItemClick } = props;
|
|
2048
|
+
const [selectedItem, setSelectedItem] = useState(null);
|
|
2049
|
+
const scrollContainerRef = useRef(null);
|
|
2050
|
+
const handleClick = (item) => {
|
|
2051
|
+
if (item.id === selectedItem || item.id === defaultTimeLineItem.id) {
|
|
2052
|
+
onItemClick == null ? void 0 : onItemClick(null);
|
|
2053
|
+
setSelectedItem(null);
|
|
2054
|
+
} else {
|
|
2055
|
+
onItemClick == null ? void 0 : onItemClick(item);
|
|
2056
|
+
setSelectedItem(item.id);
|
|
2057
|
+
}
|
|
1962
2058
|
};
|
|
1963
|
-
const
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2059
|
+
const mergedTimelineItems = [...timeline, defaultTimeLineItem];
|
|
2060
|
+
const [unselectedItems, selectedItems] = useMemo(() => {
|
|
2061
|
+
const indexSelected = mergedTimelineItems.findIndex((item) => item.id === selectedItem);
|
|
2062
|
+
return indexSelected === -1 ? [mergedTimelineItems, []] : [
|
|
2063
|
+
mergedTimelineItems.slice(0, indexSelected),
|
|
2064
|
+
mergedTimelineItems.slice(indexSelected)
|
|
2065
|
+
];
|
|
2066
|
+
}, [mergedTimelineItems, selectedItem]);
|
|
2067
|
+
const renderTimelineItems = useCallback((items) => {
|
|
2068
|
+
return items.map((item) => {
|
|
2069
|
+
if (item.type === "divider") {
|
|
2070
|
+
const { timestamp, title, subtitle } = item;
|
|
2071
|
+
return jsx(HDivider, { timestamp, title, subtitle, onClick: () => handleClick(item), isSelected: item.id === selectedItem }, item.id);
|
|
2072
|
+
}
|
|
2073
|
+
return jsx(TimelineBar, { timestamp: item.timestamp, addSize: item.addSize, delSize: item.delSize, additions: item.additions, deletions: item.deletions, isSelected: item.id === selectedItem, onClick: () => handleClick(item) }, item.id);
|
|
2074
|
+
});
|
|
2075
|
+
}, [handleClick, selectedItem]);
|
|
2076
|
+
const unselectedContent = useMemo(() => renderTimelineItems(unselectedItems), [unselectedItems, renderTimelineItems]);
|
|
2077
|
+
const selectedContent = useMemo(() => renderTimelineItems(selectedItems), [selectedItems, renderTimelineItems]);
|
|
2078
|
+
useEffect(() => {
|
|
2079
|
+
if (scrollContainerRef.current) {
|
|
2080
|
+
scrollContainerRef.current.scrollLeft = scrollContainerRef.current.scrollWidth;
|
|
2081
|
+
}
|
|
2082
|
+
}, []);
|
|
2083
|
+
return jsx(TooltipProvider, { delayDuration: 0, skipDelayDuration: 0, children: jsxs("div", { className: "relative h-[36px] w-full", children: [jsx("div", { className: "absolute left-[0px] z-[20] h-[17px] w-[6px] bg-white", children: jsx("div", { className: "mt-[11px] h-[6px] w-[6px] rounded-tl-md bg-slate-50" }) }), jsx("div", { className: "absolute right-[0px] top-[11px] z-[20] h-[6px] w-[6px] bg-white", children: jsx("div", { className: "h-[6px] w-[6px] rounded-tr-md bg-slate-50" }) }), jsx("div", { className: "absolute inset-x-0 bottom-0 h-[25px] rounded-md bg-slate-50" }), jsx("div", { className: "absolute inset-x-0 bottom-0 h-[36px]", children: jsx("div", { ref: scrollContainerRef, className: "h-full overflow-x-auto rounded-md", children: jsxs("div", { className: "ml-auto flex h-[36px] w-max items-end px-2 pb-0", children: [jsx("div", { className: "flex", children: unselectedContent }), jsx("div", { className: "flex rounded-sm bg-blue-200", children: selectedContent })] }) }) }), jsx("div", { className: "pointer-events-none absolute bottom-0 left-0 z-10 h-[25px] w-2 rounded-l-md bg-slate-50" }), jsx("div", { className: "pointer-events-none absolute bottom-0 right-0 z-10 h-[25px] w-2 rounded-r-md bg-slate-50" })] }) });
|
|
2084
|
+
};
|
|
2085
|
+
const DocumentToolbar = (props) => {
|
|
2086
|
+
const { undo: undo2, canUndo, redo: redo2, canRedo, title, onClose, onExport, className, onShowRevisionHistory, onSwitchboardLinkClick, timelineItems = [], onTimelineItemClick, initialTimelineVisible = false, timelineButtonVisible = false } = props;
|
|
2087
|
+
const [showTimeline, setShowTimeline] = useState(initialTimelineVisible);
|
|
2088
|
+
const isUndoDisabled = !canUndo || !undo2;
|
|
2089
|
+
const isRedoDisabled = !canRedo || !redo2;
|
|
2090
|
+
const isExportDisabled = !onExport;
|
|
2091
|
+
const isSwitchboardLinkDisabled = !onSwitchboardLinkClick;
|
|
2092
|
+
const isRevisionHistoryDisabled = !onShowRevisionHistory;
|
|
2093
|
+
const isTimelineDisabled = timelineItems.length === 0;
|
|
2094
|
+
useEffect(() => {
|
|
2095
|
+
if (initialTimelineVisible) {
|
|
2096
|
+
setShowTimeline(true);
|
|
1975
2097
|
}
|
|
2098
|
+
}, [initialTimelineVisible]);
|
|
2099
|
+
const handleTimelineToggle = () => {
|
|
2100
|
+
if (isTimelineDisabled)
|
|
2101
|
+
return;
|
|
2102
|
+
setShowTimeline(!showTimeline);
|
|
1976
2103
|
};
|
|
2104
|
+
return jsxs("div", { className: "flex w-full flex-col", children: [jsxs("div", { className: twMerge("flex h-12 w-full items-center justify-between rounded-xl border border-gray-200 bg-slate-50 px-4", className), children: [jsxs("div", { className: "flex items-center gap-x-2", children: [jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isUndoDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: undo2, disabled: isUndoDisabled, children: jsx(Icon, { name: "ArrowCouterclockwise", size: 16, className: isUndoDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isRedoDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: redo2, disabled: isRedoDisabled, children: jsx("div", { className: "-scale-x-100", children: jsx(Icon, { name: "ArrowCouterclockwise", size: 16, className: isRedoDisabled ? "text-gray-500" : "text-gray-900" }) }) }), jsx("button", { className: twMerge("flex h-8 items-center rounded-lg border border-gray-200 bg-white px-3 text-sm", isExportDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onExport, disabled: isExportDisabled, children: jsx("span", { className: isExportDisabled ? "text-gray-500" : "text-gray-900", children: "Export" }) })] }), jsx("div", { className: "flex items-center", children: jsx("h1", { className: "text-sm font-medium text-gray-500", children: title }) }), jsxs("div", { className: "flex items-center gap-x-2", children: [jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isSwitchboardLinkDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onSwitchboardLinkClick, disabled: isSwitchboardLinkDisabled, children: jsx(Icon, { name: "Drive", size: 16, className: isSwitchboardLinkDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isRevisionHistoryDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onShowRevisionHistory, disabled: isRevisionHistoryDisabled, children: jsx(Icon, { name: "History", size: 16, className: isRevisionHistoryDisabled ? "text-gray-500" : "text-gray-900" }) }), timelineButtonVisible && jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isTimelineDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: handleTimelineToggle, disabled: isTimelineDisabled, "aria-pressed": showTimeline, children: jsx(Icon, { name: "Timeline", size: 16, className: twMerge("text-gray-900", isTimelineDisabled && "opacity-50", showTimeline && "text-blue-600") }) }), jsx("button", { className: "grid size-8 cursor-pointer place-items-center rounded-lg border border-gray-200 bg-white active:opacity-70", onClick: onClose, children: jsx(Icon, { name: "XmarkLight", size: 16, className: "text-gray-900" }) })] })] }), showTimeline && jsx("div", { className: "mt-2 w-full", children: jsx(DocumentTimeline, { timeline: timelineItems, onItemClick: onTimelineItemClick }) })] });
|
|
1977
2105
|
};
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
2106
|
+
function useUserPermissions() {
|
|
2107
|
+
return {
|
|
2108
|
+
isAllowedToCreateDocuments: true,
|
|
2109
|
+
isAllowedToEditDocuments: true
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
function useDocument(reactor, documentMeta = {}) {
|
|
2113
|
+
const { documentId, documentType, driveId } = documentMeta;
|
|
2114
|
+
const [document2, setDocument] = useState();
|
|
2115
|
+
const onStrandUpdate = useCallback((cb) => {
|
|
2116
|
+
if (!reactor) {
|
|
2117
|
+
throw new Error("Reactor is not loaded");
|
|
1986
2118
|
}
|
|
2119
|
+
return reactor.on("strandUpdate", cb);
|
|
2120
|
+
}, [reactor]);
|
|
2121
|
+
useEffect(() => {
|
|
2122
|
+
if (!reactor)
|
|
2123
|
+
return;
|
|
2124
|
+
if (!driveId || !documentId || !documentType)
|
|
2125
|
+
return;
|
|
2126
|
+
reactor.getDocument(driveId, documentId).then(setDocument).catch(console.error);
|
|
2127
|
+
}, [driveId, documentId, documentType, reactor]);
|
|
2128
|
+
useEffect(() => {
|
|
2129
|
+
if (!reactor)
|
|
2130
|
+
return;
|
|
2131
|
+
if (!driveId || !documentId || !documentType)
|
|
2132
|
+
return;
|
|
2133
|
+
const removeListener = onStrandUpdate((strand) => {
|
|
2134
|
+
if (strand.driveId === driveId && strand.documentId === documentId) {
|
|
2135
|
+
reactor.getDocument(driveId, documentId).then(setDocument).catch(console.error);
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
return removeListener;
|
|
2139
|
+
}, [onStrandUpdate, driveId, documentId, documentType]);
|
|
2140
|
+
return document2;
|
|
2141
|
+
}
|
|
2142
|
+
async function signOperation(operation, sign, documentId, document2, reducer, user) {
|
|
2143
|
+
if (!user)
|
|
2144
|
+
return operation;
|
|
2145
|
+
if (!operation.context)
|
|
2146
|
+
return operation;
|
|
2147
|
+
if (!operation.context.signer)
|
|
2148
|
+
return operation;
|
|
2149
|
+
if (!reducer) {
|
|
2150
|
+
console.error(`Document model '${document2.documentType}' does not have a reducer`);
|
|
2151
|
+
return operation;
|
|
1987
2152
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2153
|
+
const context = {
|
|
2154
|
+
documentId,
|
|
2155
|
+
signer: operation.context.signer
|
|
2156
|
+
};
|
|
2157
|
+
const signedOperation = await buildSignedOperation(operation, reducer, document2, context, sign);
|
|
2158
|
+
return signedOperation;
|
|
2159
|
+
}
|
|
2160
|
+
function addActionContext(action, connectDid, user) {
|
|
2161
|
+
if (!user)
|
|
2162
|
+
return action;
|
|
2163
|
+
const signer = {
|
|
2164
|
+
app: {
|
|
2165
|
+
name: "Connect",
|
|
2166
|
+
key: connectDid || ""
|
|
2167
|
+
},
|
|
2168
|
+
user: {
|
|
2169
|
+
address: user.address,
|
|
2170
|
+
networkId: user.networkId,
|
|
2171
|
+
chainId: user.chainId
|
|
2172
|
+
},
|
|
2173
|
+
signatures: []
|
|
2174
|
+
};
|
|
2175
|
+
return {
|
|
2176
|
+
context: { signer },
|
|
2177
|
+
...action
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
function debounceOperations(callback, timeout = 50) {
|
|
2181
|
+
let timer;
|
|
2182
|
+
const operations = [];
|
|
2183
|
+
return (operation) => {
|
|
2184
|
+
if (timer) {
|
|
2185
|
+
clearTimeout(timer);
|
|
2186
|
+
}
|
|
2187
|
+
const index = operations.findIndex((op) => op.scope === operation.scope && op.index === operation.index);
|
|
2188
|
+
if (index > -1) {
|
|
2189
|
+
const oldOperation = operations[index];
|
|
2190
|
+
if (!(oldOperation.type === operation.type && JSON.stringify(operation.input) === JSON.stringify(oldOperation.input))) {
|
|
2191
|
+
console.warn("Two conflicting operations were dispatched:", oldOperation, operation);
|
|
2192
|
+
}
|
|
2193
|
+
operations[index] = operation;
|
|
2194
|
+
} else {
|
|
2195
|
+
operations.push(operation);
|
|
2196
|
+
}
|
|
2197
|
+
return new Promise((resolve, reject) => {
|
|
2198
|
+
timer = setTimeout(() => {
|
|
2199
|
+
callback(operations).then(resolve).catch(reject);
|
|
2200
|
+
}, timeout);
|
|
2201
|
+
});
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
function useAddDebouncedOperations(reactor, props) {
|
|
2205
|
+
const { driveId, documentId } = props;
|
|
2206
|
+
const [documentDrives] = useDocumentDrives(reactor);
|
|
2207
|
+
const documentDrivesRef = useRef(documentDrives);
|
|
2208
|
+
const { isAllowedToEditDocuments } = useUserPermissions() || {
|
|
2209
|
+
isAllowedToEditDocuments: false
|
|
2210
|
+
};
|
|
2211
|
+
useEffect(() => {
|
|
2212
|
+
documentDrivesRef.current = documentDrives;
|
|
2213
|
+
}, [documentDrives]);
|
|
2214
|
+
const addOperations = useCallback(async (driveId2, id, operations) => {
|
|
2215
|
+
if (!isAllowedToEditDocuments) {
|
|
2216
|
+
throw new Error("User is not allowed to edit documents");
|
|
2217
|
+
}
|
|
2218
|
+
if (!reactor) {
|
|
2219
|
+
throw new Error("Reactor is not loaded");
|
|
2220
|
+
}
|
|
2221
|
+
const drive = documentDrivesRef.current.find((drive2) => drive2.id === driveId2);
|
|
2222
|
+
if (!drive) {
|
|
2223
|
+
throw new Error(`Drive with id ${driveId2} not found`);
|
|
2224
|
+
}
|
|
2225
|
+
const newDocument = await reactor.queueOperations(driveId2, id, operations);
|
|
2226
|
+
return newDocument.document;
|
|
2227
|
+
}, [isAllowedToEditDocuments, reactor]);
|
|
2228
|
+
const addDebouncedOperations = useMemo(() => {
|
|
2229
|
+
return debounceOperations((operations) => addOperations(driveId, documentId, operations));
|
|
2230
|
+
}, [addOperations, driveId, documentId]);
|
|
2231
|
+
return addDebouncedOperations;
|
|
2232
|
+
}
|
|
2233
|
+
function useDocumentDispatch(documentReducer, initialState, onError = console.error) {
|
|
2234
|
+
const [state, setState] = useState(initialState);
|
|
2235
|
+
const [error, setError] = useState();
|
|
2236
|
+
const onErrorHandler = (error2) => {
|
|
2237
|
+
setError(error2);
|
|
2238
|
+
onError(error2);
|
|
2239
|
+
};
|
|
2240
|
+
useEffect(() => {
|
|
2241
|
+
setState(initialState);
|
|
2242
|
+
}, [initialState]);
|
|
2243
|
+
const dispatch = (action, callback, onErrorCallback) => {
|
|
2244
|
+
setError(void 0);
|
|
2245
|
+
setState((_state) => {
|
|
2246
|
+
if (!documentReducer || !_state)
|
|
2247
|
+
return _state;
|
|
2248
|
+
try {
|
|
2249
|
+
const newState = documentReducer(_state, action);
|
|
2250
|
+
const scope = action.scope ?? "global";
|
|
2251
|
+
const operations = newState.operations[scope];
|
|
2252
|
+
const operation = operations[operations.length - 1];
|
|
2253
|
+
if (operation.error) {
|
|
2254
|
+
const error2 = new Error(operation.error);
|
|
2255
|
+
onErrorHandler(error2);
|
|
2256
|
+
onErrorCallback == null ? void 0 : onErrorCallback(error2);
|
|
2257
|
+
}
|
|
2258
|
+
callback == null ? void 0 : callback(operation, {
|
|
2259
|
+
prevState: { ..._state },
|
|
2260
|
+
newState: { ...newState }
|
|
2261
|
+
});
|
|
2262
|
+
return newState;
|
|
2263
|
+
} catch (error2) {
|
|
2264
|
+
onErrorHandler(error2);
|
|
2265
|
+
onErrorCallback == null ? void 0 : onErrorCallback(error2);
|
|
2266
|
+
return _state;
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
};
|
|
2270
|
+
return [state, dispatch, error];
|
|
2271
|
+
}
|
|
2272
|
+
function useDocumentEditorProps(reactor, props) {
|
|
2273
|
+
const { nodeId, driveId, documentModelModule, document: initialDocument, user, connectDid, sign } = props;
|
|
2274
|
+
const addDebouncedOprations = useAddDebouncedOperations(reactor, {
|
|
2275
|
+
driveId,
|
|
2276
|
+
documentId: nodeId
|
|
2277
|
+
});
|
|
2278
|
+
const [document2, _dispatch, error] = useDocumentDispatch(documentModelModule.reducer, initialDocument);
|
|
2279
|
+
function dispatch(action, onErrorCallback) {
|
|
2280
|
+
const callback = (operation, state) => {
|
|
2281
|
+
const { prevState } = state;
|
|
2282
|
+
signOperation(operation, sign, nodeId, prevState, documentModelModule.reducer, user).then((op) => {
|
|
2283
|
+
return addDebouncedOprations(op);
|
|
2284
|
+
}).catch(console.error);
|
|
2285
|
+
};
|
|
2286
|
+
_dispatch(addActionContext(action, connectDid, user), callback, onErrorCallback);
|
|
2287
|
+
}
|
|
2288
|
+
return {
|
|
2289
|
+
dispatch,
|
|
2290
|
+
document: document2,
|
|
2291
|
+
error
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
const generateId = () => generateId$1().toString();
|
|
2295
|
+
function getNode(id, drive) {
|
|
2296
|
+
return drive.state.global.nodes.find((node) => node.id === id);
|
|
2297
|
+
}
|
|
2298
|
+
function createDriveActions(document2, dispatch, context) {
|
|
2299
|
+
const drive = document2;
|
|
2300
|
+
const driveId = drive.id;
|
|
2301
|
+
const { selectedNode } = context;
|
|
2302
|
+
const handleAddFolder = async (name, parentFolder, id = generateId()) => {
|
|
2303
|
+
dispatch(addFolder({
|
|
2304
|
+
id,
|
|
2305
|
+
name,
|
|
2306
|
+
parentFolder: parentFolder ?? null
|
|
2307
|
+
}));
|
|
2308
|
+
};
|
|
2309
|
+
const addDocument = async (name, documentType, document3, parentFolder, id = generateId()) => {
|
|
2310
|
+
const action = generateAddNodeAction(drive.state.global, {
|
|
2311
|
+
id,
|
|
2312
|
+
name,
|
|
2313
|
+
parentFolder: parentFolder ?? null,
|
|
2314
|
+
documentType,
|
|
2315
|
+
document: document3
|
|
2316
|
+
}, ["global"]);
|
|
2317
|
+
dispatch(action);
|
|
2318
|
+
};
|
|
2319
|
+
const addFile = async (file, parentFolder = selectedNode && isFileNode(selectedNode) ? void 0 : selectedNode == null ? void 0 : selectedNode.id, name = file.name.replace(/\.zip$/gim, "")) => {
|
|
2320
|
+
const folder = parentFolder ? getNode(parentFolder, drive) : void 0;
|
|
2321
|
+
if (parentFolder && !folder) {
|
|
2322
|
+
throw new Error(`Parent folder with id "${parentFolder}" not found`);
|
|
2323
|
+
}
|
|
2324
|
+
if (folder && !isFolderNode(folder)) {
|
|
2325
|
+
throw new Error(`Parent folder with id "${parentFolder}" is not a folder`);
|
|
2326
|
+
}
|
|
2327
|
+
await context.addFile(file, driveId, name, parentFolder);
|
|
2328
|
+
};
|
|
2329
|
+
const handleDeleteNode = async (id) => {
|
|
2330
|
+
dispatch(deleteNode({ id }));
|
|
2331
|
+
};
|
|
2332
|
+
const renameNode = async (id, name) => {
|
|
2333
|
+
dispatch(updateNode({ id, name }));
|
|
2334
|
+
};
|
|
2335
|
+
const handleMoveNode = async (sourceId, targetId) => {
|
|
2336
|
+
dispatch(moveNode({
|
|
2337
|
+
srcFolder: sourceId,
|
|
2338
|
+
targetParentFolder: targetId
|
|
2339
|
+
}));
|
|
2340
|
+
};
|
|
2341
|
+
const handleCopyNode = async (sourceId, targetFolderId) => {
|
|
2342
|
+
const target = targetFolderId ? getNode(targetFolderId, drive) : void 0;
|
|
2343
|
+
if (targetFolderId && !target && targetFolderId !== driveId) {
|
|
2344
|
+
throw new Error(`Target node with id "${targetFolderId}" not found`);
|
|
2345
|
+
}
|
|
2346
|
+
if (target && !isFolderNode(target)) {
|
|
2347
|
+
throw new Error(`Target node with id "${targetFolderId}" is not a folder`);
|
|
2348
|
+
}
|
|
2349
|
+
const source = getNode(sourceId, drive);
|
|
2350
|
+
if (!source) {
|
|
2351
|
+
throw new Error(`Source node with id "${sourceId}" not found`);
|
|
2352
|
+
}
|
|
2353
|
+
const copyNodesInput = generateNodesCopy({
|
|
2354
|
+
srcId: sourceId,
|
|
2355
|
+
targetParentFolder: target == null ? void 0 : target.id,
|
|
2356
|
+
targetName: source.name
|
|
2357
|
+
}, generateId, drive.state.global.nodes);
|
|
2358
|
+
const copyActions = copyNodesInput.map((copyNodeInput) => copyNode(copyNodeInput));
|
|
2359
|
+
for (const copyAction of copyActions) {
|
|
2360
|
+
dispatch(copyAction);
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
const duplicateNode = async (sourceId) => {
|
|
2364
|
+
const node = getNode(sourceId, drive);
|
|
2365
|
+
if (!node) {
|
|
2366
|
+
throw new Error(`Node with id "${sourceId}" not found`);
|
|
2367
|
+
}
|
|
2368
|
+
await handleCopyNode(node.id, node.parentFolder || void 0);
|
|
2369
|
+
};
|
|
2370
|
+
return {
|
|
2371
|
+
context,
|
|
2372
|
+
selectNode: context.selectNode,
|
|
2373
|
+
addFolder: handleAddFolder,
|
|
2374
|
+
addFile,
|
|
2375
|
+
addDocument,
|
|
2376
|
+
deleteNode: handleDeleteNode,
|
|
2377
|
+
renameNode,
|
|
2378
|
+
moveNode: handleMoveNode,
|
|
2379
|
+
copyNode: handleCopyNode,
|
|
2380
|
+
duplicateNode
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
function useDriveActions(document2, dispatch, context) {
|
|
2384
|
+
return useMemo(() => createDriveActions(document2, dispatch, context), [document2, dispatch, context]);
|
|
2385
|
+
}
|
|
2386
|
+
function toNode(uiNode) {
|
|
2387
|
+
if (uiNode.kind === "DRIVE") {
|
|
2388
|
+
throw new Error("Cannot convert drive node to regular node");
|
|
2389
|
+
}
|
|
2390
|
+
const { id, name, parentFolder, kind } = uiNode;
|
|
2391
|
+
if (kind === "FOLDER") {
|
|
2392
|
+
return { id, name, parentFolder, kind: "folder" };
|
|
2393
|
+
} else {
|
|
2394
|
+
const fileNode = uiNode;
|
|
2395
|
+
return {
|
|
2396
|
+
id,
|
|
2397
|
+
name,
|
|
2398
|
+
parentFolder,
|
|
2399
|
+
kind: "file",
|
|
2400
|
+
documentType: fileNode.documentType,
|
|
2401
|
+
synchronizationUnits: fileNode.synchronizationUnits
|
|
2402
|
+
};
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
function createUiNodeAdapter(driveActions) {
|
|
2406
|
+
return {
|
|
2407
|
+
...driveActions,
|
|
2408
|
+
addFile: (file, parentNode) => driveActions.addFile(file, parentNode == null ? void 0 : parentNode.id),
|
|
2409
|
+
addFolder: (name, parentFolder = void 0) => driveActions.addFolder(name, parentFolder),
|
|
2410
|
+
renameNode: (name, node) => {
|
|
2411
|
+
const converted = toNode(node);
|
|
2412
|
+
return driveActions.renameNode(converted.id, name);
|
|
2413
|
+
},
|
|
2414
|
+
deleteNode: (node) => {
|
|
2415
|
+
const converted = toNode(node);
|
|
2416
|
+
return driveActions.deleteNode(converted.id);
|
|
2417
|
+
},
|
|
2418
|
+
moveNode: async (src, target) => {
|
|
2419
|
+
if (target.kind === FILE || src.parentFolder === target.id)
|
|
2420
|
+
return;
|
|
2421
|
+
const srcNode = toNode(src);
|
|
2422
|
+
const targetNode = toNode(target);
|
|
2423
|
+
return driveActions.moveNode(srcNode.id, targetNode.id);
|
|
2424
|
+
},
|
|
2425
|
+
copyNode: (src, target) => {
|
|
2426
|
+
return driveActions.copyNode(src.id, target.id);
|
|
2427
|
+
},
|
|
2428
|
+
duplicateNode: (node) => {
|
|
2429
|
+
const converted = toNode(node);
|
|
2430
|
+
return driveActions.duplicateNode(converted.id);
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
function useDriveActionsWithUiNodes(document2, dispatch) {
|
|
2435
|
+
const { selectedNode, selectedDriveNode, setSelectedNode, getNodeById } = useUiNodesContext();
|
|
2436
|
+
const _driveContext = useDriveContext();
|
|
2437
|
+
const driveContext = useMemo(() => ({
|
|
2438
|
+
..._driveContext,
|
|
2439
|
+
selectedNode,
|
|
2440
|
+
onSelectNode: (node) => {
|
|
2441
|
+
_driveContext.selectNode(node);
|
|
2442
|
+
setSelectedNode(getNodeById(node.id));
|
|
2443
|
+
}
|
|
2444
|
+
}), [selectedNode, selectedDriveNode == null ? void 0 : selectedDriveNode.driveId, setSelectedNode, getNodeById]);
|
|
2445
|
+
const driveActions = useDriveActions(document2, dispatch, driveContext);
|
|
2446
|
+
const uiNodeActions = useMemo(() => createUiNodeAdapter(driveActions), [driveActions]);
|
|
2447
|
+
return uiNodeActions;
|
|
2448
|
+
}
|
|
2449
|
+
const syncIcons = {
|
|
2450
|
+
SYNCING: "Syncing",
|
|
2451
|
+
SUCCESS: "Synced",
|
|
2452
|
+
CONFLICT: "Error",
|
|
2453
|
+
MISSING: "Circle",
|
|
2454
|
+
ERROR: "Error",
|
|
2455
|
+
INITIAL_SYNC: "Syncing"
|
|
2456
|
+
};
|
|
2457
|
+
function SyncStatusIcon(props) {
|
|
2458
|
+
const { syncStatus, className, overrideSyncIcons = {}, ...iconProps } = props;
|
|
2459
|
+
const icons = { ...syncIcons, ...overrideSyncIcons };
|
|
2460
|
+
const syncStatusIcons = {
|
|
2461
|
+
[INITIAL_SYNC]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[INITIAL_SYNC] }),
|
|
2462
|
+
[SYNCING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[SYNCING] }),
|
|
2463
|
+
[SUCCESS]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-green-900", className), name: icons[SUCCESS] }),
|
|
2464
|
+
[CONFLICT]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-orange-900", className), name: icons[CONFLICT] }),
|
|
2465
|
+
[MISSING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[MISSING] }),
|
|
2466
|
+
[ERROR]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[ERROR] })
|
|
2467
|
+
};
|
|
2468
|
+
return syncStatusIcons[syncStatus];
|
|
2469
|
+
}
|
|
2470
|
+
function FileItem(props) {
|
|
2471
|
+
const { uiNode, className, customDocumentIconSrc, onSelectNode, onRenameNode, onDuplicateNode, onDeleteNode, isAllowedToCreateDocuments } = props;
|
|
2472
|
+
const [mode, setMode] = useState(READ);
|
|
2473
|
+
const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
|
|
2474
|
+
const { dragProps } = useDrag({ uiNode });
|
|
2475
|
+
const isReadMode = mode === READ;
|
|
2476
|
+
const dropdownMenuHandlers = {
|
|
2477
|
+
[DUPLICATE]: () => onDuplicateNode(uiNode),
|
|
2478
|
+
[RENAME]: () => setMode(WRITE),
|
|
2479
|
+
[DELETE]: () => onDeleteNode(uiNode)
|
|
2480
|
+
};
|
|
2481
|
+
const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
|
|
2482
|
+
...option,
|
|
2483
|
+
id
|
|
2484
|
+
})).filter((option) => defaultFileOptions.includes(option.id));
|
|
2485
|
+
function onSubmit(name) {
|
|
2486
|
+
onRenameNode(name, uiNode);
|
|
2487
|
+
setMode(READ);
|
|
2488
|
+
}
|
|
2489
|
+
function onCancel() {
|
|
2490
|
+
setMode(READ);
|
|
2491
|
+
}
|
|
2492
|
+
function onClick() {
|
|
2493
|
+
onSelectNode(uiNode);
|
|
2494
|
+
}
|
|
2495
|
+
function onDropdownMenuOptionClick(itemId) {
|
|
2496
|
+
const handler = dropdownMenuHandlers[itemId];
|
|
2497
|
+
if (!handler) {
|
|
2498
|
+
console.error(`No handler found for dropdown menu item: ${itemId}`);
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
handler();
|
|
2502
|
+
setIsDropdownMenuOpen(false);
|
|
2503
|
+
}
|
|
2504
|
+
const iconSrc = getDocumentIconSrc(uiNode.documentType, customDocumentIconSrc);
|
|
2505
|
+
const iconNode = jsxs("div", { className: "relative", children: [jsx("img", { alt: "file icon", className: "max-w-none", height: 34, src: iconSrc, width: 32 }), isReadMode && uiNode.syncStatus && jsx("div", { className: "absolute bottom-[-2px] right-0 size-3 rounded-full bg-white", children: jsx("div", { className: "absolute left-[-2px] top-[-2px]", children: jsx(SyncStatusIcon, { overrideSyncIcons: { SUCCESS: "CheckCircleFill" }, syncStatus: uiNode.syncStatus }) }) })] });
|
|
2506
|
+
const containerStyles = twMerge("group flex h-12 cursor-pointer select-none items-center rounded-lg bg-gray-200 px-2 text-gray-600 hover:text-gray-800", className);
|
|
2507
|
+
const content = isReadMode ? jsxs("div", { className: "flex w-52 items-center justify-between", children: [jsxs("div", { className: "mr-2 truncate group-hover:mr-0", children: [jsx("div", { className: "max-h-6 truncate text-sm font-medium group-hover:text-gray-800", children: uiNode.name }), jsx("div", { className: "max-h-6 truncate text-xs font-medium text-gray-600 group-hover:text-gray-800", children: uiNode.documentType })] }), isAllowedToCreateDocuments ? jsx(ConnectDropdownMenu, { items: dropdownMenuOptions, onItemClick: onDropdownMenuOptionClick, onOpenChange: setIsDropdownMenuOpen, open: isDropdownMenuOpen, children: jsx("button", { className: twMerge("hidden group-hover:block", isDropdownMenuOpen && "block"), onClick: (e) => {
|
|
2508
|
+
e.stopPropagation();
|
|
2509
|
+
setIsDropdownMenuOpen(true);
|
|
2510
|
+
}, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) : jsx(NodeInput, { className: "ml-3 flex-1 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
|
|
2511
|
+
return jsx("div", { className: "relative w-64", onClick, children: jsx("div", { ...dragProps, className: containerStyles, children: jsxs("div", { className: "flex items-center", children: [jsx("div", { className: "mr-1.5", children: iconNode }), content] }) }) });
|
|
2512
|
+
}
|
|
2513
|
+
function FolderItem(props) {
|
|
2514
|
+
const { uiNode, isAllowedToCreateDocuments, className, onRenameNode, onDuplicateNode, onDeleteNode, onSelectNode, onAddFile, onCopyNode, onMoveNode } = props;
|
|
2515
|
+
const [mode, setMode] = useState(READ);
|
|
2516
|
+
const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
|
|
2517
|
+
const { dragProps } = useDrag({ ...props, uiNode });
|
|
2518
|
+
const { isDropTarget, dropProps } = useDrop({
|
|
2519
|
+
uiNode,
|
|
2520
|
+
onAddFile,
|
|
2521
|
+
onCopyNode,
|
|
2522
|
+
onMoveNode
|
|
2523
|
+
});
|
|
2524
|
+
const isReadMode = mode === READ;
|
|
2525
|
+
function onCancel() {
|
|
2526
|
+
setMode(READ);
|
|
2527
|
+
}
|
|
2528
|
+
function onSubmit(name) {
|
|
2529
|
+
onRenameNode(name, uiNode);
|
|
2530
|
+
setMode(READ);
|
|
2531
|
+
}
|
|
2532
|
+
function onClick() {
|
|
2533
|
+
onSelectNode(uiNode);
|
|
2534
|
+
}
|
|
2535
|
+
const dropdownMenuHandlers = {
|
|
2536
|
+
[DUPLICATE]: () => onDuplicateNode(uiNode),
|
|
2537
|
+
[RENAME]: () => setMode(WRITE),
|
|
2538
|
+
[DELETE]: () => onDeleteNode(uiNode)
|
|
2539
|
+
};
|
|
2540
|
+
const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
|
|
2541
|
+
...option,
|
|
2542
|
+
id
|
|
2543
|
+
})).filter((option) => defaultFolderOptions.includes(option.id));
|
|
2544
|
+
function onDropdownMenuOptionClick(itemId) {
|
|
2545
|
+
const handler = dropdownMenuHandlers[itemId];
|
|
2546
|
+
if (!handler) {
|
|
2547
|
+
console.error(`No handler found for dropdown menu item: ${itemId}`);
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
handler();
|
|
2551
|
+
setIsDropdownMenuOpen(false);
|
|
2552
|
+
}
|
|
2553
|
+
const content = isReadMode || !isAllowedToCreateDocuments ? jsx("div", { className: "ml-3 max-h-6 truncate font-medium text-gray-600 group-hover:text-gray-800", children: uiNode.name }) : jsx(NodeInput, { className: "ml-3 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
|
|
2554
|
+
const containerStyles = twMerge("group flex h-12 cursor-pointer select-none items-center rounded-lg bg-gray-200 px-2", className, isDropTarget && "bg-blue-100");
|
|
2555
|
+
return jsx("div", { className: "relative w-64", onClick, children: jsxs("div", { ...dragProps, ...dropProps, className: containerStyles, children: [jsxs("div", { className: "flex items-center overflow-hidden", children: [jsx("div", { className: "p-1", children: jsxs("div", { className: "relative", children: [jsx(Icon, { name: "FolderClose", size: 24 }), isReadMode && uiNode.syncStatus ? jsx("div", { className: "absolute bottom-[-3px] right-[-2px] size-3 rounded-full bg-white", children: jsx("div", { className: "absolute left-[-2px] top-[-2px]", children: jsx(SyncStatusIcon, { overrideSyncIcons: {
|
|
2556
|
+
SUCCESS: "CheckCircleFill"
|
|
2557
|
+
}, syncStatus: uiNode.syncStatus }) }) }) : null] }) }), content] }), isReadMode && isAllowedToCreateDocuments ? jsx(ConnectDropdownMenu, { items: dropdownMenuOptions, onItemClick: onDropdownMenuOptionClick, onOpenChange: setIsDropdownMenuOpen, open: isDropdownMenuOpen, children: jsx("button", { className: twMerge("ml-auto hidden group-hover:block", isDropdownMenuOpen && "block"), onClick: (e) => {
|
|
2558
|
+
e.stopPropagation();
|
|
2559
|
+
setIsDropdownMenuOpen(true);
|
|
2560
|
+
}, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) });
|
|
2561
|
+
}
|
|
2562
|
+
function NodeInput(props) {
|
|
2563
|
+
const { onSubmit, onCancel, defaultValue, className, minLength = 1, ...inputProps } = props;
|
|
2564
|
+
const [value, setValue] = useState(defaultValue ?? "");
|
|
2565
|
+
const ref = useRef(null);
|
|
2566
|
+
useOnClickOutside(ref, handleSubmit);
|
|
2567
|
+
useEventListener("keyup", (e) => {
|
|
2568
|
+
if (e.key === "Enter") {
|
|
2569
|
+
handleSubmit();
|
|
2570
|
+
}
|
|
2571
|
+
if (e.key === "Escape") {
|
|
2572
|
+
onCancel();
|
|
2573
|
+
}
|
|
2574
|
+
});
|
|
2575
|
+
useLayoutEffect(() => {
|
|
2576
|
+
setTimeout(() => {
|
|
2577
|
+
var _a, _b, _c;
|
|
2578
|
+
(_a = ref.current) == null ? void 0 : _a.focus();
|
|
2579
|
+
(_b = ref.current) == null ? void 0 : _b.select();
|
|
2580
|
+
(_c = ref.current) == null ? void 0 : _c.scroll({ left: 9999 });
|
|
2581
|
+
}, 100);
|
|
2582
|
+
}, []);
|
|
2583
|
+
function handleSubmit() {
|
|
2584
|
+
if (value.length >= minLength) {
|
|
2585
|
+
onSubmit(value);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
return jsx("input", { ...inputProps, autoFocus: true, className: twMerge("bg-inherit text-inherit outline-none", className), minLength, onChange: (e) => setValue(e.target.value), ref, required: true, type: "text", value });
|
|
2589
|
+
}
|
|
2590
|
+
function Branch(props) {
|
|
2591
|
+
const { branch = "main" } = props;
|
|
2592
|
+
return jsxs("button", { className: "flex h-8 items-center gap-1 rounded-lg bg-slate-50 pl-1 pr-2 text-xs text-slate-100", children: [jsx(Icon, { name: "Branch" }), jsx("span", { children: "BRANCH" }), jsx("span", { className: "text-gray-900", children: branch })] });
|
|
2593
|
+
}
|
|
2594
|
+
function DocId(props) {
|
|
2595
|
+
const { docId } = props;
|
|
2596
|
+
const [, copy] = useCopyToClipboard();
|
|
2597
|
+
function handleCopy(text) {
|
|
2598
|
+
return () => {
|
|
2599
|
+
copy(text).catch((error) => {
|
|
2600
|
+
console.error("Failed to copy!", error);
|
|
2601
|
+
});
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
return jsxs("button", { className: "flex h-8 items-center gap-1 rounded-lg bg-slate-50 pl-1 pr-2 text-xs text-slate-100", onClick: handleCopy(docId), children: [jsx(Icon, { name: "Link" }), "DOC ID", jsx("span", { className: "text-gray-900", children: docId })] });
|
|
2605
|
+
}
|
|
2606
|
+
function Scope(props) {
|
|
2607
|
+
const { value, onChange } = props;
|
|
2608
|
+
const items = [
|
|
2609
|
+
{ displayValue: "Global scope", value: "global" },
|
|
2610
|
+
{ displayValue: "Local scope", value: "local" }
|
|
2611
|
+
];
|
|
2612
|
+
return jsx(Select, { absolutePositionMenu: true, containerClassName: "bg-slate-50 text-gray-500 rounded-lg w-fit text-xs z-10", id: "scope select", itemClassName: "py-2 text-gray-500 grid grid-cols-[auto,auto] gap-1", items, menuClassName: "min-w-0 text-gray-500", onChange, value });
|
|
2613
|
+
}
|
|
2614
|
+
function Header(props) {
|
|
2615
|
+
const { title, docId, scope, onChangeScope, onClose, className, ...divProps } = props;
|
|
2616
|
+
return jsxs("header", { className: twMerge("flex items-center justify-between bg-transparent", className), ...divProps, children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx("button", { className: "shadow-button rounded-lg bg-gray-50 p-1 text-slate-100", onClick: onClose, children: jsx(Icon, { name: "VariantArrowLeft" }) }), jsx("h1", { className: "text-xs", children: title })] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(DocId, { docId }), jsx(Branch, {}), jsx(Scope, { onChange: onChangeScope, value: scope })] })] });
|
|
2617
|
+
}
|
|
2618
|
+
function memo(getDeps, fn, opts) {
|
|
2619
|
+
let deps = opts.initialDeps ?? [];
|
|
2620
|
+
let result;
|
|
2621
|
+
function memoizedFunction() {
|
|
2622
|
+
var _a, _b, _c, _d;
|
|
2623
|
+
let depTime;
|
|
2624
|
+
if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
|
|
2625
|
+
const newDeps = getDeps();
|
|
2626
|
+
const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
|
|
2627
|
+
if (!depsChanged) {
|
|
2628
|
+
return result;
|
|
2629
|
+
}
|
|
2630
|
+
deps = newDeps;
|
|
2631
|
+
let resultTime;
|
|
2632
|
+
if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
|
|
2633
|
+
result = fn(...newDeps);
|
|
2634
|
+
if (opts.key && ((_c = opts.debug) == null ? void 0 : _c.call(opts))) {
|
|
2635
|
+
const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
|
|
2636
|
+
const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
|
|
2637
|
+
const resultFpsPercentage = resultEndTime / 16;
|
|
2638
|
+
const pad = (str, num) => {
|
|
2639
|
+
str = String(str);
|
|
2640
|
+
while (str.length < num) {
|
|
2641
|
+
str = " " + str;
|
|
2642
|
+
}
|
|
2643
|
+
return str;
|
|
2644
|
+
};
|
|
2645
|
+
console.info(
|
|
2646
|
+
`%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
|
|
2647
|
+
`
|
|
2648
|
+
font-size: .6rem;
|
|
2649
|
+
font-weight: bold;
|
|
2650
|
+
color: hsl(${Math.max(
|
|
2651
|
+
0,
|
|
2652
|
+
Math.min(120 - 120 * resultFpsPercentage, 120)
|
|
2653
|
+
)}deg 100% 31%);`,
|
|
2654
|
+
opts == null ? void 0 : opts.key
|
|
2655
|
+
);
|
|
2656
|
+
}
|
|
2657
|
+
(_d = opts == null ? void 0 : opts.onChange) == null ? void 0 : _d.call(opts, result);
|
|
2658
|
+
return result;
|
|
2659
|
+
}
|
|
2660
|
+
memoizedFunction.updateDeps = (newDeps) => {
|
|
2661
|
+
deps = newDeps;
|
|
2662
|
+
};
|
|
2663
|
+
return memoizedFunction;
|
|
2664
|
+
}
|
|
2665
|
+
function notUndefined(value, msg) {
|
|
2666
|
+
if (value === void 0) {
|
|
2667
|
+
throw new Error(`Unexpected undefined${""}`);
|
|
2668
|
+
} else {
|
|
2669
|
+
return value;
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
const approxEqual = (a, b) => Math.abs(a - b) <= 1;
|
|
2673
|
+
const debounce = (targetWindow, fn, ms) => {
|
|
2674
|
+
let timeoutId;
|
|
2675
|
+
return function(...args) {
|
|
2676
|
+
targetWindow.clearTimeout(timeoutId);
|
|
2677
|
+
timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
|
|
2678
|
+
};
|
|
2679
|
+
};
|
|
2680
|
+
const getRect = (element) => {
|
|
2681
|
+
const { offsetWidth, offsetHeight } = element;
|
|
2682
|
+
return { width: offsetWidth, height: offsetHeight };
|
|
2683
|
+
};
|
|
2684
|
+
const defaultKeyExtractor = (index) => index;
|
|
2685
|
+
const defaultRangeExtractor = (range) => {
|
|
2686
|
+
const start = Math.max(range.startIndex - range.overscan, 0);
|
|
2687
|
+
const end = Math.min(range.endIndex + range.overscan, range.count - 1);
|
|
2688
|
+
const arr = [];
|
|
2689
|
+
for (let i = start; i <= end; i++) {
|
|
2690
|
+
arr.push(i);
|
|
2691
|
+
}
|
|
2692
|
+
return arr;
|
|
2693
|
+
};
|
|
2694
|
+
const observeElementRect = (instance, cb) => {
|
|
2695
|
+
const element = instance.scrollElement;
|
|
2696
|
+
if (!element) {
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
const targetWindow = instance.targetWindow;
|
|
2700
|
+
if (!targetWindow) {
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
const handler = (rect) => {
|
|
2704
|
+
const { width, height } = rect;
|
|
2705
|
+
cb({ width: Math.round(width), height: Math.round(height) });
|
|
2706
|
+
};
|
|
2707
|
+
handler(getRect(element));
|
|
2708
|
+
if (!targetWindow.ResizeObserver) {
|
|
2709
|
+
return () => {
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
const observer = new targetWindow.ResizeObserver((entries) => {
|
|
2713
|
+
const run = () => {
|
|
2714
|
+
const entry = entries[0];
|
|
2715
|
+
if (entry == null ? void 0 : entry.borderBoxSize) {
|
|
2716
|
+
const box = entry.borderBoxSize[0];
|
|
2717
|
+
if (box) {
|
|
2718
|
+
handler({ width: box.inlineSize, height: box.blockSize });
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
handler(getRect(element));
|
|
2723
|
+
};
|
|
2724
|
+
instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
2725
|
+
});
|
|
2726
|
+
observer.observe(element, { box: "border-box" });
|
|
2727
|
+
return () => {
|
|
2728
|
+
observer.unobserve(element);
|
|
2729
|
+
};
|
|
2730
|
+
};
|
|
2731
|
+
const addEventListenerOptions = {
|
|
2732
|
+
passive: true
|
|
2733
|
+
};
|
|
2734
|
+
const supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
|
|
2735
|
+
const observeElementOffset = (instance, cb) => {
|
|
2736
|
+
const element = instance.scrollElement;
|
|
2737
|
+
if (!element) {
|
|
2738
|
+
return;
|
|
2739
|
+
}
|
|
2740
|
+
const targetWindow = instance.targetWindow;
|
|
2741
|
+
if (!targetWindow) {
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
let offset = 0;
|
|
2745
|
+
const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
|
|
2746
|
+
targetWindow,
|
|
2747
|
+
() => {
|
|
2748
|
+
cb(offset, false);
|
|
2749
|
+
},
|
|
2750
|
+
instance.options.isScrollingResetDelay
|
|
2751
|
+
);
|
|
2752
|
+
const createHandler = (isScrolling) => () => {
|
|
2753
|
+
const { horizontal, isRtl } = instance.options;
|
|
2754
|
+
offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
|
|
2755
|
+
fallback();
|
|
2756
|
+
cb(offset, isScrolling);
|
|
2757
|
+
};
|
|
2758
|
+
const handler = createHandler(true);
|
|
2759
|
+
const endHandler = createHandler(false);
|
|
2760
|
+
endHandler();
|
|
2761
|
+
element.addEventListener("scroll", handler, addEventListenerOptions);
|
|
2762
|
+
const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
|
|
2763
|
+
if (registerScrollendEvent) {
|
|
2764
|
+
element.addEventListener("scrollend", endHandler, addEventListenerOptions);
|
|
2765
|
+
}
|
|
2766
|
+
return () => {
|
|
2767
|
+
element.removeEventListener("scroll", handler);
|
|
2768
|
+
if (registerScrollendEvent) {
|
|
2769
|
+
element.removeEventListener("scrollend", endHandler);
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
};
|
|
2773
|
+
const measureElement = (element, entry, instance) => {
|
|
2774
|
+
if (entry == null ? void 0 : entry.borderBoxSize) {
|
|
2775
|
+
const box = entry.borderBoxSize[0];
|
|
2776
|
+
if (box) {
|
|
2777
|
+
const size = Math.round(
|
|
2778
|
+
box[instance.options.horizontal ? "inlineSize" : "blockSize"]
|
|
2779
|
+
);
|
|
2780
|
+
return size;
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
|
|
2784
|
+
};
|
|
2785
|
+
const elementScroll = (offset, {
|
|
2786
|
+
adjustments = 0,
|
|
2787
|
+
behavior
|
|
2788
|
+
}, instance) => {
|
|
2789
|
+
var _a, _b;
|
|
2790
|
+
const toOffset = offset + adjustments;
|
|
2791
|
+
(_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
|
|
2792
|
+
[instance.options.horizontal ? "left" : "top"]: toOffset,
|
|
2793
|
+
behavior
|
|
2794
|
+
});
|
|
2795
|
+
};
|
|
2796
|
+
class Virtualizer {
|
|
2797
|
+
constructor(opts) {
|
|
2798
|
+
this.unsubs = [];
|
|
2799
|
+
this.scrollElement = null;
|
|
2800
|
+
this.targetWindow = null;
|
|
2801
|
+
this.isScrolling = false;
|
|
2802
|
+
this.scrollToIndexTimeoutId = null;
|
|
2803
|
+
this.measurementsCache = [];
|
|
2804
|
+
this.itemSizeCache = /* @__PURE__ */ new Map();
|
|
2805
|
+
this.pendingMeasuredCacheIndexes = [];
|
|
2806
|
+
this.scrollRect = null;
|
|
2807
|
+
this.scrollOffset = null;
|
|
2808
|
+
this.scrollDirection = null;
|
|
2809
|
+
this.scrollAdjustments = 0;
|
|
2810
|
+
this.elementsCache = /* @__PURE__ */ new Map();
|
|
2811
|
+
this.observer = /* @__PURE__ */ (() => {
|
|
2812
|
+
let _ro = null;
|
|
2813
|
+
const get = () => {
|
|
2814
|
+
if (_ro) {
|
|
2815
|
+
return _ro;
|
|
2816
|
+
}
|
|
2817
|
+
if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
|
|
2818
|
+
return null;
|
|
2819
|
+
}
|
|
2820
|
+
return _ro = new this.targetWindow.ResizeObserver((entries) => {
|
|
2821
|
+
entries.forEach((entry) => {
|
|
2822
|
+
const run = () => {
|
|
2823
|
+
this._measureElement(entry.target, entry);
|
|
2824
|
+
};
|
|
2825
|
+
this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
2826
|
+
});
|
|
2827
|
+
});
|
|
2828
|
+
};
|
|
2829
|
+
return {
|
|
2830
|
+
disconnect: () => {
|
|
2831
|
+
var _a;
|
|
2832
|
+
(_a = get()) == null ? void 0 : _a.disconnect();
|
|
2833
|
+
_ro = null;
|
|
2834
|
+
},
|
|
2835
|
+
observe: (target) => {
|
|
2836
|
+
var _a;
|
|
2042
2837
|
return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
|
|
2043
2838
|
},
|
|
2044
2839
|
unobserve: (target) => {
|
|
@@ -2481,9 +3276,9 @@ class Virtualizer {
|
|
|
2481
3276
|
this.options.getItemKey(index)
|
|
2482
3277
|
);
|
|
2483
3278
|
if (elementInDOM) {
|
|
2484
|
-
const
|
|
2485
|
-
|
|
2486
|
-
|
|
3279
|
+
const result = this.getOffsetForIndex(index, align);
|
|
3280
|
+
if (!result) return;
|
|
3281
|
+
const [latestOffset] = result;
|
|
2487
3282
|
const currentScrollOffset = this.getScrollOffset();
|
|
2488
3283
|
if (!approxEqual(latestOffset, currentScrollOffset)) {
|
|
2489
3284
|
this.scrollToIndex(index, { align, behavior });
|
|
@@ -2858,8 +3653,8 @@ function RevisionHistory(props) {
|
|
|
2858
3653
|
return jsxs(TooltipProvider, { children: [jsx(Header, { docId: documentId, onChangeScope, onClose, scope, title: documentTitle }), PaginationComponent, jsx("div", { className: "mt-4 flex justify-center rounded-md bg-slate-50 p-4", children: visibleOperations.length > 0 ? jsx("div", { className: "grid grid-cols-[minmax(min-content,1018px)]", children: jsx(Timeline, { globalOperations: scope === "global" ? pageItems : [], localOperations: scope === "local" ? pageItems : [], scope }) }) : jsx("h3", { className: "my-40 text-gray-600", children: "This document has no recorded operations yet." }) }), PaginationComponent] });
|
|
2859
3654
|
}
|
|
2860
3655
|
function Tooltip(props) {
|
|
2861
|
-
const { children, content, open, defaultOpen, onOpenChange, className, ...rest } = props;
|
|
2862
|
-
return jsxs(Root3, { defaultOpen, delayDuration: 0, onOpenChange, open, children: [jsx(Trigger, { asChild: true, children }), jsx(Portal, { children: jsx(Content2, { ...rest, className: twMerge("shadow-tooltip rounded-lg border border-gray-200 bg-white p-2 text-xs", className), children: content }) })] });
|
|
3656
|
+
const { children, content, open, defaultOpen, onOpenChange, className, side = "top", sideOffset = 5, ...rest } = props;
|
|
3657
|
+
return jsxs(Root3, { defaultOpen, delayDuration: 0, onOpenChange, open, children: [jsx(Trigger, { asChild: true, children }), jsx(Portal, { children: jsx(Content2, { ...rest, side, sideOffset, className: twMerge("shadow-tooltip rounded-lg border border-gray-200 bg-white p-2 text-xs", className), children: content }) })] });
|
|
2863
3658
|
}
|
|
2864
3659
|
const TooltipProvider = Provider;
|
|
2865
3660
|
const BUDGET = "powerhouse/budget-statement";
|
|
@@ -2883,471 +3678,150 @@ function useDrag(props) {
|
|
|
2883
3678
|
const onDragEnd = useCallback(() => {
|
|
2884
3679
|
setIsDragging(false);
|
|
2885
3680
|
}, []);
|
|
2886
|
-
const allowedToDragNode = !!uiNode && uiNode.kind !== DRIVE;
|
|
2887
|
-
return useMemo(() => {
|
|
2888
|
-
const dragProps = allowedToDragNode ? {
|
|
2889
|
-
draggable: true,
|
|
2890
|
-
onDragStart,
|
|
2891
|
-
onDragEnd
|
|
2892
|
-
} : {
|
|
2893
|
-
draggable: false,
|
|
2894
|
-
onDragStart: void 0,
|
|
2895
|
-
onDragEnd: void 0
|
|
2896
|
-
};
|
|
2897
|
-
return {
|
|
2898
|
-
isDragging,
|
|
2899
|
-
dragProps
|
|
2900
|
-
};
|
|
2901
|
-
}, [allowedToDragNode, isDragging, onDragEnd, onDragStart]);
|
|
2902
|
-
}
|
|
2903
|
-
function useDrop(props) {
|
|
2904
|
-
const { uiNode, onAddFile, onCopyNode, onMoveNode } = props;
|
|
2905
|
-
const [isDropTarget, setIsDropTarget] = useState(false);
|
|
2906
|
-
const allowedToBeDropTarget = !!uiNode && uiNode.kind !== FILE;
|
|
2907
|
-
const onDragOver = useCallback((event) => {
|
|
2908
|
-
event.preventDefault();
|
|
2909
|
-
setIsDropTarget(true);
|
|
2910
|
-
}, []);
|
|
2911
|
-
const onDragLeave = useCallback(() => {
|
|
2912
|
-
setIsDropTarget(false);
|
|
2913
|
-
}, []);
|
|
2914
|
-
const onDrop = useCallback(async (event) => {
|
|
2915
|
-
event.preventDefault();
|
|
2916
|
-
event.stopPropagation();
|
|
2917
|
-
if (!uiNode)
|
|
2918
|
-
return;
|
|
2919
|
-
const droppedFiles = getDroppedFiles(event.dataTransfer.items).filter(Boolean);
|
|
2920
|
-
if (droppedFiles.length) {
|
|
2921
|
-
for (const file of droppedFiles) {
|
|
2922
|
-
if (file) {
|
|
2923
|
-
await onAddFile(file, uiNode);
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
|
-
} else {
|
|
2927
|
-
const altOrOptionKeyPressed = event.getModifierState("Alt");
|
|
2928
|
-
const data = event.dataTransfer.getData(UI_NODE);
|
|
2929
|
-
const droppedNode = JSON.parse(data);
|
|
2930
|
-
if (altOrOptionKeyPressed) {
|
|
2931
|
-
await onCopyNode(droppedNode, uiNode);
|
|
2932
|
-
} else {
|
|
2933
|
-
await onMoveNode(droppedNode, uiNode);
|
|
2934
|
-
}
|
|
2935
|
-
}
|
|
2936
|
-
setIsDropTarget(false);
|
|
2937
|
-
}, [onAddFile, onCopyNode, onMoveNode, uiNode]);
|
|
2938
|
-
return useMemo(() => {
|
|
2939
|
-
const dropProps = allowedToBeDropTarget ? { onDragOver, onDragLeave, onDrop } : {
|
|
2940
|
-
onDragOver: void 0,
|
|
2941
|
-
onDragLeave: void 0,
|
|
2942
|
-
onDrop: void 0
|
|
2943
|
-
};
|
|
2944
|
-
return {
|
|
2945
|
-
isDropTarget,
|
|
2946
|
-
dropProps
|
|
2947
|
-
};
|
|
2948
|
-
}, [allowedToBeDropTarget, isDropTarget, onDragLeave, onDragOver, onDrop]);
|
|
2949
|
-
}
|
|
2950
|
-
function getDroppedFiles(items) {
|
|
2951
|
-
const droppedFiles = Array.from(items).map((item) => item.kind === "file" ? item.getAsFile() : null).filter(Boolean);
|
|
2952
|
-
return droppedFiles;
|
|
2953
|
-
}
|
|
2954
|
-
function formatEthAddress(address) {
|
|
2955
|
-
return `${address.slice(0, 7)}...${address.slice(-5)}`;
|
|
2956
|
-
}
|
|
2957
|
-
function getDocumentIconSrc(documentType, customDocumentIconSrc) {
|
|
2958
|
-
if (customDocumentIconSrc) {
|
|
2959
|
-
return customDocumentIconSrc;
|
|
2960
|
-
}
|
|
2961
|
-
if (documentTypes.includes(documentType)) {
|
|
2962
|
-
return iconMap[documentType];
|
|
2963
|
-
}
|
|
2964
|
-
return iconMap[DEFAULT];
|
|
2965
|
-
}
|
|
2966
|
-
const validateDocument = (document2) => {
|
|
2967
|
-
const errors = [];
|
|
2968
|
-
if (document2.documentType !== "powerhouse/document-model") {
|
|
2969
|
-
return errors;
|
|
2970
|
-
}
|
|
2971
|
-
const doc = document2;
|
|
2972
|
-
const specs = doc.state.global.specifications[0];
|
|
2973
|
-
const initialStateErrors = Object.keys(specs.state).reduce((acc, scopeKey) => {
|
|
2974
|
-
const scope = scopeKey;
|
|
2975
|
-
return [
|
|
2976
|
-
...acc,
|
|
2977
|
-
...validateInitialState(
|
|
2978
|
-
specs.state[scope].initialValue,
|
|
2979
|
-
scope !== "global"
|
|
2980
|
-
).map((err) => ({
|
|
2981
|
-
...err,
|
|
2982
|
-
message: `${err.message}. Scope: ${scope}`,
|
|
2983
|
-
details: { ...err.details, scope }
|
|
2984
|
-
}))
|
|
2985
|
-
];
|
|
2986
|
-
}, []);
|
|
2987
|
-
const schemaStateErrors = Object.keys(specs.state).reduce((acc, scopeKey) => {
|
|
2988
|
-
var _a;
|
|
2989
|
-
const scope = scopeKey;
|
|
2990
|
-
const isGlobalScope = scope === "global";
|
|
2991
|
-
return [
|
|
2992
|
-
...acc,
|
|
2993
|
-
...validateStateSchemaName(
|
|
2994
|
-
specs.state[scope].schema,
|
|
2995
|
-
doc.name || ((_a = doc.state.global) == null ? void 0 : _a.name) || "",
|
|
2996
|
-
!isGlobalScope ? scope : "",
|
|
2997
|
-
!isGlobalScope
|
|
2998
|
-
).map((err) => ({
|
|
2999
|
-
...err,
|
|
3000
|
-
message: `${err.message}. Scope: ${scope}`,
|
|
3001
|
-
details: { ...err.details, scope }
|
|
3002
|
-
}))
|
|
3003
|
-
];
|
|
3004
|
-
}, []);
|
|
3005
|
-
const modulesErrors = validateModules(specs.modules);
|
|
3006
|
-
return [...initialStateErrors, ...schemaStateErrors, ...modulesErrors];
|
|
3007
|
-
};
|
|
3008
|
-
function useUserPermissions() {
|
|
3009
|
-
return {
|
|
3010
|
-
isAllowedToCreateDocuments: true,
|
|
3011
|
-
isAllowedToEditDocuments: true
|
|
3012
|
-
};
|
|
3013
|
-
}
|
|
3014
|
-
function useDocument(reactor, documentMeta = {}) {
|
|
3015
|
-
const { documentId, documentType, driveId } = documentMeta;
|
|
3016
|
-
const [document2, setDocument] = useState();
|
|
3017
|
-
const onStrandUpdate = useCallback((cb) => {
|
|
3018
|
-
if (!reactor) {
|
|
3019
|
-
throw new Error("Reactor is not loaded");
|
|
3020
|
-
}
|
|
3021
|
-
return reactor.on("strandUpdate", cb);
|
|
3022
|
-
}, [reactor]);
|
|
3023
|
-
useEffect(() => {
|
|
3024
|
-
if (!reactor)
|
|
3025
|
-
return;
|
|
3026
|
-
if (!driveId || !documentId || !documentType)
|
|
3027
|
-
return;
|
|
3028
|
-
reactor.getDocument(driveId, documentId).then(setDocument).catch(console.error);
|
|
3029
|
-
}, [driveId, documentId, documentType, reactor]);
|
|
3030
|
-
useEffect(() => {
|
|
3031
|
-
if (!reactor)
|
|
3032
|
-
return;
|
|
3033
|
-
if (!driveId || !documentId || !documentType)
|
|
3034
|
-
return;
|
|
3035
|
-
const removeListener = onStrandUpdate((strand) => {
|
|
3036
|
-
if (strand.driveId === driveId && strand.documentId === documentId) {
|
|
3037
|
-
reactor.getDocument(driveId, documentId).then(setDocument).catch(console.error);
|
|
3038
|
-
}
|
|
3039
|
-
});
|
|
3040
|
-
return removeListener;
|
|
3041
|
-
}, [onStrandUpdate, driveId, documentId, documentType]);
|
|
3042
|
-
return document2;
|
|
3043
|
-
}
|
|
3044
|
-
async function signOperation(operation, sign, documentId, document2, reducer, user) {
|
|
3045
|
-
if (!user)
|
|
3046
|
-
return operation;
|
|
3047
|
-
if (!operation.context)
|
|
3048
|
-
return operation;
|
|
3049
|
-
if (!operation.context.signer)
|
|
3050
|
-
return operation;
|
|
3051
|
-
if (!reducer) {
|
|
3052
|
-
console.error(`Document model '${document2.documentType}' does not have a reducer`);
|
|
3053
|
-
return operation;
|
|
3054
|
-
}
|
|
3055
|
-
const context = {
|
|
3056
|
-
documentId,
|
|
3057
|
-
signer: operation.context.signer
|
|
3058
|
-
};
|
|
3059
|
-
const signedOperation = await buildSignedOperation(operation, reducer, document2, context, sign);
|
|
3060
|
-
return signedOperation;
|
|
3061
|
-
}
|
|
3062
|
-
function addActionContext(action, connectDid, user) {
|
|
3063
|
-
if (!user)
|
|
3064
|
-
return action;
|
|
3065
|
-
const signer = {
|
|
3066
|
-
app: {
|
|
3067
|
-
name: "Connect",
|
|
3068
|
-
key: connectDid || ""
|
|
3069
|
-
},
|
|
3070
|
-
user: {
|
|
3071
|
-
address: user.address,
|
|
3072
|
-
networkId: user.networkId,
|
|
3073
|
-
chainId: user.chainId
|
|
3074
|
-
},
|
|
3075
|
-
signatures: []
|
|
3076
|
-
};
|
|
3077
|
-
return {
|
|
3078
|
-
context: { signer },
|
|
3079
|
-
...action
|
|
3080
|
-
};
|
|
3081
|
-
}
|
|
3082
|
-
function debounceOperations(callback, timeout = 50) {
|
|
3083
|
-
let timer;
|
|
3084
|
-
const operations = [];
|
|
3085
|
-
return (operation) => {
|
|
3086
|
-
if (timer) {
|
|
3087
|
-
clearTimeout(timer);
|
|
3088
|
-
}
|
|
3089
|
-
const index = operations.findIndex((op) => op.scope === operation.scope && op.index === operation.index);
|
|
3090
|
-
if (index > -1) {
|
|
3091
|
-
const oldOperation = operations[index];
|
|
3092
|
-
if (!(oldOperation.type === operation.type && JSON.stringify(operation.input) === JSON.stringify(oldOperation.input))) {
|
|
3093
|
-
console.warn("Two conflicting operations were dispatched:", oldOperation, operation);
|
|
3094
|
-
}
|
|
3095
|
-
operations[index] = operation;
|
|
3096
|
-
} else {
|
|
3097
|
-
operations.push(operation);
|
|
3098
|
-
}
|
|
3099
|
-
return new Promise((resolve, reject) => {
|
|
3100
|
-
timer = setTimeout(() => {
|
|
3101
|
-
callback(operations).then(resolve).catch(reject);
|
|
3102
|
-
}, timeout);
|
|
3103
|
-
});
|
|
3104
|
-
};
|
|
3105
|
-
}
|
|
3106
|
-
function useAddDebouncedOperations(reactor, props) {
|
|
3107
|
-
const { driveId, documentId } = props;
|
|
3108
|
-
const [documentDrives] = useDocumentDrives(reactor);
|
|
3109
|
-
const documentDrivesRef = useRef(documentDrives);
|
|
3110
|
-
const { isAllowedToEditDocuments } = useUserPermissions() || {
|
|
3111
|
-
isAllowedToEditDocuments: false
|
|
3112
|
-
};
|
|
3113
|
-
useEffect(() => {
|
|
3114
|
-
documentDrivesRef.current = documentDrives;
|
|
3115
|
-
}, [documentDrives]);
|
|
3116
|
-
const addOperations = useCallback(async (driveId2, id, operations) => {
|
|
3117
|
-
if (!isAllowedToEditDocuments) {
|
|
3118
|
-
throw new Error("User is not allowed to edit documents");
|
|
3119
|
-
}
|
|
3120
|
-
if (!reactor) {
|
|
3121
|
-
throw new Error("Reactor is not loaded");
|
|
3122
|
-
}
|
|
3123
|
-
const drive = documentDrivesRef.current.find((drive2) => drive2.state.global.id === driveId2);
|
|
3124
|
-
if (!drive) {
|
|
3125
|
-
throw new Error(`Drive with id ${driveId2} not found`);
|
|
3126
|
-
}
|
|
3127
|
-
const newDocument = await reactor.queueOperations(driveId2, id, operations);
|
|
3128
|
-
return newDocument.document;
|
|
3129
|
-
}, [isAllowedToEditDocuments, reactor]);
|
|
3130
|
-
const addDebouncedOperations = useMemo(() => {
|
|
3131
|
-
return debounceOperations((operations) => addOperations(driveId, documentId, operations));
|
|
3132
|
-
}, [addOperations, driveId, documentId]);
|
|
3133
|
-
return addDebouncedOperations;
|
|
3681
|
+
const allowedToDragNode = !!uiNode && uiNode.kind !== DRIVE;
|
|
3682
|
+
return useMemo(() => {
|
|
3683
|
+
const dragProps = allowedToDragNode ? {
|
|
3684
|
+
draggable: true,
|
|
3685
|
+
onDragStart,
|
|
3686
|
+
onDragEnd
|
|
3687
|
+
} : {
|
|
3688
|
+
draggable: false,
|
|
3689
|
+
onDragStart: void 0,
|
|
3690
|
+
onDragEnd: void 0
|
|
3691
|
+
};
|
|
3692
|
+
return {
|
|
3693
|
+
isDragging,
|
|
3694
|
+
dragProps
|
|
3695
|
+
};
|
|
3696
|
+
}, [allowedToDragNode, isDragging, onDragEnd, onDragStart]);
|
|
3134
3697
|
}
|
|
3135
|
-
function
|
|
3136
|
-
const
|
|
3137
|
-
const [
|
|
3138
|
-
const
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
const error2 = new Error(operation.error);
|
|
3157
|
-
onErrorHandler(error2);
|
|
3158
|
-
onErrorCallback == null ? void 0 : onErrorCallback(error2);
|
|
3698
|
+
function useDrop(props) {
|
|
3699
|
+
const { uiNode, onAddFile, onCopyNode, onMoveNode } = props;
|
|
3700
|
+
const [isDropTarget, setIsDropTarget] = useState(false);
|
|
3701
|
+
const allowedToBeDropTarget = !!uiNode && uiNode.kind !== FILE$1;
|
|
3702
|
+
const onDragOver = useCallback((event) => {
|
|
3703
|
+
event.preventDefault();
|
|
3704
|
+
setIsDropTarget(true);
|
|
3705
|
+
}, []);
|
|
3706
|
+
const onDragLeave = useCallback(() => {
|
|
3707
|
+
setIsDropTarget(false);
|
|
3708
|
+
}, []);
|
|
3709
|
+
const onDrop = useCallback(async (event) => {
|
|
3710
|
+
event.preventDefault();
|
|
3711
|
+
event.stopPropagation();
|
|
3712
|
+
if (!uiNode)
|
|
3713
|
+
return;
|
|
3714
|
+
const droppedFiles = getDroppedFiles(event.dataTransfer.items).filter(Boolean);
|
|
3715
|
+
if (droppedFiles.length) {
|
|
3716
|
+
for (const file of droppedFiles) {
|
|
3717
|
+
if (file) {
|
|
3718
|
+
await onAddFile(file, uiNode);
|
|
3159
3719
|
}
|
|
3160
|
-
callback == null ? void 0 : callback(operation, {
|
|
3161
|
-
prevState: { ..._state },
|
|
3162
|
-
newState: { ...newState }
|
|
3163
|
-
});
|
|
3164
|
-
return newState;
|
|
3165
|
-
} catch (error2) {
|
|
3166
|
-
onErrorHandler(error2);
|
|
3167
|
-
onErrorCallback == null ? void 0 : onErrorCallback(error2);
|
|
3168
|
-
return _state;
|
|
3169
3720
|
}
|
|
3170
|
-
}
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3721
|
+
} else {
|
|
3722
|
+
const altOrOptionKeyPressed = event.getModifierState("Alt");
|
|
3723
|
+
const data = event.dataTransfer.getData(UI_NODE);
|
|
3724
|
+
const droppedNode = JSON.parse(data);
|
|
3725
|
+
if (altOrOptionKeyPressed) {
|
|
3726
|
+
await onCopyNode(droppedNode, uiNode);
|
|
3727
|
+
} else {
|
|
3728
|
+
await onMoveNode(droppedNode, uiNode);
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
setIsDropTarget(false);
|
|
3732
|
+
}, [onAddFile, onCopyNode, onMoveNode, uiNode]);
|
|
3733
|
+
return useMemo(() => {
|
|
3734
|
+
const dropProps = allowedToBeDropTarget ? { onDragOver, onDragLeave, onDrop } : {
|
|
3735
|
+
onDragOver: void 0,
|
|
3736
|
+
onDragLeave: void 0,
|
|
3737
|
+
onDrop: void 0
|
|
3187
3738
|
};
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
error
|
|
3194
|
-
};
|
|
3739
|
+
return {
|
|
3740
|
+
isDropTarget,
|
|
3741
|
+
dropProps
|
|
3742
|
+
};
|
|
3743
|
+
}, [allowedToBeDropTarget, isDropTarget, onDragLeave, onDragOver, onDrop]);
|
|
3195
3744
|
}
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
return
|
|
3745
|
+
function getDroppedFiles(items) {
|
|
3746
|
+
const droppedFiles = Array.from(items).map((item) => item.kind === "file" ? item.getAsFile() : null).filter(Boolean);
|
|
3747
|
+
return droppedFiles;
|
|
3199
3748
|
}
|
|
3200
|
-
function
|
|
3201
|
-
|
|
3202
|
-
const { id: driveId } = drive.state.global;
|
|
3203
|
-
const { selectedNode } = context;
|
|
3204
|
-
const handleAddFolder = async (name, parentFolder, id = generateId()) => {
|
|
3205
|
-
dispatch(addFolder({
|
|
3206
|
-
id,
|
|
3207
|
-
name,
|
|
3208
|
-
parentFolder: parentFolder ?? null
|
|
3209
|
-
}));
|
|
3210
|
-
};
|
|
3211
|
-
const addDocument = async (name, documentType, document3, parentFolder, id = generateId()) => {
|
|
3212
|
-
const action = generateAddNodeAction(drive.state.global, {
|
|
3213
|
-
id,
|
|
3214
|
-
name,
|
|
3215
|
-
parentFolder: parentFolder ?? null,
|
|
3216
|
-
documentType,
|
|
3217
|
-
document: document3
|
|
3218
|
-
}, ["global"]);
|
|
3219
|
-
dispatch(action);
|
|
3220
|
-
};
|
|
3221
|
-
const addFile = async (file, parentFolder = selectedNode && isFileNode(selectedNode) ? void 0 : selectedNode == null ? void 0 : selectedNode.id, name = file.name.replace(/\.zip$/gim, "")) => {
|
|
3222
|
-
const folder = parentFolder ? getNode(parentFolder, drive) : void 0;
|
|
3223
|
-
if (parentFolder && !folder) {
|
|
3224
|
-
throw new Error(`Parent folder with id "${parentFolder}" not found`);
|
|
3225
|
-
}
|
|
3226
|
-
if (folder && !isFolderNode(folder)) {
|
|
3227
|
-
throw new Error(`Parent folder with id "${parentFolder}" is not a folder`);
|
|
3228
|
-
}
|
|
3229
|
-
await context.addFile(file, driveId, name, parentFolder);
|
|
3230
|
-
};
|
|
3231
|
-
const handleDeleteNode = async (id) => {
|
|
3232
|
-
dispatch(deleteNode({ id }));
|
|
3233
|
-
};
|
|
3234
|
-
const renameNode = async (id, name) => {
|
|
3235
|
-
dispatch(updateNode({ id, name }));
|
|
3236
|
-
};
|
|
3237
|
-
const handleMoveNode = async (sourceId, targetId) => {
|
|
3238
|
-
dispatch(moveNode({
|
|
3239
|
-
srcFolder: sourceId,
|
|
3240
|
-
targetParentFolder: targetId
|
|
3241
|
-
}));
|
|
3242
|
-
};
|
|
3243
|
-
const handleCopyNode = async (sourceId, targetFolderId) => {
|
|
3244
|
-
const target = targetFolderId ? getNode(targetFolderId, drive) : void 0;
|
|
3245
|
-
if (targetFolderId && !target && targetFolderId !== driveId) {
|
|
3246
|
-
throw new Error(`Target node with id "${targetFolderId}" not found`);
|
|
3247
|
-
}
|
|
3248
|
-
if (target && !isFolderNode(target)) {
|
|
3249
|
-
throw new Error(`Target node with id "${targetFolderId}" is not a folder`);
|
|
3250
|
-
}
|
|
3251
|
-
const source = getNode(sourceId, drive);
|
|
3252
|
-
if (!source) {
|
|
3253
|
-
throw new Error(`Source node with id "${sourceId}" not found`);
|
|
3254
|
-
}
|
|
3255
|
-
const copyNodesInput = generateNodesCopy({
|
|
3256
|
-
srcId: sourceId,
|
|
3257
|
-
targetParentFolder: target == null ? void 0 : target.id,
|
|
3258
|
-
targetName: source.name
|
|
3259
|
-
}, generateId, drive.state.global.nodes);
|
|
3260
|
-
const copyActions = copyNodesInput.map((copyNodeInput) => copyNode(copyNodeInput));
|
|
3261
|
-
for (const copyAction of copyActions) {
|
|
3262
|
-
dispatch(copyAction);
|
|
3263
|
-
}
|
|
3264
|
-
};
|
|
3265
|
-
const duplicateNode = async (sourceId) => {
|
|
3266
|
-
const node = getNode(sourceId, drive);
|
|
3267
|
-
if (!node) {
|
|
3268
|
-
throw new Error(`Node with id "${sourceId}" not found`);
|
|
3269
|
-
}
|
|
3270
|
-
await handleCopyNode(node.id, node.parentFolder || void 0);
|
|
3271
|
-
};
|
|
3272
|
-
return {
|
|
3273
|
-
context,
|
|
3274
|
-
selectNode: context.selectNode,
|
|
3275
|
-
addFolder: handleAddFolder,
|
|
3276
|
-
addFile,
|
|
3277
|
-
addDocument,
|
|
3278
|
-
deleteNode: handleDeleteNode,
|
|
3279
|
-
renameNode,
|
|
3280
|
-
moveNode: handleMoveNode,
|
|
3281
|
-
copyNode: handleCopyNode,
|
|
3282
|
-
duplicateNode
|
|
3283
|
-
};
|
|
3749
|
+
function formatEthAddress(address) {
|
|
3750
|
+
return `${address.slice(0, 7)}...${address.slice(-5)}`;
|
|
3284
3751
|
}
|
|
3285
|
-
function
|
|
3286
|
-
|
|
3752
|
+
function getDocumentIconSrc(documentType, customDocumentIconSrc) {
|
|
3753
|
+
if (customDocumentIconSrc) {
|
|
3754
|
+
return customDocumentIconSrc;
|
|
3755
|
+
}
|
|
3756
|
+
if (documentTypes.includes(documentType)) {
|
|
3757
|
+
return iconMap[documentType];
|
|
3758
|
+
}
|
|
3759
|
+
return iconMap[DEFAULT];
|
|
3287
3760
|
}
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3761
|
+
const validateDocument = (document2) => {
|
|
3762
|
+
const errors = [];
|
|
3763
|
+
if (document2.documentType !== "powerhouse/document-model") {
|
|
3764
|
+
return errors;
|
|
3291
3765
|
}
|
|
3292
|
-
const
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3766
|
+
const doc = document2;
|
|
3767
|
+
const specs = doc.state.global.specifications[0];
|
|
3768
|
+
const initialStateErrors = Object.keys(specs.state).reduce((acc, scopeKey) => {
|
|
3769
|
+
const scope = scopeKey;
|
|
3770
|
+
return [
|
|
3771
|
+
...acc,
|
|
3772
|
+
...validateInitialState(
|
|
3773
|
+
specs.state[scope].initialValue,
|
|
3774
|
+
scope !== "global"
|
|
3775
|
+
).map((err) => ({
|
|
3776
|
+
...err,
|
|
3777
|
+
message: `${err.message}. Scope: ${scope}`,
|
|
3778
|
+
details: { ...err.details, scope }
|
|
3779
|
+
}))
|
|
3780
|
+
];
|
|
3781
|
+
}, []);
|
|
3782
|
+
const schemaStateErrors = Object.keys(specs.state).reduce((acc, scopeKey) => {
|
|
3783
|
+
var _a;
|
|
3784
|
+
const scope = scopeKey;
|
|
3785
|
+
const isGlobalScope = scope === "global";
|
|
3786
|
+
return [
|
|
3787
|
+
...acc,
|
|
3788
|
+
...validateStateSchemaName(
|
|
3789
|
+
specs.state[scope].schema,
|
|
3790
|
+
doc.name || ((_a = doc.state.global) == null ? void 0 : _a.name) || "",
|
|
3791
|
+
!isGlobalScope ? scope : "",
|
|
3792
|
+
!isGlobalScope
|
|
3793
|
+
).map((err) => ({
|
|
3794
|
+
...err,
|
|
3795
|
+
message: `${err.message}. Scope: ${scope}`,
|
|
3796
|
+
details: { ...err.details, scope }
|
|
3797
|
+
}))
|
|
3798
|
+
];
|
|
3799
|
+
}, []);
|
|
3800
|
+
const modulesErrors = validateModules(specs.modules);
|
|
3801
|
+
return [...initialStateErrors, ...schemaStateErrors, ...modulesErrors];
|
|
3802
|
+
};
|
|
3803
|
+
function useDocumentDriveById(driveId) {
|
|
3804
|
+
var _a;
|
|
3805
|
+
const { documentDrives } = useDocumentDriveServer();
|
|
3806
|
+
if (!driveId)
|
|
3297
3807
|
return {
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
kind: "file",
|
|
3302
|
-
documentType: fileNode.documentType,
|
|
3303
|
-
synchronizationUnits: fileNode.synchronizationUnits
|
|
3808
|
+
drive: null,
|
|
3809
|
+
remoteUrl: null,
|
|
3810
|
+
isRemoteDrive: false
|
|
3304
3811
|
};
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3812
|
+
const drive = documentDrives.find((drive2) => drive2.id === driveId);
|
|
3813
|
+
const pullResponder = drive == null ? void 0 : drive.state.local.triggers.find(
|
|
3814
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
3815
|
+
(trigger) => trigger.type === "PullResponder"
|
|
3816
|
+
);
|
|
3817
|
+
const { remoteUrl } = (drive == null ? void 0 : drive.state.global) || {};
|
|
3818
|
+
const isRemoteDrive = !!remoteUrl || !!pullResponder;
|
|
3308
3819
|
return {
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
renameNode: (name, node) => {
|
|
3313
|
-
const converted = toNode(node);
|
|
3314
|
-
return driveActions.renameNode(converted.id, name);
|
|
3315
|
-
},
|
|
3316
|
-
deleteNode: (node) => {
|
|
3317
|
-
const converted = toNode(node);
|
|
3318
|
-
return driveActions.deleteNode(converted.id);
|
|
3319
|
-
},
|
|
3320
|
-
moveNode: async (src, target) => {
|
|
3321
|
-
if (target.kind === FILE$1 || src.parentFolder === target.id)
|
|
3322
|
-
return;
|
|
3323
|
-
const srcNode = toNode(src);
|
|
3324
|
-
const targetNode = toNode(target);
|
|
3325
|
-
return driveActions.moveNode(srcNode.id, targetNode.id);
|
|
3326
|
-
},
|
|
3327
|
-
copyNode: (src, target) => {
|
|
3328
|
-
return driveActions.copyNode(src.id, target.id);
|
|
3329
|
-
},
|
|
3330
|
-
duplicateNode: (node) => {
|
|
3331
|
-
const converted = toNode(node);
|
|
3332
|
-
return driveActions.duplicateNode(converted.id);
|
|
3333
|
-
}
|
|
3820
|
+
drive,
|
|
3821
|
+
remoteUrl: remoteUrl || ((_a = pullResponder == null ? void 0 : pullResponder.data) == null ? void 0 : _a.url),
|
|
3822
|
+
isRemoteDrive
|
|
3334
3823
|
};
|
|
3335
3824
|
}
|
|
3336
|
-
function useDriveActionsWithUiNodes(document2, dispatch) {
|
|
3337
|
-
const { selectedNode, selectedDriveNode, setSelectedNode, getNodeById } = useUiNodesContext();
|
|
3338
|
-
const _driveContext = useDriveContext();
|
|
3339
|
-
const driveContext = useMemo(() => ({
|
|
3340
|
-
..._driveContext,
|
|
3341
|
-
selectedNode,
|
|
3342
|
-
onSelectNode: (node) => {
|
|
3343
|
-
_driveContext.selectNode(node);
|
|
3344
|
-
setSelectedNode(getNodeById(node.id));
|
|
3345
|
-
}
|
|
3346
|
-
}), [selectedNode, selectedDriveNode == null ? void 0 : selectedDriveNode.driveId, setSelectedNode, getNodeById]);
|
|
3347
|
-
const driveActions = useDriveActions(document2, dispatch, driveContext);
|
|
3348
|
-
const uiNodeActions = useMemo(() => createUiNodeAdapter(driveActions), [driveActions]);
|
|
3349
|
-
return uiNodeActions;
|
|
3350
|
-
}
|
|
3351
3825
|
function useDocumentEditor(props) {
|
|
3352
3826
|
const { driveId, documentId, documentType, documentModelModule, user } = props;
|
|
3353
3827
|
const reactor = useUnwrappedReactor();
|
|
@@ -3420,12 +3894,9 @@ function useEditorProps(document2, node, documentDispatch, onAddOperation) {
|
|
|
3420
3894
|
const user = useUser() || void 0;
|
|
3421
3895
|
const userPermissions = useUserPermissions$1();
|
|
3422
3896
|
const context = useMemo(() => ({ theme, user }), [theme, user]);
|
|
3423
|
-
const {
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
setSelectedNode,
|
|
3427
|
-
getDocumentModelModule
|
|
3428
|
-
} = useUiNodes();
|
|
3897
|
+
const { selectedDocument } = useFileNodeDocument();
|
|
3898
|
+
const { selectedParentNode, setSelectedNode } = useUiNodesContext();
|
|
3899
|
+
const getDocumentModelModule = useGetDocumentModelModule();
|
|
3429
3900
|
const canUndo = !!document2 && (document2.revision.global > 0 || document2.revision.local > 0);
|
|
3430
3901
|
const canRedo = !!(document2 == null ? void 0 : document2.clipboard.length);
|
|
3431
3902
|
const dispatch = useEditorDispatch(node, documentDispatch, onAddOperation);
|
|
@@ -3489,6 +3960,31 @@ function useEditorProps(document2, node, documentDispatch, onAddOperation) {
|
|
|
3489
3960
|
isAllowedToEditDocuments: (userPermissions == null ? void 0 : userPermissions.isAllowedToEditDocuments) ?? false
|
|
3490
3961
|
};
|
|
3491
3962
|
}
|
|
3963
|
+
function useGetDocument() {
|
|
3964
|
+
const { openFile } = useDocumentDriveServer();
|
|
3965
|
+
const getDocument = useCallback(
|
|
3966
|
+
async (driveId, documentId, options) => {
|
|
3967
|
+
const document2 = await openFile(driveId, documentId, options);
|
|
3968
|
+
return document2;
|
|
3969
|
+
},
|
|
3970
|
+
[openFile]
|
|
3971
|
+
);
|
|
3972
|
+
return getDocument;
|
|
3973
|
+
}
|
|
3974
|
+
const useOpenSwitchboardLink = (driveId) => {
|
|
3975
|
+
const { isRemoteDrive, remoteUrl } = useDocumentDriveById(driveId);
|
|
3976
|
+
const reactor = useUnwrappedReactor();
|
|
3977
|
+
const { getDocumentGraphqlQuery, getSwitchboardGatewayUrl } = useSwitchboard(reactor);
|
|
3978
|
+
return async (uiNode) => {
|
|
3979
|
+
if ((uiNode == null ? void 0 : uiNode.kind) !== FILE$1 || !remoteUrl || !isRemoteDrive) return;
|
|
3980
|
+
const url = new URL(remoteUrl);
|
|
3981
|
+
url.origin;
|
|
3982
|
+
const switchboardUrl = getSwitchboardGatewayUrl(remoteUrl);
|
|
3983
|
+
const query = await getDocumentGraphqlQuery(driveId, uiNode.id);
|
|
3984
|
+
const encodedQuery = encodeURIComponent(query);
|
|
3985
|
+
await openUrl(`${switchboardUrl}?query=${encodedQuery}`);
|
|
3986
|
+
};
|
|
3987
|
+
};
|
|
3492
3988
|
function useSyncStatus(driveId, documentId) {
|
|
3493
3989
|
const { getSyncStatusSync, onSyncStatus, documentDrives } = useDocumentDriveServer();
|
|
3494
3990
|
const syncStatus = useSyncExternalStore(
|
|
@@ -3498,9 +3994,7 @@ function useSyncStatus(driveId, documentId) {
|
|
|
3498
3994
|
},
|
|
3499
3995
|
() => {
|
|
3500
3996
|
var _a;
|
|
3501
|
-
const drive = documentDrives.find(
|
|
3502
|
-
(_drive) => _drive.state.global.id === driveId
|
|
3503
|
-
);
|
|
3997
|
+
const drive = documentDrives.find((_drive) => _drive.id === driveId);
|
|
3504
3998
|
if (!drive) return;
|
|
3505
3999
|
const isReadDrive = "readContext" in drive;
|
|
3506
4000
|
const _sharingType = !isReadDrive ? (_a = drive.state.local.sharingType) == null ? void 0 : _a.toUpperCase() : "PUBLIC";
|
|
@@ -3523,41 +4017,366 @@ function useSyncStatus(driveId, documentId) {
|
|
|
3523
4017
|
);
|
|
3524
4018
|
return status;
|
|
3525
4019
|
}
|
|
3526
|
-
);
|
|
3527
|
-
return syncStatus;
|
|
4020
|
+
);
|
|
4021
|
+
return syncStatus;
|
|
4022
|
+
}
|
|
4023
|
+
const useUndoRedoShortcuts = (props) => {
|
|
4024
|
+
var _a;
|
|
4025
|
+
const { undo: undo2, redo: redo2, canRedo, canUndo } = props;
|
|
4026
|
+
const { isMac } = ((_a = window.electronAPI) == null ? void 0 : _a.platformInfo) || {};
|
|
4027
|
+
let undoShortcut = "ctrl+z";
|
|
4028
|
+
let redoShortcut = "ctrl+y";
|
|
4029
|
+
if (isMac) {
|
|
4030
|
+
undoShortcut = "mod+z";
|
|
4031
|
+
redoShortcut = "mod+shift+z";
|
|
4032
|
+
}
|
|
4033
|
+
useHotkeys(
|
|
4034
|
+
undoShortcut,
|
|
4035
|
+
(event) => {
|
|
4036
|
+
event.preventDefault();
|
|
4037
|
+
if (canUndo) {
|
|
4038
|
+
undo2();
|
|
4039
|
+
}
|
|
4040
|
+
},
|
|
4041
|
+
{},
|
|
4042
|
+
[canUndo, undo2]
|
|
4043
|
+
);
|
|
4044
|
+
useHotkeys(
|
|
4045
|
+
redoShortcut,
|
|
4046
|
+
(event) => {
|
|
4047
|
+
event.preventDefault();
|
|
4048
|
+
if (canRedo) {
|
|
4049
|
+
redo2();
|
|
4050
|
+
}
|
|
4051
|
+
},
|
|
4052
|
+
{},
|
|
4053
|
+
[canRedo, redo2]
|
|
4054
|
+
);
|
|
4055
|
+
};
|
|
4056
|
+
function getDocumentSpec(doc) {
|
|
4057
|
+
if ("documentModelState" in doc) {
|
|
4058
|
+
return doc.documentModelState;
|
|
4059
|
+
}
|
|
4060
|
+
return doc.documentModel;
|
|
4061
|
+
}
|
|
4062
|
+
const CreateDocument = ({ documentModels, createDocument }) => {
|
|
4063
|
+
return jsxs("div", { className: "px-6", children: [jsx("h3", { className: "mb-3 mt-4 text-xl font-bold text-gray-600", children: "New document" }), jsx("div", { className: "flex w-full flex-wrap gap-4", children: documentModels == null ? void 0 : documentModels.map((doc) => {
|
|
4064
|
+
const spec = getDocumentSpec(doc);
|
|
4065
|
+
return jsx(Button, { color: "light", "aria-details": spec.description, onClick: () => createDocument(doc), children: jsx("span", { className: "text-sm", children: spec.name }) }, spec.id);
|
|
4066
|
+
}) })] });
|
|
4067
|
+
};
|
|
4068
|
+
function sortUiNodesByName(a, b) {
|
|
4069
|
+
return a.name.localeCompare(b.name);
|
|
4070
|
+
}
|
|
4071
|
+
const GAP = 8;
|
|
4072
|
+
const ITEM_WIDTH = 256;
|
|
4073
|
+
const ITEM_HEIGHT = 48;
|
|
4074
|
+
const USED_SPACE = 420;
|
|
4075
|
+
function FileContentView(props) {
|
|
4076
|
+
const parentRef = useRef(null);
|
|
4077
|
+
const { t } = useTranslation();
|
|
4078
|
+
const windowSize = useWindowSize();
|
|
4079
|
+
const { fileNodes, ...fileProps } = props;
|
|
4080
|
+
const availableWidth = windowSize.innerWidth - USED_SPACE;
|
|
4081
|
+
const columnCount = Math.floor(availableWidth / (ITEM_WIDTH + GAP)) || 1;
|
|
4082
|
+
const rowCount = Math.ceil(fileNodes.length / columnCount);
|
|
4083
|
+
const rowVirtualizer = useVirtualizer({
|
|
4084
|
+
count: rowCount,
|
|
4085
|
+
getScrollElement: () => parentRef.current,
|
|
4086
|
+
estimateSize: (index) => {
|
|
4087
|
+
if (index > 0) {
|
|
4088
|
+
return ITEM_HEIGHT + GAP;
|
|
4089
|
+
}
|
|
4090
|
+
return ITEM_HEIGHT;
|
|
4091
|
+
},
|
|
4092
|
+
overscan: 5
|
|
4093
|
+
});
|
|
4094
|
+
const columnVirtualizer = useVirtualizer({
|
|
4095
|
+
horizontal: true,
|
|
4096
|
+
count: columnCount,
|
|
4097
|
+
getScrollElement: () => parentRef.current,
|
|
4098
|
+
estimateSize: (index) => {
|
|
4099
|
+
if (index > 0) {
|
|
4100
|
+
return ITEM_WIDTH + GAP;
|
|
4101
|
+
}
|
|
4102
|
+
return ITEM_WIDTH;
|
|
4103
|
+
},
|
|
4104
|
+
overscan: 5
|
|
4105
|
+
});
|
|
4106
|
+
const getItemIndex = (rowIndex, columnIndex) => rowIndex * columnCount + columnIndex;
|
|
4107
|
+
const getItem = (rowIndex, columnIndex) => {
|
|
4108
|
+
const index = getItemIndex(rowIndex, columnIndex);
|
|
4109
|
+
return fileNodes[index] || null;
|
|
4110
|
+
};
|
|
4111
|
+
if (fileNodes.length === 0) {
|
|
4112
|
+
return jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.documents.empty", {
|
|
4113
|
+
defaultValue: "No documents or files 📄"
|
|
4114
|
+
}) });
|
|
4115
|
+
}
|
|
4116
|
+
const renderItem = (rowIndex, columnIndex) => {
|
|
4117
|
+
const fileNode = getItem(rowIndex, columnIndex);
|
|
4118
|
+
if (!fileNode) {
|
|
4119
|
+
return null;
|
|
4120
|
+
}
|
|
4121
|
+
return jsx("div", { style: {
|
|
4122
|
+
marginLeft: columnIndex === 0 ? 0 : GAP
|
|
4123
|
+
}, children: jsx(FileItem, { uiNode: fileNode, ...fileProps }, fileNode.id) });
|
|
4124
|
+
};
|
|
4125
|
+
return jsx("div", { ref: parentRef, style: {
|
|
4126
|
+
height: `400px`,
|
|
4127
|
+
width: `100%`,
|
|
4128
|
+
overflow: "auto"
|
|
4129
|
+
}, children: jsx("div", { style: {
|
|
4130
|
+
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
4131
|
+
width: `${columnVirtualizer.getTotalSize()}px`,
|
|
4132
|
+
position: "relative"
|
|
4133
|
+
}, children: rowVirtualizer.getVirtualItems().map((virtualRow) => jsx(React__default.Fragment, { children: columnVirtualizer.getVirtualItems().map((virtualColumn) => jsx("div", { style: {
|
|
4134
|
+
position: "absolute",
|
|
4135
|
+
top: 0,
|
|
4136
|
+
left: 0,
|
|
4137
|
+
marginTop: virtualRow.index === 0 ? 0 : GAP,
|
|
4138
|
+
width: `${virtualColumn.size}px`,
|
|
4139
|
+
height: `${virtualRow.size}px`,
|
|
4140
|
+
transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`
|
|
4141
|
+
}, children: renderItem(virtualRow.index, virtualColumn.index) }, virtualColumn.key)) }, virtualRow.key)) }) });
|
|
4142
|
+
}
|
|
4143
|
+
function FolderView(props) {
|
|
4144
|
+
const { node, className, isDropTarget, containerProps, ...nodeProps } = props;
|
|
4145
|
+
const { t } = useTranslation();
|
|
4146
|
+
const folderNodes = node.children.filter((node2) => node2.kind === FOLDER).sort(sortUiNodesByName);
|
|
4147
|
+
const fileNodes = node.children.filter((node2) => node2.kind === FILE$1).sort(sortUiNodesByName);
|
|
4148
|
+
const folderCallbacks = {
|
|
4149
|
+
onSelectNode: (node2) => nodeProps.onSelectNode(node2),
|
|
4150
|
+
onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
|
|
4151
|
+
onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
|
|
4152
|
+
onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
|
|
4153
|
+
};
|
|
4154
|
+
const fileCallbacks = {
|
|
4155
|
+
onSelectNode: (node2) => nodeProps.onSelectNode(node2),
|
|
4156
|
+
onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
|
|
4157
|
+
onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
|
|
4158
|
+
onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
|
|
4159
|
+
};
|
|
4160
|
+
const baseNodeCallbacks = {
|
|
4161
|
+
onAddFile: async (file, parentNode) => {
|
|
4162
|
+
await nodeProps.onAddFile(file, parentNode);
|
|
4163
|
+
},
|
|
4164
|
+
onCopyNode: async (uiNode, targetNode) => {
|
|
4165
|
+
await nodeProps.onCopyNode(uiNode, targetNode);
|
|
4166
|
+
},
|
|
4167
|
+
onMoveNode: async (uiNode, targetNode) => {
|
|
4168
|
+
await nodeProps.onMoveNode(uiNode, targetNode);
|
|
4169
|
+
}
|
|
4170
|
+
};
|
|
4171
|
+
return jsxs("div", { className: twMerge("rounded-md border-2 border-transparent p-2", isDropTarget && "border-dashed border-blue-100", className), ...containerProps, children: [jsx(DriveLayout.ContentSection, { title: t("folderView.sections.folders.title", {
|
|
4172
|
+
defaultValue: "Folders"
|
|
4173
|
+
}), className: "mb-4", children: folderNodes.length > 0 ? folderNodes.map((folderNode) => jsx(FolderItem, { uiNode: folderNode, ...baseNodeCallbacks, ...folderCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }, folderNode.id)) : jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.folders.empty", {
|
|
4174
|
+
defaultValue: "No documents or files 📄"
|
|
4175
|
+
}) }) }), jsx(DriveLayout.ContentSection, { title: t("folderView.sections.documents.title", {
|
|
4176
|
+
defaultValue: "Documents and files"
|
|
4177
|
+
}), children: jsx("div", { className: twMerge("w-full", fileNodes.length > 0 ? "min-h-[400px]" : "min-h-14"), children: jsx(FileContentView, { fileNodes, ...fileCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }) }) })] });
|
|
4178
|
+
}
|
|
4179
|
+
function BaseEditor(props) {
|
|
4180
|
+
const { document: document2, dispatch, className, children } = props;
|
|
4181
|
+
const { id: driveId } = document2;
|
|
4182
|
+
const { showSearchBar, isAllowedToCreateDocuments, documentModels, showCreateDocumentModal } = useDriveContext();
|
|
4183
|
+
const { driveNodes, selectedNode, selectedNodePath, getNodeById, setSelectedNode } = useUiNodesContext();
|
|
4184
|
+
const driveNode = useMemo(() => driveNodes.find((n) => n.id === driveId), [driveNodes, driveId]);
|
|
4185
|
+
const { addDocument, addFile, addFolder: addFolder2, renameNode, deleteNode: deleteNode2, moveNode: moveNode2, copyNode: copyNode2, duplicateNode } = useDriveActionsWithUiNodes(document2, dispatch);
|
|
4186
|
+
const onCreateDocument = useCallback(async (documentModel) => {
|
|
4187
|
+
const { name } = await showCreateDocumentModal(documentModel);
|
|
4188
|
+
const document3 = documentModel.utils.createDocument();
|
|
4189
|
+
await addDocument(name, documentModel.documentModel.name, document3, selectedNode == null ? void 0 : selectedNode.id);
|
|
4190
|
+
}, [addDocument, showCreateDocumentModal, selectedNode == null ? void 0 : selectedNode.id]);
|
|
4191
|
+
const { isDropTarget, dropProps } = useDrop({
|
|
4192
|
+
uiNode: selectedNode,
|
|
4193
|
+
onAddFile: addFile,
|
|
4194
|
+
onCopyNode: copyNode2,
|
|
4195
|
+
onMoveNode: moveNode2
|
|
4196
|
+
});
|
|
4197
|
+
const { breadcrumbs, onBreadcrumbSelected } = useBreadcrumbs({
|
|
4198
|
+
selectedNodePath,
|
|
4199
|
+
getNodeById,
|
|
4200
|
+
setSelectedNode
|
|
4201
|
+
});
|
|
4202
|
+
if (!driveNode) {
|
|
4203
|
+
return jsx("div", { children: "Drive not found" });
|
|
4204
|
+
} else if ((selectedNode == null ? void 0 : selectedNode.kind) === FILE) {
|
|
4205
|
+
return jsx(Fragment$1, {});
|
|
4206
|
+
}
|
|
4207
|
+
return jsxs(DriveLayout, { className, children: [children, jsxs(DriveLayout.Header, { children: [jsx(Breadcrumbs, { breadcrumbs, createEnabled: isAllowedToCreateDocuments, onCreate: addFolder2, onBreadcrumbSelected }), showSearchBar && jsx(SearchBar, {})] }), jsx(DriveLayout.Content, { children: jsx(FolderView, { node: selectedNode || driveNode, onSelectNode: setSelectedNode, onRenameNode: renameNode, onDuplicateNode: duplicateNode, onDeleteNode: deleteNode2, onAddFile: addFile, onCopyNode: copyNode2, onMoveNode: moveNode2, isDropTarget, isAllowedToCreateDocuments }) }), jsx(DriveLayout.Footer, { children: isAllowedToCreateDocuments && jsx(CreateDocument, { documentModels, createDocument: onCreateDocument }) })] });
|
|
4208
|
+
}
|
|
4209
|
+
function Editor(props) {
|
|
4210
|
+
return jsx(DriveContextProvider, { value: props.context, children: jsx(BaseEditor, { ...props }) });
|
|
4211
|
+
}
|
|
4212
|
+
const GenericDriveExplorer = {
|
|
4213
|
+
Component: Editor
|
|
4214
|
+
};
|
|
4215
|
+
function useAnalyticsQueryWrapper(options) {
|
|
4216
|
+
const { queryFn, ...queryOptions } = options;
|
|
4217
|
+
const store = useAnalyticsStore();
|
|
4218
|
+
const engine = useAnalyticsEngine();
|
|
4219
|
+
return useQuery({
|
|
4220
|
+
...queryOptions,
|
|
4221
|
+
queryFn: () => {
|
|
4222
|
+
if (!store || !engine) {
|
|
4223
|
+
throw new Error("No analytics store available. Use within an AnalyticsProvider.");
|
|
4224
|
+
}
|
|
4225
|
+
return queryFn({ store, engine });
|
|
4226
|
+
}
|
|
4227
|
+
});
|
|
3528
4228
|
}
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
const {
|
|
3532
|
-
const
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
}
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
4229
|
+
function useAnalyticsQuery(query, options) {
|
|
4230
|
+
const store = useAnalyticsStore();
|
|
4231
|
+
const { data: querySources } = useQuerySources(query);
|
|
4232
|
+
const queryClient = useQueryClient();
|
|
4233
|
+
const subscriptions = useRef([]);
|
|
4234
|
+
const result = useAnalyticsQueryWrapper({
|
|
4235
|
+
queryKey: ["analytics", "query", query],
|
|
4236
|
+
queryFn: ({ engine }) => engine.execute(query),
|
|
4237
|
+
...options
|
|
4238
|
+
});
|
|
4239
|
+
useEffect(() => {
|
|
4240
|
+
if (!(querySources == null ? void 0 : querySources.length) || !store) {
|
|
4241
|
+
return;
|
|
4242
|
+
}
|
|
4243
|
+
querySources.forEach((source) => {
|
|
4244
|
+
const unsub = store.subscribeToSource(source, () => {
|
|
4245
|
+
return queryClient.invalidateQueries({
|
|
4246
|
+
queryKey: ["analytics", "query", query]
|
|
4247
|
+
});
|
|
4248
|
+
});
|
|
4249
|
+
subscriptions.current.push(unsub);
|
|
4250
|
+
});
|
|
4251
|
+
return () => {
|
|
4252
|
+
subscriptions.current.forEach((unsub) => unsub());
|
|
4253
|
+
subscriptions.current = [];
|
|
4254
|
+
};
|
|
4255
|
+
}, [querySources]);
|
|
4256
|
+
return result;
|
|
4257
|
+
}
|
|
4258
|
+
function useMatchingSeries(query, options) {
|
|
4259
|
+
const result = useAnalyticsQueryWrapper({
|
|
4260
|
+
queryKey: ["analytics", "matchingSeries", query],
|
|
4261
|
+
queryFn: ({ store }) => store.getMatchingSeries(query),
|
|
4262
|
+
...options
|
|
4263
|
+
});
|
|
4264
|
+
return result;
|
|
4265
|
+
}
|
|
4266
|
+
function useQuerySources(query, options) {
|
|
4267
|
+
const { data: matchingSeries } = useMatchingSeries(query);
|
|
4268
|
+
return useQuery({
|
|
4269
|
+
queryKey: ["analytics", "sources", query],
|
|
4270
|
+
queryFn: () => {
|
|
4271
|
+
if (!(matchingSeries == null ? void 0 : matchingSeries.length)) {
|
|
4272
|
+
return [];
|
|
3545
4273
|
}
|
|
4274
|
+
const uniqueSources = [
|
|
4275
|
+
...new Set(matchingSeries.map((s) => s.source.toString()))
|
|
4276
|
+
];
|
|
4277
|
+
return uniqueSources.map((source) => AnalyticsPath.fromString(source));
|
|
3546
4278
|
},
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
);
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
4279
|
+
enabled: !!matchingSeries,
|
|
4280
|
+
...options
|
|
4281
|
+
});
|
|
4282
|
+
}
|
|
4283
|
+
const getBarSize = (value) => {
|
|
4284
|
+
if (value <= 0)
|
|
4285
|
+
return 0;
|
|
4286
|
+
if (value > 0 && value <= 50)
|
|
4287
|
+
return 1;
|
|
4288
|
+
if (value > 50 && value <= 100)
|
|
4289
|
+
return 2;
|
|
4290
|
+
if (value > 100 && value <= 250)
|
|
4291
|
+
return 3;
|
|
4292
|
+
return 4;
|
|
4293
|
+
};
|
|
4294
|
+
const useTimelineItems = (documentId, startTimestamp) => {
|
|
4295
|
+
const start = startTimestamp ? DateTime.fromISO(startTimestamp) : DateTime.now().startOf("day");
|
|
4296
|
+
const { data: diffResult, isLoading } = useAnalyticsQuery({
|
|
4297
|
+
start,
|
|
4298
|
+
end: DateTime.now().endOf("day"),
|
|
4299
|
+
granularity: AnalyticsGranularity.Hourly,
|
|
4300
|
+
metrics: ["Count"],
|
|
4301
|
+
select: {
|
|
4302
|
+
changes: [AnalyticsPath.fromString(`changes`)],
|
|
4303
|
+
document: [AnalyticsPath.fromString(`document/${documentId}`)]
|
|
3557
4304
|
},
|
|
3558
|
-
{
|
|
3559
|
-
|
|
3560
|
-
|
|
4305
|
+
lod: {
|
|
4306
|
+
changes: 2
|
|
4307
|
+
},
|
|
4308
|
+
currency: AnalyticsPath.fromString("")
|
|
4309
|
+
});
|
|
4310
|
+
const mappedResult = useMemo(() => {
|
|
4311
|
+
if (!diffResult)
|
|
4312
|
+
return [];
|
|
4313
|
+
return diffResult.sort((a, b) => {
|
|
4314
|
+
const aDate = new Date(a.start);
|
|
4315
|
+
const bDate = new Date(b.start);
|
|
4316
|
+
return aDate.getTime() - bDate.getTime();
|
|
4317
|
+
}).filter((result) => {
|
|
4318
|
+
return result.rows.every((row) => row.value > 0);
|
|
4319
|
+
}).map((result) => {
|
|
4320
|
+
const { additions, deletions } = result.rows.reduce((acc, row) => {
|
|
4321
|
+
if (row.dimensions.changes.path === "changes/add") {
|
|
4322
|
+
acc.additions += row.value;
|
|
4323
|
+
} else if (row.dimensions.changes.path === "changes/remove") {
|
|
4324
|
+
acc.deletions += row.value;
|
|
4325
|
+
}
|
|
4326
|
+
return acc;
|
|
4327
|
+
}, { additions: 0, deletions: 0 });
|
|
4328
|
+
const startDate = new Date(result.start);
|
|
4329
|
+
return {
|
|
4330
|
+
id: startDate.toISOString(),
|
|
4331
|
+
type: "bar",
|
|
4332
|
+
addSize: getBarSize(additions),
|
|
4333
|
+
delSize: getBarSize(deletions),
|
|
4334
|
+
additions,
|
|
4335
|
+
deletions,
|
|
4336
|
+
timestamp: startDate.toISOString(),
|
|
4337
|
+
startDate,
|
|
4338
|
+
endDate: new Date(result.end),
|
|
4339
|
+
revision: 0
|
|
4340
|
+
};
|
|
4341
|
+
});
|
|
4342
|
+
}, [diffResult]);
|
|
4343
|
+
const resultWithDividers = useMemo(() => {
|
|
4344
|
+
if (!mappedResult.length)
|
|
4345
|
+
return [];
|
|
4346
|
+
const result = [];
|
|
4347
|
+
mappedResult.forEach((item, index) => {
|
|
4348
|
+
result.push(item);
|
|
4349
|
+
if (index < mappedResult.length - 1) {
|
|
4350
|
+
const currentDate = new Date(item.startDate);
|
|
4351
|
+
const nextDate = new Date(mappedResult[index + 1].startDate);
|
|
4352
|
+
const currentHour = currentDate.getHours();
|
|
4353
|
+
const nextHour = nextDate.getHours();
|
|
4354
|
+
const currentDay = currentDate.toDateString();
|
|
4355
|
+
const nextDay = nextDate.toDateString();
|
|
4356
|
+
if (currentDay !== nextDay || currentDay === nextDay && Math.abs(nextHour - currentHour) > 1) {
|
|
4357
|
+
result.push({
|
|
4358
|
+
id: `divider-${item.id}-${mappedResult[index + 1].id}`,
|
|
4359
|
+
type: "divider",
|
|
4360
|
+
revision: 0
|
|
4361
|
+
});
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
});
|
|
4365
|
+
return result;
|
|
4366
|
+
}, [mappedResult]);
|
|
4367
|
+
return {
|
|
4368
|
+
isLoading,
|
|
4369
|
+
data: resultWithDividers
|
|
4370
|
+
};
|
|
4371
|
+
};
|
|
4372
|
+
const getRevisionFromDate = (startDate, endDate, operations = []) => {
|
|
4373
|
+
if (!startDate || !endDate)
|
|
4374
|
+
return 0;
|
|
4375
|
+
const operation = operations.find((operation2) => {
|
|
4376
|
+
const operationDate = new Date(operation2.timestamp);
|
|
4377
|
+
return operationDate >= startDate && operationDate <= endDate;
|
|
4378
|
+
});
|
|
4379
|
+
return operation ? operation.index : 0;
|
|
3561
4380
|
};
|
|
3562
4381
|
function EditorLoader(props) {
|
|
3563
4382
|
const [showLoading, setShowLoading] = useState(false);
|
|
@@ -3586,9 +4405,11 @@ const DocumentEditor = (props) => {
|
|
|
3586
4405
|
onChange,
|
|
3587
4406
|
onExport,
|
|
3588
4407
|
onAddOperation,
|
|
4408
|
+
onGetDocumentRevision,
|
|
3589
4409
|
onOpenSwitchboardLink
|
|
3590
4410
|
} = props;
|
|
3591
4411
|
const documentId = fileNodeDocument == null ? void 0 : fileNodeDocument.documentId;
|
|
4412
|
+
const [selectedTimelineItem, setSelectedTimelineItem] = useState(null);
|
|
3592
4413
|
const [revisionHistoryVisible, setRevisionHistoryVisible] = useState(false);
|
|
3593
4414
|
const theme = useAtomValue(themeAtom);
|
|
3594
4415
|
const user = useUser() || void 0;
|
|
@@ -3614,6 +4435,10 @@ const DocumentEditor = (props) => {
|
|
|
3614
4435
|
[theme, user]
|
|
3615
4436
|
);
|
|
3616
4437
|
const userPermissions = useUserPermissions$1();
|
|
4438
|
+
const timelineItems = useTimelineItems(
|
|
4439
|
+
documentId,
|
|
4440
|
+
initialDocument == null ? void 0 : initialDocument.created
|
|
4441
|
+
);
|
|
3617
4442
|
const currentDocument = useRef({ ...fileNodeDocument, document: document2 });
|
|
3618
4443
|
useEffect(() => {
|
|
3619
4444
|
var _a;
|
|
@@ -3790,7 +4615,8 @@ const DocumentEditor = (props) => {
|
|
|
3790
4615
|
const {
|
|
3791
4616
|
disableExternalControls,
|
|
3792
4617
|
documentToolbarEnabled,
|
|
3793
|
-
showSwitchboardLink
|
|
4618
|
+
showSwitchboardLink,
|
|
4619
|
+
timelineEnabled
|
|
3794
4620
|
} = editor.config || {};
|
|
3795
4621
|
const handleSwitchboardLinkClick = showSwitchboardLink !== false ? onOpenSwitchboardLink : void 0;
|
|
3796
4622
|
return /* @__PURE__ */ jsxs("div", { className: "relative h-full", id: "document-editor-context", children: [
|
|
@@ -3801,7 +4627,10 @@ const DocumentEditor = (props) => {
|
|
|
3801
4627
|
onExport,
|
|
3802
4628
|
onShowRevisionHistory: showRevisionHistory,
|
|
3803
4629
|
title: fileNodeDocument.name || document2.name,
|
|
3804
|
-
onSwitchboardLinkClick: handleSwitchboardLinkClick
|
|
4630
|
+
onSwitchboardLinkClick: handleSwitchboardLinkClick,
|
|
4631
|
+
timelineButtonVisible: timelineEnabled,
|
|
4632
|
+
timelineItems: timelineItems.data,
|
|
4633
|
+
onTimelineItemClick: setSelectedTimelineItem
|
|
3805
4634
|
}
|
|
3806
4635
|
),
|
|
3807
4636
|
!disableExternalControls && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex justify-end gap-10", children: [
|
|
@@ -3831,7 +4660,16 @@ const DocumentEditor = (props) => {
|
|
|
3831
4660
|
EditorComponent,
|
|
3832
4661
|
{
|
|
3833
4662
|
error,
|
|
3834
|
-
context
|
|
4663
|
+
context: {
|
|
4664
|
+
...context,
|
|
4665
|
+
getDocumentRevision: onGetDocumentRevision,
|
|
4666
|
+
readMode: !!selectedTimelineItem,
|
|
4667
|
+
selectedTimelineRevision: getRevisionFromDate(
|
|
4668
|
+
selectedTimelineItem == null ? void 0 : selectedTimelineItem.startDate,
|
|
4669
|
+
selectedTimelineItem == null ? void 0 : selectedTimelineItem.endDate,
|
|
4670
|
+
document2.operations.global
|
|
4671
|
+
)
|
|
4672
|
+
},
|
|
3835
4673
|
document: document2,
|
|
3836
4674
|
documentNodeName: fileNodeDocument.name,
|
|
3837
4675
|
dispatch,
|
|
@@ -3857,18 +4695,22 @@ function DocumentEditorContainer() {
|
|
|
3857
4695
|
const { t } = useTranslation();
|
|
3858
4696
|
const { showModal } = useModal();
|
|
3859
4697
|
const {
|
|
3860
|
-
selectedNode,
|
|
3861
|
-
selectedParentNode,
|
|
3862
|
-
isRemoteDrive,
|
|
3863
4698
|
selectedDocument,
|
|
3864
4699
|
fileNodeDocument,
|
|
3865
|
-
setSelectedNode,
|
|
3866
4700
|
setSelectedDocument,
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
4701
|
+
addOperationToSelectedDocument
|
|
4702
|
+
} = useFileNodeDocument();
|
|
4703
|
+
const { renameNode } = useDocumentDriveServer();
|
|
4704
|
+
const {
|
|
4705
|
+
selectedNode,
|
|
4706
|
+
selectedDriveNode,
|
|
4707
|
+
selectedParentNode,
|
|
4708
|
+
setSelectedNode
|
|
4709
|
+
} = useUiNodesContext();
|
|
4710
|
+
const { isRemoteDrive } = useDocumentDriveById(selectedDriveNode == null ? void 0 : selectedDriveNode.id);
|
|
4711
|
+
const openSwitchboardLink = useOpenSwitchboardLink(selectedDriveNode == null ? void 0 : selectedDriveNode.id);
|
|
4712
|
+
const getDocumentModelModule = useGetDocumentModelModule();
|
|
4713
|
+
const getDocument = useGetDocument();
|
|
3872
4714
|
const handleAddOperationToSelectedDocument = useCallback(
|
|
3873
4715
|
async (operation) => {
|
|
3874
4716
|
if (!selectedDocument) {
|
|
@@ -3931,6 +4773,20 @@ function DocumentEditorContainer() {
|
|
|
3931
4773
|
},
|
|
3932
4774
|
[getDocumentModelModule, showModal, t]
|
|
3933
4775
|
);
|
|
4776
|
+
const onGetDocumentRevision = useCallback(
|
|
4777
|
+
(options) => {
|
|
4778
|
+
if (!selectedNode) {
|
|
4779
|
+
console.error("No selected node");
|
|
4780
|
+
return Promise.reject(new Error("No selected node"));
|
|
4781
|
+
}
|
|
4782
|
+
return getDocument(
|
|
4783
|
+
selectedNode.driveId,
|
|
4784
|
+
selectedNode.id,
|
|
4785
|
+
options
|
|
4786
|
+
);
|
|
4787
|
+
},
|
|
4788
|
+
[getDocument, selectedNode]
|
|
4789
|
+
);
|
|
3934
4790
|
const onExport = useCallback(() => {
|
|
3935
4791
|
if (selectedDocument) {
|
|
3936
4792
|
return exportDocument(selectedDocument);
|
|
@@ -3952,6 +4808,7 @@ function DocumentEditorContainer() {
|
|
|
3952
4808
|
onChange: onDocumentChangeHandler,
|
|
3953
4809
|
onClose,
|
|
3954
4810
|
onExport,
|
|
4811
|
+
onGetDocumentRevision,
|
|
3955
4812
|
onAddOperation: handleAddOperationToSelectedDocument,
|
|
3956
4813
|
onOpenSwitchboardLink
|
|
3957
4814
|
}
|
|
@@ -3960,165 +4817,6 @@ function DocumentEditorContainer() {
|
|
|
3960
4817
|
fileNodeDocument.documentId
|
|
3961
4818
|
);
|
|
3962
4819
|
}
|
|
3963
|
-
function getDocumentSpec(doc) {
|
|
3964
|
-
if ("documentModelState" in doc) {
|
|
3965
|
-
return doc.documentModelState;
|
|
3966
|
-
}
|
|
3967
|
-
return doc.documentModel;
|
|
3968
|
-
}
|
|
3969
|
-
const CreateDocument = ({ documentModels, createDocument }) => {
|
|
3970
|
-
return jsxs("div", { className: "px-6", children: [jsx("h3", { className: "mb-3 mt-4 text-xl font-bold text-gray-600", children: "New document" }), jsx("div", { className: "flex w-full flex-wrap gap-4", children: documentModels == null ? void 0 : documentModels.map((doc) => {
|
|
3971
|
-
const spec = getDocumentSpec(doc);
|
|
3972
|
-
return jsx(Button, { color: "light", "aria-details": spec.description, onClick: () => createDocument(doc), children: jsx("span", { className: "text-sm", children: spec.name }) }, spec.id);
|
|
3973
|
-
}) })] });
|
|
3974
|
-
};
|
|
3975
|
-
function sortUiNodesByName(a, b) {
|
|
3976
|
-
return a.name.localeCompare(b.name);
|
|
3977
|
-
}
|
|
3978
|
-
const GAP = 8;
|
|
3979
|
-
const ITEM_WIDTH = 256;
|
|
3980
|
-
const ITEM_HEIGHT = 48;
|
|
3981
|
-
const USED_SPACE = 420;
|
|
3982
|
-
function FileContentView(props) {
|
|
3983
|
-
const parentRef = useRef(null);
|
|
3984
|
-
const { t } = useTranslation();
|
|
3985
|
-
const windowSize = useWindowSize();
|
|
3986
|
-
const { fileNodes, ...fileProps } = props;
|
|
3987
|
-
const availableWidth = windowSize.innerWidth - USED_SPACE;
|
|
3988
|
-
const columnCount = Math.floor(availableWidth / (ITEM_WIDTH + GAP)) || 1;
|
|
3989
|
-
const rowCount = Math.ceil(fileNodes.length / columnCount);
|
|
3990
|
-
const rowVirtualizer = useVirtualizer({
|
|
3991
|
-
count: rowCount,
|
|
3992
|
-
getScrollElement: () => parentRef.current,
|
|
3993
|
-
estimateSize: (index) => {
|
|
3994
|
-
if (index > 0) {
|
|
3995
|
-
return ITEM_HEIGHT + GAP;
|
|
3996
|
-
}
|
|
3997
|
-
return ITEM_HEIGHT;
|
|
3998
|
-
},
|
|
3999
|
-
overscan: 5
|
|
4000
|
-
});
|
|
4001
|
-
const columnVirtualizer = useVirtualizer({
|
|
4002
|
-
horizontal: true,
|
|
4003
|
-
count: columnCount,
|
|
4004
|
-
getScrollElement: () => parentRef.current,
|
|
4005
|
-
estimateSize: (index) => {
|
|
4006
|
-
if (index > 0) {
|
|
4007
|
-
return ITEM_WIDTH + GAP;
|
|
4008
|
-
}
|
|
4009
|
-
return ITEM_WIDTH;
|
|
4010
|
-
},
|
|
4011
|
-
overscan: 5
|
|
4012
|
-
});
|
|
4013
|
-
const getItemIndex = (rowIndex, columnIndex) => rowIndex * columnCount + columnIndex;
|
|
4014
|
-
const getItem = (rowIndex, columnIndex) => {
|
|
4015
|
-
const index = getItemIndex(rowIndex, columnIndex);
|
|
4016
|
-
return fileNodes[index] || null;
|
|
4017
|
-
};
|
|
4018
|
-
if (fileNodes.length === 0) {
|
|
4019
|
-
return jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.documents.empty", {
|
|
4020
|
-
defaultValue: "No documents or files 📄"
|
|
4021
|
-
}) });
|
|
4022
|
-
}
|
|
4023
|
-
const renderItem = (rowIndex, columnIndex) => {
|
|
4024
|
-
const fileNode = getItem(rowIndex, columnIndex);
|
|
4025
|
-
if (!fileNode) {
|
|
4026
|
-
return null;
|
|
4027
|
-
}
|
|
4028
|
-
return jsx("div", { style: {
|
|
4029
|
-
marginLeft: columnIndex === 0 ? 0 : GAP
|
|
4030
|
-
}, children: jsx(FileItem, { uiNode: fileNode, ...fileProps }, fileNode.id) });
|
|
4031
|
-
};
|
|
4032
|
-
return jsx("div", { ref: parentRef, style: {
|
|
4033
|
-
height: `400px`,
|
|
4034
|
-
width: `100%`,
|
|
4035
|
-
overflow: "auto"
|
|
4036
|
-
}, children: jsx("div", { style: {
|
|
4037
|
-
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
4038
|
-
width: `${columnVirtualizer.getTotalSize()}px`,
|
|
4039
|
-
position: "relative"
|
|
4040
|
-
}, children: rowVirtualizer.getVirtualItems().map((virtualRow) => jsx(React__default.Fragment, { children: columnVirtualizer.getVirtualItems().map((virtualColumn) => jsx("div", { style: {
|
|
4041
|
-
position: "absolute",
|
|
4042
|
-
top: 0,
|
|
4043
|
-
left: 0,
|
|
4044
|
-
marginTop: virtualRow.index === 0 ? 0 : GAP,
|
|
4045
|
-
width: `${virtualColumn.size}px`,
|
|
4046
|
-
height: `${virtualRow.size}px`,
|
|
4047
|
-
transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`
|
|
4048
|
-
}, children: renderItem(virtualRow.index, virtualColumn.index) }, virtualColumn.key)) }, virtualRow.key)) }) });
|
|
4049
|
-
}
|
|
4050
|
-
function FolderView(props) {
|
|
4051
|
-
const { node, className, isDropTarget, containerProps, ...nodeProps } = props;
|
|
4052
|
-
const { t } = useTranslation();
|
|
4053
|
-
const folderNodes = node.children.filter((node2) => node2.kind === FOLDER).sort(sortUiNodesByName);
|
|
4054
|
-
const fileNodes = node.children.filter((node2) => node2.kind === FILE).sort(sortUiNodesByName);
|
|
4055
|
-
const folderCallbacks = {
|
|
4056
|
-
onSelectNode: (node2) => nodeProps.onSelectNode(node2),
|
|
4057
|
-
onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
|
|
4058
|
-
onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
|
|
4059
|
-
onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
|
|
4060
|
-
};
|
|
4061
|
-
const fileCallbacks = {
|
|
4062
|
-
onSelectNode: (node2) => nodeProps.onSelectNode(node2),
|
|
4063
|
-
onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
|
|
4064
|
-
onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
|
|
4065
|
-
onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
|
|
4066
|
-
};
|
|
4067
|
-
const baseNodeCallbacks = {
|
|
4068
|
-
onAddFile: async (file, parentNode) => {
|
|
4069
|
-
await nodeProps.onAddFile(file, parentNode);
|
|
4070
|
-
},
|
|
4071
|
-
onCopyNode: async (uiNode, targetNode) => {
|
|
4072
|
-
await nodeProps.onCopyNode(uiNode, targetNode);
|
|
4073
|
-
},
|
|
4074
|
-
onMoveNode: async (uiNode, targetNode) => {
|
|
4075
|
-
await nodeProps.onMoveNode(uiNode, targetNode);
|
|
4076
|
-
}
|
|
4077
|
-
};
|
|
4078
|
-
return jsxs("div", { className: twMerge("rounded-md border-2 border-transparent p-2", isDropTarget && "border-dashed border-blue-100", className), ...containerProps, children: [jsx(DriveLayout.ContentSection, { title: t("folderView.sections.folders.title", {
|
|
4079
|
-
defaultValue: "Folders"
|
|
4080
|
-
}), className: "mb-4", children: folderNodes.length > 0 ? folderNodes.map((folderNode) => jsx(FolderItem, { uiNode: folderNode, ...baseNodeCallbacks, ...folderCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }, folderNode.id)) : jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.folders.empty", {
|
|
4081
|
-
defaultValue: "No documents or files 📄"
|
|
4082
|
-
}) }) }), jsx(DriveLayout.ContentSection, { title: t("folderView.sections.documents.title", {
|
|
4083
|
-
defaultValue: "Documents and files"
|
|
4084
|
-
}), children: jsx("div", { className: twMerge("w-full", fileNodes.length > 0 ? "min-h-[400px]" : "min-h-14"), children: jsx(FileContentView, { fileNodes, ...fileCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }) }) })] });
|
|
4085
|
-
}
|
|
4086
|
-
function BaseEditor(props) {
|
|
4087
|
-
const { document: document2, dispatch, className, children } = props;
|
|
4088
|
-
const { state: { global: { id: driveId } } } = document2;
|
|
4089
|
-
const { showSearchBar, isAllowedToCreateDocuments, documentModels, showCreateDocumentModal } = useDriveContext();
|
|
4090
|
-
const { driveNodes, selectedNode, selectedNodePath, getNodeById, setSelectedNode } = useUiNodesContext();
|
|
4091
|
-
const driveNode = useMemo(() => driveNodes.find((n) => n.id === driveId), [driveNodes, driveId]);
|
|
4092
|
-
const { addDocument, addFile, addFolder: addFolder2, renameNode, deleteNode: deleteNode2, moveNode: moveNode2, copyNode: copyNode2, duplicateNode } = useDriveActionsWithUiNodes(document2, dispatch);
|
|
4093
|
-
const onCreateDocument = useCallback(async (documentModel) => {
|
|
4094
|
-
const { name } = await showCreateDocumentModal(documentModel);
|
|
4095
|
-
const document3 = documentModel.utils.createDocument();
|
|
4096
|
-
await addDocument(name, documentModel.documentModel.name, document3, selectedNode == null ? void 0 : selectedNode.id);
|
|
4097
|
-
}, [addDocument, showCreateDocumentModal, selectedNode == null ? void 0 : selectedNode.id]);
|
|
4098
|
-
const { isDropTarget, dropProps } = useDrop({
|
|
4099
|
-
uiNode: selectedNode,
|
|
4100
|
-
onAddFile: addFile,
|
|
4101
|
-
onCopyNode: copyNode2,
|
|
4102
|
-
onMoveNode: moveNode2
|
|
4103
|
-
});
|
|
4104
|
-
const { breadcrumbs, onBreadcrumbSelected } = useBreadcrumbs({
|
|
4105
|
-
selectedNodePath,
|
|
4106
|
-
getNodeById,
|
|
4107
|
-
setSelectedNode
|
|
4108
|
-
});
|
|
4109
|
-
if (!driveNode) {
|
|
4110
|
-
return jsx("div", { children: "Drive not found" });
|
|
4111
|
-
} else if ((selectedNode == null ? void 0 : selectedNode.kind) === FILE$1) {
|
|
4112
|
-
return jsx(Fragment$1, {});
|
|
4113
|
-
}
|
|
4114
|
-
return jsxs(DriveLayout, { className, children: [children, jsxs(DriveLayout.Header, { children: [jsx(Breadcrumbs, { breadcrumbs, createEnabled: isAllowedToCreateDocuments, onCreate: addFolder2, onBreadcrumbSelected }), showSearchBar && jsx(SearchBar, {})] }), jsx(DriveLayout.Content, { children: jsx(FolderView, { node: selectedNode || driveNode, onSelectNode: setSelectedNode, onRenameNode: renameNode, onDuplicateNode: duplicateNode, onDeleteNode: deleteNode2, onAddFile: addFile, onCopyNode: copyNode2, onMoveNode: moveNode2, isDropTarget, isAllowedToCreateDocuments }) }), jsx(DriveLayout.Footer, { children: isAllowedToCreateDocuments && jsx(CreateDocument, { documentModels, createDocument: onCreateDocument }) })] });
|
|
4115
|
-
}
|
|
4116
|
-
function Editor(props) {
|
|
4117
|
-
return jsx(DriveContextProvider, { value: props.context, children: jsx(BaseEditor, { ...props }) });
|
|
4118
|
-
}
|
|
4119
|
-
const GenericDriveExplorer = {
|
|
4120
|
-
Component: Editor
|
|
4121
|
-
};
|
|
4122
4820
|
function useDocumentsState(args) {
|
|
4123
4821
|
const { reactor, driveId, documentIds, options } = args;
|
|
4124
4822
|
const [statesByDocumentId, setStatesByDocumentId] = useState({});
|
|
@@ -4268,10 +4966,10 @@ function DriveEditorContainer() {
|
|
|
4268
4966
|
selectedNode,
|
|
4269
4967
|
selectedParentNode
|
|
4270
4968
|
} = useUiNodesContext();
|
|
4271
|
-
const { addOperationToSelectedDrive } =
|
|
4969
|
+
const { addOperationToSelectedDrive } = useFileNodeDocument();
|
|
4272
4970
|
const documentDrive = useSelectedDocumentDrive();
|
|
4273
4971
|
const [document2, _dispatch, error] = useDocumentDispatch$1(
|
|
4274
|
-
|
|
4972
|
+
module.reducer,
|
|
4275
4973
|
documentDrive
|
|
4276
4974
|
);
|
|
4277
4975
|
const reactor = useAsyncReactor();
|
|
@@ -4305,8 +5003,19 @@ function DriveEditorContainer() {
|
|
|
4305
5003
|
const { addFile, addDocument } = useDocumentDriveServer();
|
|
4306
5004
|
const documentModels = useFilteredDocumentModels();
|
|
4307
5005
|
const useDriveDocumentState = makeDriveDocumentStateHook(reactor);
|
|
5006
|
+
const getDocument = useGetDocument();
|
|
4308
5007
|
const getDocumentModelModule = useGetDocumentModelModule();
|
|
4309
5008
|
const getEditor = useGetEditor();
|
|
5009
|
+
const onGetDocumentRevision = useCallback(
|
|
5010
|
+
(documentId, options) => {
|
|
5011
|
+
if (!selectedNode) {
|
|
5012
|
+
console.error("No selected node");
|
|
5013
|
+
return Promise.reject(new Error("No selected node"));
|
|
5014
|
+
}
|
|
5015
|
+
return getDocument(selectedNode.driveId, documentId, options);
|
|
5016
|
+
},
|
|
5017
|
+
[getDocument, selectedNode]
|
|
5018
|
+
);
|
|
4310
5019
|
const driveContext = useMemo(
|
|
4311
5020
|
() => ({
|
|
4312
5021
|
showSearchBar: false,
|
|
@@ -4350,6 +5059,8 @@ function DriveEditorContainer() {
|
|
|
4350
5059
|
context: {
|
|
4351
5060
|
...editorProps.context,
|
|
4352
5061
|
...driveContext,
|
|
5062
|
+
analyticsDatabaseName: connectConfig.analyticsDatabaseName,
|
|
5063
|
+
getDocumentRevision: onGetDocumentRevision,
|
|
4353
5064
|
getDocumentModelModule,
|
|
4354
5065
|
getEditor
|
|
4355
5066
|
},
|
|
@@ -4367,12 +5078,13 @@ function Content() {
|
|
|
4367
5078
|
const navigate = useNavigate();
|
|
4368
5079
|
const { driveId } = useParams();
|
|
4369
5080
|
const [documentDrives, , , status] = useDocumentDrives$1();
|
|
4370
|
-
const
|
|
4371
|
-
const {
|
|
5081
|
+
const { selectedDriveNode, selectedNode } = useUiNodesContext();
|
|
5082
|
+
const { addFile } = useDocumentDriveServer();
|
|
5083
|
+
const { fileNodeDocument } = useFileNodeDocument();
|
|
4372
5084
|
useEffect(() => {
|
|
4373
5085
|
var _a;
|
|
4374
5086
|
return (_a = window.electronAPI) == null ? void 0 : _a.handleFileOpen(async (file) => {
|
|
4375
|
-
if (!selectedDriveNode || (selectedNode == null ? void 0 : selectedNode.kind) !== FILE) {
|
|
5087
|
+
if (!selectedDriveNode || (selectedNode == null ? void 0 : selectedNode.kind) !== FILE$1) {
|
|
4376
5088
|
return;
|
|
4377
5089
|
}
|
|
4378
5090
|
await addFile(
|
|
@@ -4385,7 +5097,7 @@ function Content() {
|
|
|
4385
5097
|
}, [selectedDriveNode, selectedNode, addFile]);
|
|
4386
5098
|
useEffect(() => {
|
|
4387
5099
|
if ((status === "LOADED" || status === "ERROR") && !documentDrives.find(
|
|
4388
|
-
(d) => d.
|
|
5100
|
+
(d) => d.id === driveId || d.slug === driveId || d.state.global.name === driveId
|
|
4389
5101
|
)) {
|
|
4390
5102
|
toast(/* @__PURE__ */ jsxs("p", { children: [
|
|
4391
5103
|
"Drive ",
|
|
@@ -4400,3 +5112,4 @@ function Content() {
|
|
|
4400
5112
|
export {
|
|
4401
5113
|
Content as default
|
|
4402
5114
|
};
|
|
5115
|
+
//# sourceMappingURL=content-ChtzAUHq.js.map
|