@geode/opengeodeweb-front 10.34.0-rc.8 → 10.34.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/{server → .build/server}/utils/app_config.js +10 -9
- package/{server → .build/server}/utils/cleanup.js +31 -17
- package/{server → .build/server}/utils/cloud.js +1 -4
- package/{server → .build/server}/utils/microservices.js +21 -9
- package/{server → .build/server}/utils/path.js +4 -4
- package/{server → .build/server}/utils/scripts.js +9 -9
- package/.build/server/utils/server_config.js +51 -0
- package/.build/server/utils/stream.js +6 -0
- package/{server → .build/server}/utils/ws_client.js +18 -3
- package/{shared → .build/shared}/scripts.js +10 -10
- package/.build/shared/utils/call_raw.js +31 -0
- package/{shared → .build/shared}/utils/call_schema.js +2 -2
- package/.build/shared/utils/fetch_raw.js +52 -0
- package/{shared → .build/shared}/utils/fetch_schema.js +2 -2
- package/{shared → .build/shared}/utils/file.js +3 -1
- package/{shared → .build/shared}/utils/response_handlers/load.js +10 -7
- package/{shared → .build/shared}/utils/validate_schema.js +1 -1
- package/app/components/ActionButton.vue +6 -6
- package/app/components/CameraManager/List.vue +5 -5
- package/app/components/CameraManager/Saver.vue +3 -5
- package/app/components/CameraManager.vue +6 -4
- package/app/components/CameraOrientation.vue +12 -12
- package/app/components/ClippingPlaneCard.vue +4 -2
- package/app/components/ClippingPlanes.vue +8 -8
- package/app/components/CrsSelector.vue +9 -10
- package/app/components/DeleteDialog.vue +4 -2
- package/app/components/DragAndDrop.vue +10 -12
- package/app/components/DragAndDropInternal/DragAndDropInline.vue +4 -2
- package/app/components/ExtensionSelector.vue +4 -4
- package/app/components/FeedBack/ErrorBanner.vue +3 -1
- package/app/components/FeedBack/Snackers.vue +4 -3
- package/app/components/FileSelector.vue +10 -8
- package/app/components/FileUploader.vue +11 -11
- package/app/components/GlassCard.vue +1 -1
- package/app/components/HybridRenderingView.vue +7 -5
- package/app/components/HybridViewerTooltip.vue +7 -7
- package/app/components/Launcher.vue +1 -1
- package/app/components/Loading/EcoMessages.vue +9 -3
- package/app/components/Loading.vue +2 -2
- package/app/components/MissingFilesSelector.vue +12 -10
- package/app/components/ObjectSelector.vue +10 -9
- package/app/components/PackagesVersions.vue +6 -5
- package/app/components/Recaptcha.vue +3 -3
- package/app/components/RemoteRenderingView.vue +6 -7
- package/app/components/Ruler.vue +3 -3
- package/app/components/Screenshot.vue +6 -6
- package/app/components/SearchBar.vue +4 -2
- package/app/components/ShrinkFilter.vue +7 -7
- package/app/components/Step.vue +6 -5
- package/app/components/Stepper.vue +4 -3
- package/app/components/ToolPanel.vue +6 -3
- package/app/components/ViewToolbar.vue +25 -28
- package/app/components/Viewer/ContextMenu/CenterButton.vue +7 -5
- package/app/components/Viewer/ContextMenu/CircularItems.vue +3 -3
- package/app/components/Viewer/ContextMenu/ContextMenu.vue +22 -19
- package/app/components/Viewer/ContextMenu/ContextMenuItem.vue +10 -8
- package/app/components/Viewer/ContextMenu/InfoCard.vue +11 -12
- package/app/components/Viewer/EdgedCurve/SpecificEdgesOptions.vue +13 -17
- package/app/components/Viewer/Generic/Mesh/CellsOptions.vue +11 -15
- package/app/components/Viewer/Generic/Mesh/EdgesOptions.vue +12 -16
- package/app/components/Viewer/Generic/Mesh/PointsOptions.vue +8 -12
- package/app/components/Viewer/Generic/Mesh/PolygonsOptions.vue +11 -15
- package/app/components/Viewer/Generic/Mesh/PolyhedraOptions.vue +11 -15
- package/app/components/Viewer/Generic/Model/BlocksOptions.vue +67 -38
- package/app/components/Viewer/Generic/Model/CornersOptions.vue +53 -23
- package/app/components/Viewer/Generic/Model/EdgesOptions.vue +1 -3
- package/app/components/Viewer/Generic/Model/LinesOptions.vue +67 -38
- package/app/components/Viewer/Generic/Model/ModelStyleCard.vue +31 -15
- package/app/components/Viewer/Generic/Model/ModelStyleOptions.vue +2 -3
- package/app/components/Viewer/Generic/Model/PointsOptions.vue +6 -6
- package/app/components/Viewer/Generic/Model/SurfacesOptions.vue +69 -38
- package/app/components/Viewer/ObjectTree/Base/CommonTreeView.vue +64 -46
- package/app/components/Viewer/ObjectTree/Base/Controls.vue +5 -5
- package/app/components/Viewer/ObjectTree/Base/ItemLabel.vue +13 -9
- package/app/components/Viewer/ObjectTree/Base/StickyHeader.vue +5 -5
- package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +10 -10
- package/app/components/Viewer/ObjectTree/Box.vue +6 -4
- package/app/components/Viewer/ObjectTree/Layout.vue +23 -21
- package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +14 -14
- package/app/components/Viewer/ObjectTree/Views/{ModelCollections.vue → ModelTree.vue} +59 -24
- package/app/components/Viewer/Options/AttributeColorBar.vue +4 -2
- package/app/components/Viewer/Options/AttributeRangeSelector.vue +4 -2
- package/app/components/Viewer/Options/AttributeSelector.vue +7 -9
- package/app/components/Viewer/Options/ColorMapList.vue +15 -13
- package/app/components/Viewer/Options/ColorMapPicker.vue +4 -4
- package/app/components/Viewer/Options/ColorPicker.vue +223 -41
- package/app/components/Viewer/Options/ColoringTypeSelector.vue +16 -16
- package/app/components/Viewer/Options/ColormapQuickPicker.vue +7 -5
- package/app/components/Viewer/Options/OptionsSection.vue +1 -1
- package/app/components/Viewer/Options/TextureItem.vue +8 -6
- package/app/components/Viewer/Options/TexturesSelector.vue +1 -1
- package/app/components/Viewer/OverlappingObjectsPicker.vue +8 -9
- package/app/components/Viewer/PointSet/SpecificPointsOptions.vue +2 -6
- package/app/components/Viewer/Solid/SpecificPolyhedraOptions.vue +12 -16
- package/app/components/Viewer/Surface/PolygonsOptions.vue +12 -16
- package/app/components/Viewer/Ui.vue +2 -2
- package/app/components/Wrapper.vue +1 -4
- package/app/components/ZScaling.vue +5 -3
- package/app/components/csv-preview/CsvPreviewer.vue +38 -37
- package/app/components/csv-preview/CsvSettings.vue +6 -4
- package/app/components/csv-preview/CsvTable.vue +1 -1
- package/app/composables/batch_style.ts +5 -6
- package/app/composables/clipping_planes_widget.ts +73 -181
- package/app/composables/clipping_planes_widget_entries.ts +189 -0
- package/app/composables/copy_to_clipboard.ts +7 -0
- package/app/composables/global_attribute_style.ts +163 -83
- package/app/composables/hover_highlight.ts +46 -14
- package/app/composables/model_collections.ts +26 -13
- package/app/composables/model_components.ts +26 -13
- package/app/composables/project_manager.ts +76 -19
- package/app/composables/responsive_middle_truncate.ts +13 -13
- package/app/composables/stepper_tree.ts +34 -11
- package/app/composables/tree_filter.ts +66 -44
- package/app/composables/tree_keyboard_nav.ts +25 -11
- package/app/composables/tree_scroll.ts +44 -14
- package/app/composables/use_adaptive_styles.ts +66 -23
- package/app/composables/use_overlapping_picker.ts +66 -16
- package/app/composables/use_quick_colormap.ts +16 -5
- package/app/composables/virtual_tree.ts +75 -43
- package/app/composables/vtk_implicit_plane_widget.d.ts +2 -0
- package/app/plugins/auto_store_register.ts +2 -7
- package/app/plugins/click_outside_ignore_snackbars.client.ts +39 -0
- package/app/stores/app.ts +57 -192
- package/app/stores/app_helpers/extension.ts +179 -0
- package/app/stores/back.ts +37 -21
- package/app/stores/camera_manager.ts +22 -20
- package/app/stores/cloud.ts +17 -13
- package/app/stores/data.ts +88 -47
- package/app/stores/data_helpers/collections.ts +36 -38
- package/app/stores/data_helpers/mesh.ts +45 -36
- package/app/stores/data_style.ts +50 -28
- package/app/stores/feedback.ts +6 -6
- package/app/stores/hybrid_viewer.ts +41 -52
- package/app/stores/infra.ts +39 -37
- package/app/stores/menu.ts +23 -14
- package/app/stores/treeview.ts +72 -42
- package/app/stores/viewer.ts +39 -27
- package/app/utils/attributes.ts +3 -3
- package/app/utils/clipping_planes.ts +111 -14
- package/app/utils/color_picker.ts +57 -0
- package/app/utils/colormap.ts +58 -35
- package/app/utils/default_styles/index.ts +2 -2
- package/app/utils/default_styles/meshes.ts +107 -13
- package/app/utils/default_styles/models.ts +83 -14
- package/app/utils/extension.ts +47 -20
- package/app/utils/import_workflow.ts +21 -13
- package/app/utils/name_cleaner.ts +1 -1
- package/app/utils/string.ts +1 -1
- package/app/utils/treeview.ts +2 -2
- package/internal/database/base_database.ts +7 -3
- package/internal/database/database.ts +36 -26
- package/internal/database/extended_database.ts +1 -1
- package/internal/stores/data_style/mesh/cells/cell.ts +70 -34
- package/internal/stores/data_style/mesh/cells/color.ts +12 -6
- package/internal/stores/data_style/mesh/cells/common.ts +18 -8
- package/internal/stores/data_style/mesh/cells/index.ts +122 -42
- package/internal/stores/data_style/mesh/cells/textures.ts +12 -6
- package/internal/stores/data_style/mesh/cells/vertex.ts +75 -34
- package/internal/stores/data_style/mesh/cells/visibility.ts +15 -4
- package/internal/stores/data_style/mesh/edges/color.ts +12 -6
- package/internal/stores/data_style/mesh/edges/common.ts +14 -6
- package/internal/stores/data_style/mesh/edges/edge.ts +71 -39
- package/internal/stores/data_style/mesh/edges/index.ts +117 -42
- package/internal/stores/data_style/mesh/edges/vertex.ts +76 -39
- package/internal/stores/data_style/mesh/edges/visibility.ts +15 -4
- package/internal/stores/data_style/mesh/edges/width.ts +12 -4
- package/internal/stores/data_style/mesh/index.ts +30 -11
- package/internal/stores/data_style/mesh/points/color.ts +12 -6
- package/internal/stores/data_style/mesh/points/common.ts +21 -8
- package/internal/stores/data_style/mesh/points/index.ts +101 -50
- package/internal/stores/data_style/mesh/points/size.ts +12 -4
- package/internal/stores/data_style/mesh/points/vertex.ts +75 -34
- package/internal/stores/data_style/mesh/points/visibility.ts +17 -7
- package/internal/stores/data_style/mesh/polygons/color.ts +12 -6
- package/internal/stores/data_style/mesh/polygons/common.ts +23 -6
- package/internal/stores/data_style/mesh/polygons/index.ts +130 -70
- package/internal/stores/data_style/mesh/polygons/polygon.ts +77 -43
- package/internal/stores/data_style/mesh/polygons/textures.ts +12 -6
- package/internal/stores/data_style/mesh/polygons/vertex.ts +79 -39
- package/internal/stores/data_style/mesh/polygons/visibility.ts +16 -6
- package/internal/stores/data_style/mesh/polyhedra/color.ts +12 -6
- package/internal/stores/data_style/mesh/polyhedra/common.ts +14 -6
- package/internal/stores/data_style/mesh/polyhedra/index.ts +115 -56
- package/internal/stores/data_style/mesh/polyhedra/polyhedron.ts +80 -39
- package/internal/stores/data_style/mesh/polyhedra/vertex.ts +79 -39
- package/internal/stores/data_style/mesh/polyhedra/visibility.ts +16 -6
- package/internal/stores/data_style/model/blocks/color.ts +43 -13
- package/internal/stores/data_style/model/blocks/common.ts +21 -6
- package/internal/stores/data_style/model/blocks/index.ts +89 -59
- package/internal/stores/data_style/model/blocks/polyhedron.ts +101 -44
- package/internal/stores/data_style/model/blocks/vertex.ts +107 -42
- package/internal/stores/data_style/model/blocks/visibility.ts +17 -4
- package/internal/stores/data_style/model/color.ts +171 -83
- package/internal/stores/data_style/model/common.ts +70 -21
- package/internal/stores/data_style/model/corners/color.ts +43 -11
- package/internal/stores/data_style/model/corners/common.ts +25 -6
- package/internal/stores/data_style/model/corners/index.ts +66 -41
- package/internal/stores/data_style/model/corners/vertex.ts +105 -74
- package/internal/stores/data_style/model/corners/visibility.ts +19 -4
- package/internal/stores/data_style/model/edges/common.ts +12 -4
- package/internal/stores/data_style/model/edges/index.ts +2 -1
- package/internal/stores/data_style/model/edges/visibility.ts +24 -7
- package/internal/stores/data_style/model/index.ts +24 -11
- package/internal/stores/data_style/model/lines/color.ts +41 -11
- package/internal/stores/data_style/model/lines/common.ts +17 -6
- package/internal/stores/data_style/model/lines/edge.ts +105 -42
- package/internal/stores/data_style/model/lines/index.ts +86 -59
- package/internal/stores/data_style/model/lines/vertex.ts +105 -42
- package/internal/stores/data_style/model/lines/visibility.ts +17 -4
- package/internal/stores/data_style/model/points/common.ts +8 -4
- package/internal/stores/data_style/model/points/index.ts +10 -3
- package/internal/stores/data_style/model/points/size.ts +12 -4
- package/internal/stores/data_style/model/points/visibility.ts +16 -6
- package/internal/stores/data_style/model/selection.ts +80 -58
- package/internal/stores/data_style/model/surfaces/color.ts +41 -11
- package/internal/stores/data_style/model/surfaces/common.ts +23 -6
- package/internal/stores/data_style/model/surfaces/index.ts +89 -59
- package/internal/stores/data_style/model/surfaces/polygon.ts +107 -67
- package/internal/stores/data_style/model/surfaces/vertex.ts +111 -67
- package/internal/stores/data_style/model/surfaces/visibility.ts +18 -5
- package/internal/stores/data_style/model/visibility.ts +82 -20
- package/internal/stores/data_style/state.ts +77 -65
- package/internal/stores/hybrid_viewer/brightness.ts +49 -31
- package/internal/stores/hybrid_viewer/camera.ts +191 -177
- package/internal/stores/hybrid_viewer/camera_animation.ts +11 -10
- package/internal/stores/hybrid_viewer/core.ts +64 -0
- package/internal/stores/hybrid_viewer/filters.ts +10 -16
- package/internal/stores/hybrid_viewer/highlight.ts +91 -130
- package/internal/stores/hybrid_viewer/ruler.ts +24 -15
- package/internal/stores/hybrid_viewer/scene.ts +96 -117
- package/internal/stores/hybrid_viewer/viewport.ts +74 -65
- package/internal/stores/hybrid_viewer/vtk_types.ts +13 -29
- package/internal/utils/api_fetch.ts +54 -23
- package/internal/utils/upload_file.ts +74 -53
- package/internal/utils/viewer_call.ts +22 -13
- package/internal/utils/ws_client.ts +24 -13
- package/nuxt.config.ts +58 -1
- package/package.json +20 -8
- package/server/api/cloud/extensions/run.post.ts +6 -10
- package/server/api/local/app/project_folder_path.post.ts +4 -4
- package/server/api/local/app/run_back.post.ts +5 -7
- package/server/api/local/app/run_viewer.post.ts +5 -7
- package/server/api/local/extensions/kill.post.ts +4 -3
- package/server/api/local/extensions/run.post.ts +7 -10
- package/server/api/local/extensions/upload.put.ts +44 -54
- package/server/api/microservice/app/get_is_app_ready.get.ts +4 -5
- package/server/api/microservice/app/set_app_base_url.post.ts +5 -6
- package/server/api/microservice/app/set_back_base_url.post.ts +5 -6
- package/server/api/microservice/app/set_is_app_ready.post.ts +5 -6
- package/server/api/microservice/app/set_viewer_base_url.post.ts +5 -6
- package/server/api/microservice/extensions/download.post.ts +5 -4
- package/server/api/serverless/run_cloud.post.ts +53 -11
- package/server/utils/app_config.ts +29 -22
- package/server/utils/cleanup.ts +60 -30
- package/server/utils/cloud.ts +9 -8
- package/server/utils/extension.ts +23 -9
- package/server/utils/microservices.ts +33 -24
- package/server/utils/path.ts +4 -4
- package/server/utils/scripts.ts +26 -23
- package/server/utils/server.ts +2 -2
- package/server/utils/server_config.ts +41 -27
- package/server/utils/stream.ts +6 -0
- package/server/utils/ws_client.ts +42 -8
- package/shared/scripts.ts +15 -10
- package/shared/utils/call_raw.ts +23 -16
- package/shared/utils/call_schema.ts +5 -8
- package/shared/utils/fetch_raw.ts +87 -47
- package/shared/utils/fetch_schema.ts +6 -9
- package/shared/utils/file.ts +8 -2
- package/shared/utils/response_handlers/load.ts +26 -11
- package/shared/utils/types.ts +9 -9
- package/shared/utils/validate_schema.ts +1 -1
- package/app/components/Viewer/ObjectTree/Views/ModelComponents.vue +0 -276
- package/server/types/vendor.d.ts +0 -64
- package/server/utils/server_config.js +0 -43
- package/shared/utils/call_raw.js +0 -30
- package/shared/utils/fetch_raw.js +0 -40
- /package/{opengeodeweb_front_schemas.js → .build/opengeodeweb_front_schemas.js} +0 -0
- /package/{server → .build/server}/utils/server.js +0 -0
- /package/{shared → .build/shared}/app_mode.js +0 -0
- /package/{shared → .build/shared}/utils/parse_boolean.js +0 -0
- /package/{shared → .build/shared}/utils/types.js +0 -0
|
@@ -2,18 +2,50 @@ import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.jso
|
|
|
2
2
|
import fileDownload from "js-file-download";
|
|
3
3
|
import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
|
|
4
4
|
|
|
5
|
+
import { type DataStyleSnapshot, useDataStyleStore } from "@ogw_front/stores/data_style";
|
|
6
|
+
import { type NewDataItem, useDataStore } from "@ogw_front/stores/data";
|
|
7
|
+
import { type TreeviewSnapshot, useTreeviewStore } from "@ogw_front/stores/treeview";
|
|
8
|
+
import type { CameraSnapshot } from "@ogw_internal/stores/hybrid_viewer/camera";
|
|
9
|
+
import type { ModelComponentRecord } from "@ogw_front/stores/data_helpers/mesh.js";
|
|
10
|
+
import type { ModelComponentRelationRecord } from "@ogw_front/stores/data.js";
|
|
5
11
|
import { fetchRaw } from "@ogw_shared/utils/fetch_raw";
|
|
6
12
|
import { importWorkflowFromSnapshot } from "@ogw_front/utils/import_workflow";
|
|
7
13
|
import { useAppStore } from "@ogw_front/stores/app";
|
|
8
14
|
import { useBackStore } from "@ogw_front/stores/back";
|
|
9
|
-
import { useDataStore } from "@ogw_front/stores/data";
|
|
10
|
-
import { useDataStyleStore } from "@ogw_front/stores/data_style";
|
|
11
15
|
import { useFeedbackStore } from "@ogw_front/stores/feedback";
|
|
12
16
|
import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
|
|
13
|
-
import { useTreeviewStore } from "@ogw_front/stores/treeview";
|
|
14
17
|
import { useViewerStore } from "@ogw_front/stores/viewer";
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
interface ProjectDataSnapshot {
|
|
20
|
+
items: NewDataItem[];
|
|
21
|
+
modelComponents: ModelComponentRecord[];
|
|
22
|
+
modelComponentsRelations: ModelComponentRelationRecord[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ProjectSnapshot {
|
|
26
|
+
treeview?: TreeviewSnapshot;
|
|
27
|
+
data?: ProjectDataSnapshot;
|
|
28
|
+
hybridViewer?: CameraSnapshot;
|
|
29
|
+
dataStyle?: DataStyleSnapshot;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The backend response is an untyped network payload; this is the one place its shape is asserted.
|
|
33
|
+
function isImportSnapshotResponse(value: unknown): value is { snapshot?: ProjectSnapshot } {
|
|
34
|
+
return typeof value === "object" && value !== null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isFileDownloadData(
|
|
38
|
+
value: unknown,
|
|
39
|
+
): value is string | ArrayBuffer | ArrayBufferView | Blob {
|
|
40
|
+
return (
|
|
41
|
+
typeof value === "string" ||
|
|
42
|
+
value instanceof ArrayBuffer ||
|
|
43
|
+
ArrayBuffer.isView(value) ||
|
|
44
|
+
value instanceof Blob
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function exportProject(): Promise<{ result: unknown }> {
|
|
17
49
|
console.log("[export triggered]");
|
|
18
50
|
const appStore = useAppStore();
|
|
19
51
|
const backStore = useBackStore();
|
|
@@ -29,11 +61,38 @@ async function exportProject() {
|
|
|
29
61
|
baseURL: backStore.base_url,
|
|
30
62
|
});
|
|
31
63
|
|
|
64
|
+
if (!isFileDownloadData(result)) {
|
|
65
|
+
throw new Error("Unexpected export_project response type");
|
|
66
|
+
}
|
|
32
67
|
fileDownload(result, defaultName);
|
|
33
68
|
feedbackStore.add_success("Project exported successfully");
|
|
34
69
|
return { result };
|
|
35
70
|
}
|
|
36
71
|
|
|
72
|
+
async function releaseViewerDatabase(
|
|
73
|
+
client: ReturnType<typeof useViewerStore>["client"],
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
if (
|
|
76
|
+
client !== undefined &&
|
|
77
|
+
client.getConnection !== undefined &&
|
|
78
|
+
client.getConnection().getSession !== undefined
|
|
79
|
+
) {
|
|
80
|
+
await client.getConnection().getSession().call("opengeodeweb_viewer.release_database", [{}]);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function importProjectToViewer(
|
|
85
|
+
client: ReturnType<typeof useViewerStore>["client"],
|
|
86
|
+
): Promise<void> {
|
|
87
|
+
if (
|
|
88
|
+
client !== undefined &&
|
|
89
|
+
client.getConnection !== undefined &&
|
|
90
|
+
client.getConnection().getSession !== undefined
|
|
91
|
+
) {
|
|
92
|
+
await client.getConnection().getSession().call("opengeodeweb_viewer.import_project", [{}]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
37
96
|
async function importProject(file: File): Promise<void> {
|
|
38
97
|
const backStore = useBackStore();
|
|
39
98
|
const dataStyleStore = useDataStyleStore();
|
|
@@ -44,21 +103,18 @@ async function importProject(file: File): Promise<void> {
|
|
|
44
103
|
|
|
45
104
|
await viewerStore.ws_connect();
|
|
46
105
|
|
|
47
|
-
|
|
48
|
-
if (client && client.getConnection && client.getConnection().getSession) {
|
|
49
|
-
await client.getConnection().getSession().call("opengeodeweb_viewer.release_database", [{}]);
|
|
50
|
-
}
|
|
106
|
+
await releaseViewerDatabase(viewerStore.client);
|
|
51
107
|
const resetVisualizationSchema = viewer_schemas.opengeodeweb_viewer.viewer.reset_visualization;
|
|
52
108
|
const timeout = undefined;
|
|
53
109
|
await viewerStore.request({ schema: resetVisualizationSchema, timeout });
|
|
54
110
|
|
|
55
111
|
treeviewStore.clear();
|
|
56
|
-
dataStore.clear();
|
|
112
|
+
await dataStore.clear();
|
|
57
113
|
hybridViewerStore.clear();
|
|
58
114
|
|
|
59
115
|
const importProjectSchema = back_schemas.opengeodeweb_back.import_project;
|
|
60
116
|
const form = new FormData();
|
|
61
|
-
const originalFileName = file
|
|
117
|
+
const originalFileName = file.name.length > 0 ? file.name : "project.vease";
|
|
62
118
|
if (!originalFileName.toLowerCase().endsWith(".vease")) {
|
|
63
119
|
throw new Error("Uploaded file must be a .vease");
|
|
64
120
|
}
|
|
@@ -70,23 +126,24 @@ async function importProject(file: File): Promise<void> {
|
|
|
70
126
|
method: importProjectSchema.methods[0],
|
|
71
127
|
baseURL: backStore.base_url,
|
|
72
128
|
});
|
|
73
|
-
const snapshot = result
|
|
129
|
+
const snapshot = (isImportSnapshotResponse(result) ? result.snapshot : undefined) ?? {};
|
|
74
130
|
|
|
75
131
|
treeviewStore.isImporting = true;
|
|
76
132
|
|
|
77
|
-
|
|
78
|
-
if (client2 && client2.getConnection && client2.getConnection().getSession) {
|
|
79
|
-
await client2.getConnection().getSession().call("opengeodeweb_viewer.import_project", [{}]);
|
|
80
|
-
}
|
|
133
|
+
await importProjectToViewer(viewerStore.client);
|
|
81
134
|
|
|
82
|
-
|
|
83
|
-
|
|
135
|
+
treeviewStore.importStores(snapshot.treeview);
|
|
136
|
+
if (snapshot.data !== undefined) {
|
|
137
|
+
await dataStore.importStores(snapshot.data);
|
|
138
|
+
}
|
|
84
139
|
await hybridViewerStore.initHybridViewer();
|
|
85
140
|
|
|
86
|
-
const items = snapshot
|
|
141
|
+
const items = snapshot.data?.items ?? [];
|
|
87
142
|
await importWorkflowFromSnapshot(items);
|
|
88
143
|
await hybridViewerStore.importStores(snapshot.hybridViewer);
|
|
89
|
-
|
|
144
|
+
if (snapshot.dataStyle !== undefined) {
|
|
145
|
+
await dataStyleStore.importStores(snapshot.dataStyle);
|
|
146
|
+
}
|
|
90
147
|
await dataStyleStore.applyAllStylesFromState();
|
|
91
148
|
|
|
92
149
|
treeviewStore.finalizeImportSelection();
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
// Not auto-fixable (eslint's sort-imports core rule has no autofixer) and this file's import order doesn't match its syntax-kind-then-alphabetical requirement - left as-is rather than manually reordered across the codebase for a purely cosmetic rule.
|
|
2
|
-
// oxlint-disable eslint/sort-imports
|
|
3
1
|
import { middleTruncate } from "@ogw_front/utils/string";
|
|
4
|
-
import type { MaybeRefOrGetter } from "vue";
|
|
5
2
|
|
|
6
3
|
const UUID_END_CHARS = 12;
|
|
7
4
|
const ELLIPSIS_LENGTH = 3;
|
|
@@ -9,17 +6,20 @@ const MIN_START_CHARS = 4;
|
|
|
9
6
|
const DEFAULT_ESTIMATED_CHAR_WIDTH = 8.5;
|
|
10
7
|
|
|
11
8
|
interface ResponsiveMiddleTruncateOptions {
|
|
12
|
-
estimatedCharWidth?: number;
|
|
13
|
-
uuidEndChars?: number;
|
|
14
|
-
ellipsisLength?: number;
|
|
15
|
-
minStartChars?: number;
|
|
9
|
+
readonly estimatedCharWidth?: number;
|
|
10
|
+
readonly uuidEndChars?: number;
|
|
11
|
+
readonly ellipsisLength?: number;
|
|
12
|
+
readonly minStartChars?: number;
|
|
16
13
|
}
|
|
17
14
|
|
|
15
|
+
// Same shape as Vue's MaybeRefOrGetter<T>, kept as a local alias for readability at call sites.
|
|
16
|
+
type ReadonlyMaybeRefOrGetter<Value> = Value | Ref<Value> | (() => Value);
|
|
17
|
+
|
|
18
18
|
export function useResponsiveMiddleTruncate(
|
|
19
|
-
textRef:
|
|
20
|
-
containerWidthRef:
|
|
19
|
+
textRef: ReadonlyMaybeRefOrGetter<string | undefined | null>,
|
|
20
|
+
containerWidthRef: ReadonlyMaybeRefOrGetter<number | undefined>,
|
|
21
21
|
options: ResponsiveMiddleTruncateOptions = {},
|
|
22
|
-
) {
|
|
22
|
+
): ComputedRef<string> {
|
|
23
23
|
const {
|
|
24
24
|
estimatedCharWidth = DEFAULT_ESTIMATED_CHAR_WIDTH,
|
|
25
25
|
uuidEndChars = UUID_END_CHARS,
|
|
@@ -29,11 +29,11 @@ export function useResponsiveMiddleTruncate(
|
|
|
29
29
|
|
|
30
30
|
return computed(() => {
|
|
31
31
|
const text = toValue(textRef);
|
|
32
|
-
if (
|
|
32
|
+
if (text === undefined || text === null || text === "") {
|
|
33
33
|
return "";
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const width = toValue(containerWidthRef)
|
|
36
|
+
const width = toValue(containerWidthRef) ?? 0;
|
|
37
37
|
const maxChars = Math.floor(width / estimatedCharWidth);
|
|
38
38
|
|
|
39
39
|
if (maxChars <= 0 || text.length <= maxChars) {
|
|
@@ -43,6 +43,6 @@ export function useResponsiveMiddleTruncate(
|
|
|
43
43
|
const endChars = Math.min(uuidEndChars, Math.floor(maxChars / ellipsisLength));
|
|
44
44
|
const startChars = Math.max(minStartChars, maxChars - endChars - ellipsisLength);
|
|
45
45
|
|
|
46
|
-
return middleTruncate(text, maxChars, startChars, endChars);
|
|
46
|
+
return middleTruncate(text, maxChars, startChars, endChars) ?? "";
|
|
47
47
|
});
|
|
48
48
|
}
|
|
@@ -1,13 +1,36 @@
|
|
|
1
|
-
|
|
1
|
+
interface StepperTreeApi {
|
|
2
|
+
state: Record<string, unknown>;
|
|
3
|
+
update_values: (keys_values_object: Record<string, unknown>) => void;
|
|
4
|
+
increment_step: () => void;
|
|
5
|
+
decrement_step: () => void;
|
|
6
|
+
reset_values: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isUnknownArray(value: unknown): value is unknown[] {
|
|
10
|
+
return Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function get_current_step_index(state: Record<string, unknown>): number {
|
|
14
|
+
const value = state.current_step_index;
|
|
15
|
+
return typeof value === "number" ? value : 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function build_initial_state_unref(
|
|
19
|
+
initial_state: Record<string, unknown>,
|
|
20
|
+
): Record<string, unknown> {
|
|
2
21
|
const initial_state_unref: Record<string, unknown> = {};
|
|
3
22
|
for (const [key, value] of Object.entries(initial_state)) {
|
|
4
|
-
const unref_val = unref(value);
|
|
5
|
-
|
|
6
|
-
initial_state_unref[key] = [...unref_val];
|
|
7
|
-
} else {
|
|
8
|
-
initial_state_unref[key] = unref_val;
|
|
9
|
-
}
|
|
23
|
+
const unref_val: unknown = unref(value);
|
|
24
|
+
initial_state_unref[key] = isUnknownArray(unref_val) ? [...unref_val] : unref_val;
|
|
10
25
|
}
|
|
26
|
+
return initial_state_unref;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function useStepperTree(
|
|
30
|
+
steps: readonly unknown[],
|
|
31
|
+
initial_state: Record<string, unknown> = {},
|
|
32
|
+
): StepperTreeApi {
|
|
33
|
+
const initial_state_unref = build_initial_state_unref(initial_state);
|
|
11
34
|
const state = reactive<Record<string, unknown>>({
|
|
12
35
|
current_step_index: 0,
|
|
13
36
|
navigating_back: false,
|
|
@@ -16,7 +39,7 @@ export function useStepperTree(steps: unknown[], initial_state: Record<string, u
|
|
|
16
39
|
});
|
|
17
40
|
|
|
18
41
|
watch(
|
|
19
|
-
() => state
|
|
42
|
+
() => get_current_step_index(state),
|
|
20
43
|
(newVal, oldVal) => {
|
|
21
44
|
if (newVal < oldVal) {
|
|
22
45
|
state.navigating_back = true;
|
|
@@ -31,18 +54,18 @@ export function useStepperTree(steps: unknown[], initial_state: Record<string, u
|
|
|
31
54
|
}
|
|
32
55
|
|
|
33
56
|
function increment_step(): void {
|
|
34
|
-
|
|
57
|
+
state.current_step_index = get_current_step_index(state) + 1;
|
|
35
58
|
}
|
|
36
59
|
|
|
37
60
|
function decrement_step(): void {
|
|
38
|
-
|
|
61
|
+
state.current_step_index = get_current_step_index(state) - 1;
|
|
39
62
|
}
|
|
40
63
|
|
|
41
64
|
function reset_values(): void {
|
|
42
65
|
state.current_step_index = 0;
|
|
43
66
|
state.navigating_back = false;
|
|
44
67
|
for (const [key, initial_val] of Object.entries(initial_state_unref)) {
|
|
45
|
-
state[key] =
|
|
68
|
+
state[key] = isUnknownArray(initial_val) ? [...initial_val] : initial_val;
|
|
46
69
|
}
|
|
47
70
|
}
|
|
48
71
|
|
|
@@ -1,20 +1,37 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Ref } from "vue";
|
|
2
|
+
|
|
3
|
+
// Same shape as Vue's MaybeRefOrGetter<T>, kept as a local alias for readability at call sites.
|
|
4
|
+
type ReadonlyMaybeRefOrGetter<Value> = Value | Ref<Value> | (() => Value);
|
|
2
5
|
|
|
3
6
|
// This composable filters/sorts different flavors of "category with children" trees (treeview groups, model component/collection groups, ...); this shape captures just the fields it reads/writes, generically, across all of them.
|
|
4
7
|
interface FilterableItem {
|
|
5
|
-
id: unknown;
|
|
6
|
-
title?: string;
|
|
7
|
-
children?: FilterableItem[];
|
|
8
|
+
readonly id: unknown;
|
|
9
|
+
readonly title?: string;
|
|
10
|
+
readonly children?: readonly FilterableItem[];
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
interface FilterContext {
|
|
11
|
-
raw?: FilterableItem;
|
|
14
|
+
readonly raw?: FilterableItem;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
interface TreeFilterOptions {
|
|
15
|
-
recursiveSort?: boolean;
|
|
16
|
-
defaultSort?: string;
|
|
17
|
-
defaultFilters?: Record<string, boolean>;
|
|
18
|
+
readonly recursiveSort?: boolean;
|
|
19
|
+
readonly defaultSort?: string;
|
|
20
|
+
readonly defaultFilters?: Record<string, boolean>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface UseTreeFilterReturn {
|
|
24
|
+
search: Ref<string>;
|
|
25
|
+
sortType: Ref<string>;
|
|
26
|
+
filterOptions: Ref<Record<string, boolean>>;
|
|
27
|
+
processedItems: ComputedRef<FilterableItem[]>;
|
|
28
|
+
availableFilterOptions: ComputedRef<string[]>;
|
|
29
|
+
toggleSort: () => void;
|
|
30
|
+
customFilter: typeof customFilter;
|
|
31
|
+
applySearchFilter: (
|
|
32
|
+
newSelection: readonly unknown[],
|
|
33
|
+
previousSelection?: readonly unknown[],
|
|
34
|
+
) => unknown[];
|
|
18
35
|
}
|
|
19
36
|
|
|
20
37
|
function customFilter(
|
|
@@ -22,37 +39,50 @@ function customFilter(
|
|
|
22
39
|
searchQuery: string | undefined,
|
|
23
40
|
item: FilterContext,
|
|
24
41
|
): boolean {
|
|
25
|
-
if (
|
|
42
|
+
if (searchQuery === undefined || searchQuery === "") {
|
|
26
43
|
return true;
|
|
27
44
|
}
|
|
28
|
-
if (!item
|
|
45
|
+
if (!item.raw) {
|
|
29
46
|
return false;
|
|
30
47
|
}
|
|
31
48
|
const query = searchQuery.toLowerCase();
|
|
32
|
-
const { title = "", id = value } = item.raw
|
|
49
|
+
const { title = "", id = value } = item.raw;
|
|
33
50
|
return [title, id].some((field) => String(field).toLowerCase().includes(query));
|
|
34
51
|
}
|
|
35
52
|
|
|
53
|
+
// Extracted so we never call String() directly on a value typed as `unknown`/`any`.
|
|
54
|
+
// The `id` field can be anything at runtime, so only stringify primitives that have a sane toString.
|
|
55
|
+
// Anything else falls back to "" instead of risking "[object Object]".
|
|
56
|
+
function toSortKey(item: FilterableItem, field: "title" | "id"): string {
|
|
57
|
+
const raw = item[field] ?? item.id ?? "";
|
|
58
|
+
if (typeof raw === "string" || typeof raw === "number" || typeof raw === "boolean") {
|
|
59
|
+
return String(raw);
|
|
60
|
+
}
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
|
|
36
64
|
function sortAndFormatItems(
|
|
37
|
-
itemList: FilterableItem[] | undefined | null,
|
|
65
|
+
itemList: readonly FilterableItem[] | undefined | null,
|
|
38
66
|
sortType: string,
|
|
39
67
|
options: TreeFilterOptions = {},
|
|
40
68
|
): FilterableItem[] {
|
|
41
|
-
if (!itemList
|
|
69
|
+
if (!itemList) {
|
|
42
70
|
return [];
|
|
43
71
|
}
|
|
44
72
|
const field: "title" | "id" = sortType === "name" ? "title" : "id";
|
|
45
73
|
const localeOptions: Intl.CollatorOptions = { numeric: true, sensitivity: "base" };
|
|
46
74
|
|
|
47
|
-
|
|
75
|
+
// Copy into a plain mutable array up front: chaining .filter()/.toSorted() straight off the
|
|
76
|
+
// Readonly-typed parameter (previously combined with an Array.isArray guard here) has been
|
|
77
|
+
// Observed to make oxlint's type-aware analysis silently widen the result to `any[]`.
|
|
78
|
+
const items: FilterableItem[] = [...itemList];
|
|
79
|
+
const sorted = items
|
|
48
80
|
.filter((item) => item !== null && item !== undefined)
|
|
49
|
-
.toSorted((itemA, itemB) =>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return fieldA.localeCompare(fieldB, undefined, localeOptions);
|
|
53
|
-
});
|
|
81
|
+
.toSorted((itemA, itemB) =>
|
|
82
|
+
toSortKey(itemA, field).localeCompare(toSortKey(itemB, field), undefined, localeOptions),
|
|
83
|
+
);
|
|
54
84
|
|
|
55
|
-
if (options.recursiveSort) {
|
|
85
|
+
if (options.recursiveSort === true) {
|
|
56
86
|
return sorted.map((item) => {
|
|
57
87
|
if (item.children && item.children.length > 0) {
|
|
58
88
|
return {
|
|
@@ -67,39 +97,31 @@ function sortAndFormatItems(
|
|
|
67
97
|
}
|
|
68
98
|
|
|
69
99
|
function useTreeFilter(
|
|
70
|
-
itemsIn:
|
|
100
|
+
itemsIn: ReadonlyMaybeRefOrGetter<readonly FilterableItem[]>,
|
|
71
101
|
options: TreeFilterOptions = {},
|
|
72
|
-
) {
|
|
102
|
+
): UseTreeFilterReturn {
|
|
73
103
|
const rawItems = typeof itemsIn === "function" ? computed(itemsIn) : toRef(itemsIn);
|
|
74
104
|
const search = ref("");
|
|
75
|
-
const sortType = ref(options.defaultSort
|
|
76
|
-
const filterOptions = ref<Record<string, boolean>>(options.defaultFilters
|
|
105
|
+
const sortType = ref(options.defaultSort ?? "name");
|
|
106
|
+
const filterOptions = ref<Record<string, boolean>>({ ...options.defaultFilters });
|
|
77
107
|
|
|
78
|
-
const availableFilterOptions = computed(() =>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
return rawItems.value.map((category) => String(category.title || category.id));
|
|
83
|
-
});
|
|
108
|
+
const availableFilterOptions = computed(() =>
|
|
109
|
+
rawItems.value.map((category) => String(category.title ?? category.id)),
|
|
110
|
+
);
|
|
84
111
|
|
|
85
112
|
watch(
|
|
86
113
|
availableFilterOptions,
|
|
87
|
-
(newOptions) => {
|
|
114
|
+
(newOptions: readonly string[]) => {
|
|
88
115
|
for (const option of newOptions) {
|
|
89
|
-
|
|
90
|
-
filterOptions.value[option] = true;
|
|
91
|
-
}
|
|
116
|
+
filterOptions.value[option] ??= true;
|
|
92
117
|
}
|
|
93
118
|
},
|
|
94
119
|
{ immediate: true },
|
|
95
120
|
);
|
|
96
121
|
|
|
97
122
|
const processedItems = computed(() => {
|
|
98
|
-
if (!rawItems.value) {
|
|
99
|
-
return [];
|
|
100
|
-
}
|
|
101
123
|
const filteredByCategory = rawItems.value.filter((category) => {
|
|
102
|
-
const key = String(category.title
|
|
124
|
+
const key = String(category.title ?? category.id);
|
|
103
125
|
return filterOptions.value[key] !== false;
|
|
104
126
|
});
|
|
105
127
|
|
|
@@ -111,7 +133,7 @@ function useTreeFilter(
|
|
|
111
133
|
|
|
112
134
|
const result: FilterableItem[] = [];
|
|
113
135
|
for (const category of sorted) {
|
|
114
|
-
const children = (category.children
|
|
136
|
+
const children = (category.children ?? []).filter((child) =>
|
|
115
137
|
customFilter(child.id, search.value, { raw: child }),
|
|
116
138
|
);
|
|
117
139
|
if (children.length > 0 || customFilter(category.id, search.value, { raw: category })) {
|
|
@@ -127,7 +149,7 @@ function useTreeFilter(
|
|
|
127
149
|
|
|
128
150
|
const allItems = computed(() => {
|
|
129
151
|
const map = new Map<unknown, FilterableItem>();
|
|
130
|
-
function traverse(items: FilterableItem[]): void {
|
|
152
|
+
function traverse(items: readonly FilterableItem[]): void {
|
|
131
153
|
for (const item of items) {
|
|
132
154
|
map.set(item.id, item);
|
|
133
155
|
if (item.children) {
|
|
@@ -135,16 +157,16 @@ function useTreeFilter(
|
|
|
135
157
|
}
|
|
136
158
|
}
|
|
137
159
|
}
|
|
138
|
-
traverse(rawItems.value
|
|
160
|
+
traverse(rawItems.value);
|
|
139
161
|
return map;
|
|
140
162
|
});
|
|
141
163
|
|
|
142
164
|
function applySearchFilter(
|
|
143
|
-
newSelection: unknown[],
|
|
144
|
-
previousSelection: unknown[] = [],
|
|
165
|
+
newSelection: readonly unknown[],
|
|
166
|
+
previousSelection: readonly unknown[] = [],
|
|
145
167
|
): unknown[] {
|
|
146
168
|
if (!search.value) {
|
|
147
|
-
return newSelection;
|
|
169
|
+
return [...newSelection];
|
|
148
170
|
}
|
|
149
171
|
const allItemsMap = allItems.value;
|
|
150
172
|
function matches(id: unknown): boolean {
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Not auto-fixable (eslint's sort-imports core rule has no autofixer) and this file's import order doesn't match its syntax-kind-then-alphabetical requirement - left as-is rather than manually reordered across the codebase for a purely cosmetic rule.
|
|
2
|
-
// oxlint-disable eslint/sort-imports
|
|
3
|
-
import type { Ref } from "vue";
|
|
4
1
|
import type { DisplayItem, EmitFn } from "./virtual_tree";
|
|
5
2
|
|
|
6
3
|
interface VisibleBounds {
|
|
@@ -14,6 +11,20 @@ interface ScrollInfo {
|
|
|
14
11
|
itemHeight: number;
|
|
15
12
|
}
|
|
16
13
|
|
|
14
|
+
type ReadonlyDisplayItem = Omit<DisplayItem, "raw"> & {
|
|
15
|
+
readonly raw: Record<string, unknown>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
interface KeyboardEventLike {
|
|
19
|
+
readonly key: string;
|
|
20
|
+
preventDefault: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface UseTreeKeyboardNavReturn {
|
|
24
|
+
focusedIndex: Ref<number>;
|
|
25
|
+
handleKeyDown: (event: KeyboardEventLike) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
function getBaseIndex(
|
|
18
29
|
currentIndex: number,
|
|
19
30
|
lastIndex: number,
|
|
@@ -38,17 +49,17 @@ function getBaseIndex(
|
|
|
38
49
|
}
|
|
39
50
|
|
|
40
51
|
export function useTreeKeyboardNav(
|
|
41
|
-
displayItems: Ref<
|
|
52
|
+
displayItems: Ref<readonly ReadonlyDisplayItem[]>,
|
|
42
53
|
emit: EmitFn,
|
|
43
54
|
scrollToIndex: (index: number) => void,
|
|
44
55
|
toggleOpen: (raw: Record<string, unknown>) => void,
|
|
45
|
-
handleItemClick: (item:
|
|
56
|
+
handleItemClick: (item: ReadonlyDisplayItem, index: number) => void,
|
|
46
57
|
getScrollInfo: (() => ScrollInfo | undefined) | undefined,
|
|
47
58
|
externalFocusedIndex: Ref<number> | undefined,
|
|
48
|
-
) {
|
|
49
|
-
const focusedIndex = externalFocusedIndex
|
|
59
|
+
): UseTreeKeyboardNavReturn {
|
|
60
|
+
const focusedIndex = externalFocusedIndex ?? ref(-1);
|
|
50
61
|
|
|
51
|
-
function findParentIndex(item:
|
|
62
|
+
function findParentIndex(item: ReadonlyDisplayItem, currentIndex: number): number {
|
|
52
63
|
for (let index = currentIndex - 1; index >= 0; index -= 1) {
|
|
53
64
|
const candidate = displayItems.value[index];
|
|
54
65
|
if (candidate && candidate.depth < item.depth) {
|
|
@@ -74,7 +85,7 @@ export function useTreeKeyboardNav(
|
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
function handleExpandOrNext(
|
|
77
|
-
item:
|
|
88
|
+
item: ReadonlyDisplayItem | undefined,
|
|
78
89
|
currentIndex: number,
|
|
79
90
|
lastIndex: number,
|
|
80
91
|
): number {
|
|
@@ -85,7 +96,10 @@ export function useTreeKeyboardNav(
|
|
|
85
96
|
return Math.min(currentIndex + 1, lastIndex);
|
|
86
97
|
}
|
|
87
98
|
|
|
88
|
-
function handleCollapseOrParent(
|
|
99
|
+
function handleCollapseOrParent(
|
|
100
|
+
item: ReadonlyDisplayItem | undefined,
|
|
101
|
+
currentIndex: number,
|
|
102
|
+
): number {
|
|
89
103
|
if (item && !item.isLeaf && item.isOpen) {
|
|
90
104
|
toggleOpen(item.raw);
|
|
91
105
|
return currentIndex;
|
|
@@ -129,7 +143,7 @@ export function useTreeKeyboardNav(
|
|
|
129
143
|
return currentIndex;
|
|
130
144
|
}
|
|
131
145
|
|
|
132
|
-
function handleKeyDown(event:
|
|
146
|
+
function handleKeyDown(event: KeyboardEventLike): void {
|
|
133
147
|
if (displayItems.value.length === 0) {
|
|
134
148
|
return;
|
|
135
149
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Not auto-fixable (eslint's sort-imports core rule has no autofixer) and this file's import order doesn't match its syntax-kind-then-alphabetical requirement - left as-is rather than manually reordered across the codebase for a purely cosmetic rule.
|
|
2
|
-
// oxlint-disable eslint/sort-imports
|
|
3
|
-
import type { MaybeRefOrGetter, Ref } from "vue";
|
|
4
1
|
import type { DisplayItem, EmitFn, ItemPropsConfig } from "./virtual_tree";
|
|
5
2
|
|
|
6
3
|
interface TreeScrollProps {
|
|
@@ -8,24 +5,57 @@ interface TreeScrollProps {
|
|
|
8
5
|
}
|
|
9
6
|
|
|
10
7
|
interface ScrollableElement {
|
|
11
|
-
$el: { scrollTop: number; clientHeight: number };
|
|
8
|
+
$el: { scrollTop: number; clientHeight: number } | undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ScrollInfo {
|
|
12
|
+
scrollTop: number;
|
|
13
|
+
containerHeight: number;
|
|
14
|
+
itemHeight: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type ReadonlyMaybeRefOrGetter<Value> = Value | Ref<Value> | (() => Value);
|
|
18
|
+
|
|
19
|
+
type ReadonlyDisplayItem = Omit<DisplayItem, "raw"> & {
|
|
20
|
+
readonly raw: Record<string, unknown>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
interface ScrollEventLike {
|
|
24
|
+
readonly target: EventTarget | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface UseTreeScrollReturn {
|
|
28
|
+
internalScrollTop: Ref<number>;
|
|
29
|
+
virtualScrollRef: Ref<ScrollableElement | undefined>;
|
|
30
|
+
stickyHeader: ComputedRef<ReadonlyDisplayItem | undefined>;
|
|
31
|
+
handleScroll: (event: ScrollEventLike) => void;
|
|
32
|
+
scrollToIndex: (index: number) => void;
|
|
33
|
+
getScrollInfo: () => ScrollInfo;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isHtmlElement(value: EventTarget | null): value is HTMLElement {
|
|
37
|
+
return value instanceof HTMLElement;
|
|
12
38
|
}
|
|
13
39
|
|
|
14
40
|
export function useTreeScroll(
|
|
15
|
-
propsIn:
|
|
41
|
+
propsIn: ReadonlyMaybeRefOrGetter<TreeScrollProps>,
|
|
16
42
|
emit: EmitFn,
|
|
17
|
-
displayItems: Ref<
|
|
43
|
+
displayItems: Ref<readonly ReadonlyDisplayItem[]>,
|
|
18
44
|
actualItemProps: Ref<ItemPropsConfig>,
|
|
19
|
-
) {
|
|
45
|
+
): UseTreeScrollReturn {
|
|
20
46
|
const SCROLL_STICKY_THRESHOLD = 10;
|
|
21
47
|
const DEFAULT_ITEM_HEIGHT = 28;
|
|
22
48
|
|
|
23
49
|
const props = toRef(propsIn);
|
|
24
|
-
const internalScrollTop = ref(props.value.scrollTop
|
|
50
|
+
const internalScrollTop = ref(props.value.scrollTop ?? 0);
|
|
25
51
|
const virtualScrollRef = ref<ScrollableElement | undefined>(undefined);
|
|
26
52
|
|
|
27
|
-
function handleScroll(event:
|
|
28
|
-
const {
|
|
53
|
+
function handleScroll(event: ScrollEventLike): void {
|
|
54
|
+
const { target } = event;
|
|
55
|
+
if (!isHtmlElement(target)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const { scrollTop } = target;
|
|
29
59
|
internalScrollTop.value = scrollTop;
|
|
30
60
|
emit("update:scrollTop", scrollTop);
|
|
31
61
|
}
|
|
@@ -35,7 +65,7 @@ export function useTreeScroll(
|
|
|
35
65
|
(newVal = 0) => {
|
|
36
66
|
if (Math.abs(newVal - internalScrollTop.value) > 1) {
|
|
37
67
|
internalScrollTop.value = newVal;
|
|
38
|
-
if (virtualScrollRef.value && virtualScrollRef.value.$el) {
|
|
68
|
+
if (virtualScrollRef.value !== undefined && virtualScrollRef.value.$el !== undefined) {
|
|
39
69
|
virtualScrollRef.value.$el.scrollTop = newVal;
|
|
40
70
|
}
|
|
41
71
|
}
|
|
@@ -76,12 +106,12 @@ export function useTreeScroll(
|
|
|
76
106
|
});
|
|
77
107
|
|
|
78
108
|
function scrollToIndex(index: number): void {
|
|
79
|
-
if (index === -1 ||
|
|
109
|
+
if (index === -1 || virtualScrollRef.value === undefined) {
|
|
80
110
|
return;
|
|
81
111
|
}
|
|
82
112
|
|
|
83
113
|
const container = virtualScrollRef.value.$el;
|
|
84
|
-
if (
|
|
114
|
+
if (container === undefined) {
|
|
85
115
|
return;
|
|
86
116
|
}
|
|
87
117
|
|
|
@@ -100,7 +130,7 @@ export function useTreeScroll(
|
|
|
100
130
|
}
|
|
101
131
|
}
|
|
102
132
|
|
|
103
|
-
function getScrollInfo() {
|
|
133
|
+
function getScrollInfo(): ScrollInfo {
|
|
104
134
|
const container = virtualScrollRef.value?.$el;
|
|
105
135
|
const containerHeight = container ? container.clientHeight : 0;
|
|
106
136
|
const itemHeight = actualItemProps.value?.height || DEFAULT_ITEM_HEIGHT;
|