@peter.prib/renderengine 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +203 -2
- package/REFERENCE.md +2198 -0
- package/dist/renderengine-workspace.umd.js +46905 -0
- package/dist/renderengine.umd.js +42223 -0
- package/examples/archimate.html +66 -0
- package/examples/diagram-editor.html +51 -0
- package/package.json +65 -4
- package/src/BaseRenderer.js +57 -0
- package/src/CanvasRenderer.js +194 -0
- package/src/Mat4.js +111 -0
- package/src/OrbitCamera.js +57 -0
- package/src/RendererFactory.js +50 -0
- package/src/SVGRenderer.js +439 -0
- package/src/archimate.js +723 -0
- package/src/archimateArchiFormat.js +1693 -0
- package/src/archimateArchiTypes.js +72 -0
- package/src/archimateArchive.js +140 -0
- package/src/archimateCSV.js +127 -0
- package/src/archimateDrawio.js +182 -0
- package/src/archimateExchangeFormat.js +546 -0
- package/src/archimateRelationshipTable.js +93 -0
- package/src/archimateSpecializations.js +161 -0
- package/src/chartCatalog.js +589 -0
- package/src/charting/ArchimateDiagramEditor.js +1624 -0
- package/src/charting/ArchimateSpecializationSelector.js +134 -0
- package/src/charting/Axis.js +68 -0
- package/src/charting/CameraSeeder.js +30 -0
- package/src/charting/CatalogDialog.js +92 -0
- package/src/charting/Chart.js +37 -0
- package/src/charting/Chart3D.js +99 -0
- package/src/charting/ChartBox.js +80 -0
- package/src/charting/ChartData.js +27 -0
- package/src/charting/ChartRegistry.js +76 -0
- package/src/charting/CheckboxTreeSelector.js +304 -0
- package/src/charting/ColorField.js +107 -0
- package/src/charting/ColorPalette.js +88 -0
- package/src/charting/CommandHistory.js +176 -0
- package/src/charting/DataFilter.js +52 -0
- package/src/charting/Diagram.js +36 -0
- package/src/charting/Diagram3D.js +29 -0
- package/src/charting/DiagramClipboard.js +31 -0
- package/src/charting/DiagramEditor.js +3330 -0
- package/src/charting/DiagramEditorCommand.js +110 -0
- package/src/charting/DiagramEditorCommands.js +237 -0
- package/src/charting/DiagramEditorEdgeCommands.js +142 -0
- package/src/charting/DiagramEditorState.js +98 -0
- package/src/charting/DiagramRegistry.js +86 -0
- package/src/charting/DiagramTypeRegistry.js +326 -0
- package/src/charting/DiagramViewPropertiesDialog.js +46 -0
- package/src/charting/Edge.js +113 -0
- package/src/charting/ErrorBars.js +37 -0
- package/src/charting/FilterSystem.js +285 -0
- package/src/charting/FindReplaceDialog.js +139 -0
- package/src/charting/FormDialog.js +385 -0
- package/src/charting/Gauge.js +58 -0
- package/src/charting/Hierarchy.js +175 -0
- package/src/charting/HintPanel.js +86 -0
- package/src/charting/ImageSelector.js +148 -0
- package/src/charting/InteractionManager.js +725 -0
- package/src/charting/Legend.js +58 -0
- package/src/charting/MenuBar.js +62 -0
- package/src/charting/ModelFileFormat.js +115 -0
- package/src/charting/OrbitControls.js +77 -0
- package/src/charting/Palette.js +119 -0
- package/src/charting/PopupMenu.js +117 -0
- package/src/charting/PropertiesPanel.js +155 -0
- package/src/charting/PropertyListEditor.js +96 -0
- package/src/charting/PropertyLog.js +92 -0
- package/src/charting/SettingsCascade.js +93 -0
- package/src/charting/SettingsDialog.js +53 -0
- package/src/charting/SettingsManager.js +179 -0
- package/src/charting/SettingsViewPropertiesDialog.js +58 -0
- package/src/charting/ShapePaths.js +27 -0
- package/src/charting/Statistics.js +134 -0
- package/src/charting/TemplateCatalog.js +59 -0
- package/src/charting/TemplateLibrary.js +103 -0
- package/src/charting/TextStyle.js +168 -0
- package/src/charting/ViewFit.js +55 -0
- package/src/charting/ViewMenu.js +70 -0
- package/src/charting/ViewPropertiesDialog.js +77 -0
- package/src/charting/ViewTabStrip.js +81 -0
- package/src/charting/archimate/ArchiFileFormat.js +32 -0
- package/src/charting/archimate/ArchimateCanvasEditor.js +815 -0
- package/src/charting/archimate/ArchimateCsvFileFormat.js +35 -0
- package/src/charting/archimate/ArchimateEdge.js +137 -0
- package/src/charting/archimate/ArchimateElementTemplate.js +195 -0
- package/src/charting/archimate/ArchimatePalette.js +102 -0
- package/src/charting/archimate/CsvImportDialog.js +105 -0
- package/src/charting/archimate/CustomViewpoints.js +74 -0
- package/src/charting/archimate/DerivedEdge.js +29 -0
- package/src/charting/archimate/DrawioFileFormat.js +24 -0
- package/src/charting/archimate/ElementFilterRule.js +21 -0
- package/src/charting/archimate/ExchangeFileFormat.js +28 -0
- package/src/charting/archimate/ViewGroupTemplate.js +137 -0
- package/src/charting/archimate/ViewLineEdge.js +84 -0
- package/src/charting/archimate/ViewpointRule.js +45 -0
- package/src/charting/archimate/archimateFileFormats.js +12 -0
- package/src/charting/archimate/archimateNames.js +5 -0
- package/src/charting/archimate/csvMapping.js +122 -0
- package/src/charting/archimate/elementMenuItems.js +32 -0
- package/src/charting/archimate/elementProperties.js +146 -0
- package/src/charting/archimate/elementTypeChoices.js +7 -0
- package/src/charting/archimate/elementTypeGroups.js +19 -0
- package/src/charting/archimate/index.js +17 -0
- package/src/charting/archimate/specializationCatalogDialog.js +64 -0
- package/src/charting/archimate/typeGlyph.js +29 -0
- package/src/charting/arrangement/Arrangement.js +112 -0
- package/src/charting/arrangement/ArrangementCommand.js +29 -0
- package/src/charting/arrangement/BackgroundCommand.js +41 -0
- package/src/charting/arrangement/BackgroundGroup.js +18 -0
- package/src/charting/arrangement/GroupCommand.js +46 -0
- package/src/charting/arrangement/ItemGroup.js +62 -0
- package/src/charting/arrangement/LockCommand.js +32 -0
- package/src/charting/arrangement/UngroupCommand.js +27 -0
- package/src/charting/arrangement/alignment.js +88 -0
- package/src/charting/arrangement/arrangeMenuItems.js +25 -0
- package/src/charting/arrangement/index.js +11 -0
- package/src/charting/arrangement/layouts.js +233 -0
- package/src/charting/canvas/ArchiHint.js +78 -0
- package/src/charting/canvas/CanvasBoxKind.js +39 -0
- package/src/charting/canvas/CanvasPalette.js +49 -0
- package/src/charting/canvas/CanvasTemplateLibrary.js +61 -0
- package/src/charting/canvas/CanvasTemplateMenu.js +151 -0
- package/src/charting/canvas/EditHintCommand.js +28 -0
- package/src/charting/canvas/canvasBoxKinds.js +27 -0
- package/src/charting/canvas/canvasBoxMenuItems.js +40 -0
- package/src/charting/canvas/defaultCanvasTemplates.js +186 -0
- package/src/charting/canvas/diagramBoxKinds.js +12 -0
- package/src/charting/canvas/hintMenuItems.js +49 -0
- package/src/charting/charts/ArcGauge.js +55 -0
- package/src/charting/charts/AreaChart.js +71 -0
- package/src/charting/charts/BarChart.js +80 -0
- package/src/charting/charts/BarChart3D.js +56 -0
- package/src/charting/charts/BarChart3DOrbit.js +53 -0
- package/src/charting/charts/BarChartBase.js +70 -0
- package/src/charting/charts/BarGauge.js +42 -0
- package/src/charting/charts/BoxPlotChart.js +41 -0
- package/src/charting/charts/BubbleChart.js +99 -0
- package/src/charting/charts/BubbleChart3D.js +78 -0
- package/src/charting/charts/BulletGauge.js +87 -0
- package/src/charting/charts/CalendarHeatmapChart.js +204 -0
- package/src/charting/charts/CandlestickChart.js +54 -0
- package/src/charting/charts/ChordDiagram.js +130 -0
- package/src/charting/charts/DensityChart.js +73 -0
- package/src/charting/charts/DialGauge.js +65 -0
- package/src/charting/charts/DonutChart.js +15 -0
- package/src/charting/charts/FunnelChart.js +47 -0
- package/src/charting/charts/GanttChart.js +201 -0
- package/src/charting/charts/GaugeChart.js +175 -0
- package/src/charting/charts/GeoMapChart.js +146 -0
- package/src/charting/charts/GroupedBarChart.js +88 -0
- package/src/charting/charts/GroupedDistributionChart.js +70 -0
- package/src/charting/charts/HeatmapChart.js +80 -0
- package/src/charting/charts/HierarchyChart.js +39 -0
- package/src/charting/charts/HistogramChart.js +45 -0
- package/src/charting/charts/KpiTile.js +72 -0
- package/src/charting/charts/LagPlotChart.js +41 -0
- package/src/charting/charts/LineChart.js +124 -0
- package/src/charting/charts/MultiRingGauge.js +54 -0
- package/src/charting/charts/OutlineChart.js +95 -0
- package/src/charting/charts/ParallelCoordinatesChart.js +130 -0
- package/src/charting/charts/PieChart.js +56 -0
- package/src/charting/charts/PipeChart.js +88 -0
- package/src/charting/charts/PopulationPyramidChart.js +106 -0
- package/src/charting/charts/QQPlotChart.js +64 -0
- package/src/charting/charts/RadarChart.js +76 -0
- package/src/charting/charts/RadialBarChart.js +96 -0
- package/src/charting/charts/RingGauge.js +23 -0
- package/src/charting/charts/SankeyDiagram.js +174 -0
- package/src/charting/charts/ScatterChart.js +104 -0
- package/src/charting/charts/StackedAreaChart.js +15 -0
- package/src/charting/charts/StackedBarChart.js +33 -0
- package/src/charting/charts/StackedBarChart3DOrbit.js +58 -0
- package/src/charting/charts/StreamGraphChart.js +131 -0
- package/src/charting/charts/SunburstChart.js +57 -0
- package/src/charting/charts/ThermometerGauge.js +43 -0
- package/src/charting/charts/TreemapChart.js +54 -0
- package/src/charting/charts/ViolinChart.js +37 -0
- package/src/charting/charts/WaterfallChart.js +66 -0
- package/src/charting/charts/WordCloudChart.js +104 -0
- package/src/charting/diagramModel/AddViewCommand.js +33 -0
- package/src/charting/diagramModel/DiagramModel.js +39 -0
- package/src/charting/diagramModel/DiagramModelSearch.js +22 -0
- package/src/charting/diagramModel/DiagramView.js +50 -0
- package/src/charting/diagramModel/ModelHistory.js +43 -0
- package/src/charting/diagramModel/ModelReport.js +153 -0
- package/src/charting/diagramModel/ModelSearch.js +223 -0
- package/src/charting/diagramModel/RemoveViewCommand.js +25 -0
- package/src/charting/diagramModel/RenameViewCommand.js +21 -0
- package/src/charting/diagramModel/ReplaceTextCommand.js +25 -0
- package/src/charting/diagramModel/ViewSetChecker.js +34 -0
- package/src/charting/diagramModel/ViewSetModel.js +243 -0
- package/src/charting/diagramModel/index.js +6 -0
- package/src/charting/diagramModel/nameTransforms.js +29 -0
- package/src/charting/diagrams/ArchimateCommands.js +141 -0
- package/src/charting/diagrams/ArchimateDiagram.js +263 -0
- package/src/charting/diagrams/ArchimateModel.js +1155 -0
- package/src/charting/diagrams/ArchimateModelChecker.js +125 -0
- package/src/charting/diagrams/ArchimateModelReport.js +128 -0
- package/src/charting/diagrams/ArchimateModelSearch.js +41 -0
- package/src/charting/diagrams/ArchimateValidator.js +357 -0
- package/src/charting/diagrams/CanvasView.js +112 -0
- package/src/charting/diagrams/ContextMenu.js +37 -0
- package/src/charting/diagrams/DataModelDiagram.js +86 -0
- package/src/charting/diagrams/DataModelRelationshipStrategy.js +38 -0
- package/src/charting/diagrams/DerivedRelationships.js +102 -0
- package/src/charting/diagrams/EbnfDiagram.js +39 -0
- package/src/charting/diagrams/EdgeGeometry.js +553 -0
- package/src/charting/diagrams/EditViewItemCommand.js +128 -0
- package/src/charting/diagrams/ElementInstance.js +157 -0
- package/src/charting/diagrams/GraphDiagram.js +1340 -0
- package/src/charting/diagrams/GraphDiagram3D.js +152 -0
- package/src/charting/diagrams/GraphMetrics.js +81 -0
- package/src/charting/diagrams/GridOverlay.js +137 -0
- package/src/charting/diagrams/ImportRowsCommand.js +62 -0
- package/src/charting/diagrams/LabelHelper.js +108 -0
- package/src/charting/diagrams/MergeElementsCommand.js +101 -0
- package/src/charting/diagrams/NestInstanceCommand.js +34 -0
- package/src/charting/diagrams/NodeGeometry.js +158 -0
- package/src/charting/diagrams/PlaceItemsCommand.js +77 -0
- package/src/charting/diagrams/RadialLayout.js +101 -0
- package/src/charting/diagrams/RelationshipInstance.js +93 -0
- package/src/charting/diagrams/RemoveGroupFromViewCommand.js +28 -0
- package/src/charting/diagrams/RemoveLineCommand.js +27 -0
- package/src/charting/diagrams/ShowElementCommand.js +43 -0
- package/src/charting/diagrams/SvgCoordinate.js +17 -0
- package/src/charting/diagrams/SvgZoom.js +127 -0
- package/src/charting/diagrams/Tooltip.js +62 -0
- package/src/charting/diagrams/ViewConnectionInstance.js +102 -0
- package/src/charting/diagrams/ViewObjectInstance.js +56 -0
- package/src/charting/diagrams/commands/AddEdgeCommand.js +34 -0
- package/src/charting/diagrams/commands/AddNodeCommand.js +28 -0
- package/src/charting/diagrams/commands/AddWaypointCommand.js +27 -0
- package/src/charting/diagrams/commands/CreateFolderCommand.js +27 -0
- package/src/charting/diagrams/commands/DeleteEdgeCommand.js +30 -0
- package/src/charting/diagrams/commands/DeleteFolderCommand.js +47 -0
- package/src/charting/diagrams/commands/DeleteNodeCommand.js +35 -0
- package/src/charting/diagrams/commands/DeleteWaypointCommand.js +24 -0
- package/src/charting/diagrams/commands/GraphCommand.js +65 -0
- package/src/charting/diagrams/commands/InvertEdgeCommand.js +24 -0
- package/src/charting/diagrams/commands/MoveNodeCommand.js +44 -0
- package/src/charting/diagrams/commands/MoveWaypointCommand.js +27 -0
- package/src/charting/diagrams/commands/RenameEdgeCommand.js +23 -0
- package/src/charting/diagrams/commands/RenameFolderCommand.js +31 -0
- package/src/charting/diagrams/commands/RenameNodeCommand.js +27 -0
- package/src/charting/diagrams/commands/ResizeNodeCommand.js +46 -0
- package/src/charting/diagrams/commands/UpdateEdgeSideCommand.js +40 -0
- package/src/charting/diagrams/commands/index.js +17 -0
- package/src/charting/diagrams/duplicates.js +49 -0
- package/src/charting/diagrams/nesting.js +30 -0
- package/src/charting/diagrams/roadmap.js +77 -0
- package/src/charting/edgeMarkers/ArrowMarker.js +15 -0
- package/src/charting/edgeMarkers/CircleMarker.js +13 -0
- package/src/charting/edgeMarkers/CrowsFootMarker.js +16 -0
- package/src/charting/edgeMarkers/DiamondMarker.js +15 -0
- package/src/charting/edgeMarkers/EdgeMarker.js +34 -0
- package/src/charting/edgeMarkers/HalfArrowMarker.js +8 -0
- package/src/charting/edgeMarkers/HollowDiamondMarker.js +18 -0
- package/src/charting/edgeMarkers/HollowTriangleMarker.js +18 -0
- package/src/charting/edgeMarkers/OpenArrowMarker.js +17 -0
- package/src/charting/edgeMarkers/index.js +27 -0
- package/src/charting/edgeStyle/EdgeStyleCascade.js +22 -0
- package/src/charting/edgeStyle/EdgeStyleDialog.js +51 -0
- package/src/charting/edgeStyle/EdgeStyleSchema.js +43 -0
- package/src/charting/edgeStyle/SetStyleCommand.js +12 -0
- package/src/charting/edgeStyle/index.js +4 -0
- package/src/charting/emphasis/EmphasisRule.js +42 -0
- package/src/charting/emphasis/HighlightRule.js +24 -0
- package/src/charting/gauges/ArcTrack.js +51 -0
- package/src/charting/gauges/GaugeAnimation.js +51 -0
- package/src/charting/gauges/GaugeBands.js +48 -0
- package/src/charting/gauges/GaugeScale.js +87 -0
- package/src/charting/gauges/GaugeTrack.js +44 -0
- package/src/charting/gauges/GaugeValue.js +61 -0
- package/src/charting/gauges/HorizontalTrack.js +14 -0
- package/src/charting/gauges/LinearTrack.js +29 -0
- package/src/charting/gauges/Sparkline.js +42 -0
- package/src/charting/gauges/VerticalTrack.js +17 -0
- package/src/charting/glyphs/ArrowGlyph.js +21 -0
- package/src/charting/glyphs/BatteryGlyph.js +18 -0
- package/src/charting/glyphs/ChevronGlyph.js +18 -0
- package/src/charting/glyphs/CircleGlyph.js +15 -0
- package/src/charting/glyphs/CloudGlyph.js +19 -0
- package/src/charting/glyphs/ComponentGlyph.js +18 -0
- package/src/charting/glyphs/ConstraintGlyph.js +24 -0
- package/src/charting/glyphs/ContractBoxGlyph.js +10 -0
- package/src/charting/glyphs/CubeGlyph.js +19 -0
- package/src/charting/glyphs/CylinderGlyph.js +19 -0
- package/src/charting/glyphs/DashedDoubleArrowGlyph.js +11 -0
- package/src/charting/glyphs/DeviceGlyph.js +17 -0
- package/src/charting/glyphs/DiamondGlyph.js +17 -0
- package/src/charting/glyphs/DocumentGlyph.js +17 -0
- package/src/charting/glyphs/DoubleArrowGlyph.js +23 -0
- package/src/charting/glyphs/DoubleCircleGlyph.js +24 -0
- package/src/charting/glyphs/EllipseGlyph.js +18 -0
- package/src/charting/glyphs/FactoryGlyph.js +20 -0
- package/src/charting/glyphs/FlagGlyph.js +16 -0
- package/src/charting/glyphs/GapCircleGlyph.js +17 -0
- package/src/charting/glyphs/GearGlyph.js +37 -0
- package/src/charting/glyphs/Glyph.js +98 -0
- package/src/charting/glyphs/GroupingBoxGlyph.js +13 -0
- package/src/charting/glyphs/HalfCircleGlyph.js +22 -0
- package/src/charting/glyphs/HexagonGlyph.js +28 -0
- package/src/charting/glyphs/IconGlyph.js +22 -0
- package/src/charting/glyphs/ImageGlyph.js +16 -0
- package/src/charting/glyphs/JunctionGlyph.js +17 -0
- package/src/charting/glyphs/LollipopGlyph.js +22 -0
- package/src/charting/glyphs/MagnifierGlyph.js +17 -0
- package/src/charting/glyphs/NoteGlyph.js +33 -0
- package/src/charting/glyphs/ObjectBoxGlyph.js +21 -0
- package/src/charting/glyphs/OrJunctionGlyph.js +17 -0
- package/src/charting/glyphs/ParallelogramGlyph.js +20 -0
- package/src/charting/glyphs/PersonGlyph.js +23 -0
- package/src/charting/glyphs/PillGlyph.js +23 -0
- package/src/charting/glyphs/PinGlyph.js +18 -0
- package/src/charting/glyphs/PrincipleGlyph.js +23 -0
- package/src/charting/glyphs/ProductBoxGlyph.js +16 -0
- package/src/charting/glyphs/RectGlyph.js +15 -0
- package/src/charting/glyphs/RoundedBoxGlyph.js +13 -0
- package/src/charting/glyphs/StackGlyph.js +14 -0
- package/src/charting/glyphs/StepsGlyph.js +20 -0
- package/src/charting/glyphs/StickFigureGlyph.js +32 -0
- package/src/charting/glyphs/SystemSoftwareBoxGlyph.js +16 -0
- package/src/charting/glyphs/TabbedBoxGlyph.js +37 -0
- package/src/charting/glyphs/TargetGlyph.js +23 -0
- package/src/charting/glyphs/WavyBoxGlyph.js +17 -0
- package/src/charting/glyphs/WheelGlyph.js +28 -0
- package/src/charting/glyphs/drawGlyph.js +28 -0
- package/src/charting/glyphs/glyphSvg.js +55 -0
- package/src/charting/glyphs/index.js +109 -0
- package/src/charting/modelTree/AnalysisPanel.js +159 -0
- package/src/charting/modelTree/DiagramTabs.js +217 -0
- package/src/charting/modelTree/ModelTree.js +164 -0
- package/src/charting/modelTree/ModelTreePanel.js +914 -0
- package/src/charting/modelTree/TreeBuilder.js +238 -0
- package/src/charting/modelTree/TreeModeRegistry.js +51 -0
- package/src/charting/modelTree/TreeViewMode.js +110 -0
- package/src/charting/modelTree/buildFilterTrees.js +138 -0
- package/src/charting/modelTree/modes/FlatTreeMode.js +46 -0
- package/src/charting/modelTree/modes/LayerGroupedTreeMode.js +62 -0
- package/src/charting/modelTree/modes/TypeGroupedTreeMode.js +60 -0
- package/src/charting/modelTree/treeLayers.js +11 -0
- package/src/charting/report/reportHtml.js +82 -0
- package/src/charting/report/reportViewer.js +118 -0
- package/src/charting/shapes/CircleShape.js +19 -0
- package/src/charting/shapes/PathShape.js +17 -0
- package/src/charting/shapes/RectShape.js +20 -0
- package/src/charting/shapes/Shape.js +20 -0
- package/src/charting/shapes/ShapeFactory.js +23 -0
- package/src/charting/storage/LocalJsonStore.js +43 -0
- package/src/charting/templates/ActorTemplate.js +36 -0
- package/src/charting/templates/BoxTemplate.js +27 -0
- package/src/charting/templates/CircleTemplate.js +18 -0
- package/src/charting/templates/ClassTemplate.js +20 -0
- package/src/charting/templates/CloudTemplate.js +43 -0
- package/src/charting/templates/ComponentTemplate.js +19 -0
- package/src/charting/templates/CylinderTemplate.js +30 -0
- package/src/charting/templates/DataStoreTemplate.js +16 -0
- package/src/charting/templates/DiamondTemplate.js +10 -0
- package/src/charting/templates/DocumentTemplate.js +32 -0
- package/src/charting/templates/EllipseTemplate.js +15 -0
- package/src/charting/templates/FinalStateTemplate.js +12 -0
- package/src/charting/templates/GlyphTemplate.js +47 -0
- package/src/charting/templates/HexagonTemplate.js +17 -0
- package/src/charting/templates/LifelineTemplate.js +20 -0
- package/src/charting/templates/ManualInputTemplate.js +12 -0
- package/src/charting/templates/NoteTemplate.js +23 -0
- package/src/charting/templates/OctagonTemplate.js +7 -0
- package/src/charting/templates/PackageTemplate.js +25 -0
- package/src/charting/templates/ParallelogramTemplate.js +11 -0
- package/src/charting/templates/PentagonTemplate.js +5 -0
- package/src/charting/templates/PolygonTemplate.js +18 -0
- package/src/charting/templates/PredefinedProcessTemplate.js +13 -0
- package/src/charting/templates/RectangleTemplate.js +28 -0
- package/src/charting/templates/RegularPolygonTemplate.js +21 -0
- package/src/charting/templates/RoundedRectangleTemplate.js +5 -0
- package/src/charting/templates/StadiumTemplate.js +8 -0
- package/src/charting/templates/StarTemplate.js +17 -0
- package/src/charting/templates/SwimlaneTemplate.js +18 -0
- package/src/charting/templates/Template.js +208 -0
- package/src/charting/templates/TrapezoidTemplate.js +11 -0
- package/src/charting/templates/TriangleTemplate.js +12 -0
- package/src/charting/templates/index.js +31 -0
- package/src/charting/templates/outline.js +59 -0
- package/src/charting/templates/svg.js +19 -0
- package/src/charting/textToShapes.js +61 -0
- package/src/charting/toolbar/ToolbarButton.js +38 -0
- package/src/charting/toolbar/ToolbarCheckbox.js +31 -0
- package/src/charting/toolbar/ToolbarItem.js +19 -0
- package/src/charting/toolbar/ToolbarMenu.js +36 -0
- package/src/charting/toolbar/ToolbarSelect.js +38 -0
- package/src/charting/toolbar/ToolbarSeparator.js +7 -0
- package/src/charting/toolbar/ToolbarText.js +22 -0
- package/src/charting/toolbar/index.js +8 -0
- package/src/charting/toolbar/pickFile.js +14 -0
- package/src/charting/viewDecorations.js +85 -0
- package/src/charting/zOrder.js +57 -0
- package/src/csvTable.js +61 -0
- package/src/datamodel.js +122 -0
- package/src/datawindow/BoundedWindow.js +36 -0
- package/src/datawindow/FlatDataWindow.js +34 -0
- package/src/datawindow/GraphDataWindow.js +40 -0
- package/src/datawindow/GroupedSampleDataWindow.js +16 -0
- package/src/datawindow/SeriesDataWindow.js +42 -0
- package/src/datawindow/index.js +31 -0
- package/src/diagramCatalog.js +30 -0
- package/src/ebnfDiagram.js +57 -0
- package/src/ebnfParser.js +129 -0
- package/src/exportImage.js +233 -0
- package/src/index.js +2636 -0
- package/src/itIcons.js +84 -0
- package/src/kernelExpr.js +243 -0
- package/src/propertiesDialog.js +463 -0
- package/src/railroadDiagram.js +571 -0
- package/src/treeModeCatalog.js +16 -0
- package/src/workspace/ContextBus.js +79 -0
- package/src/workspace/EditorShell.js +67 -0
- package/src/workspace/Part.js +163 -0
- package/src/workspace/PartFactory.js +49 -0
- package/src/workspace/PartRegistry.js +86 -0
- package/src/workspace/Workspace.js +321 -0
- package/src/workspace/archimateServices.js +33 -0
- package/src/workspace/dom.js +120 -0
- package/src/workspace/index.js +75 -0
- package/src/workspace/layout/GridNode.js +280 -0
- package/src/workspace/layout/LayoutNode.js +89 -0
- package/src/workspace/layout/PartNode.js +77 -0
- package/src/workspace/layout/SplitNode.js +114 -0
- package/src/workspace/layout/TabsNode.js +191 -0
- package/src/workspace/layout/index.js +24 -0
- package/src/workspace/parts/AdapterPart.js +33 -0
- package/src/workspace/parts/ArchimateCanvasPart.js +345 -0
- package/src/workspace/parts/ArchimateEditorPart.js +79 -0
- package/src/workspace/parts/ChartPart.js +115 -0
- package/src/workspace/parts/ContentPart.js +34 -0
- package/src/workspace/parts/DetailPart.js +46 -0
- package/src/workspace/parts/DiagramEditorPart.js +135 -0
- package/src/workspace/parts/EditorPart.js +388 -0
- package/src/workspace/parts/HtmlPart.js +25 -0
- package/src/workspace/parts/ListPart.js +57 -0
- package/src/workspace/parts/ModelFile.js +187 -0
- package/src/workspace/parts/ModelsPart.js +135 -0
- package/src/workspace/styles.js +258 -0
- package/src/workspace/themes.js +96 -0
- package/src/workspace/workArea.js +413 -0
- package/src/workspace/workAreaModel.js +197 -0
- package/src/workspace.js +16 -0
- package/src/zip/ZipArchive.js +160 -0
- package/src/zip/crc32.js +25 -0
- package/src/zip/inflate.js +159 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkArea — the IDE-style editor area: several views open at once, in tab
|
|
3
|
+
* groups that can sit side by side.
|
|
4
|
+
*
|
|
5
|
+
* The whole point is the separation the plain `tabs`/`split` layout does not
|
|
6
|
+
* make on its own: a view being OPEN (it has a tab) is not the same as it being
|
|
7
|
+
* VISIBLE (its tab is selected in a group that is on screen). A shell that
|
|
8
|
+
* rebuilds a fixed layout per view can only ever show one; a work area lets you
|
|
9
|
+
* put the data model next to the notebook that queries it.
|
|
10
|
+
*
|
|
11
|
+
* Structure lives in `workAreaModel.js` as pure functions over plain JSON, and
|
|
12
|
+
* this class is the thin rendering shell over them:
|
|
13
|
+
*
|
|
14
|
+
* model (JSON) --createLayout--> live layout tree --serialize--> model
|
|
15
|
+
*
|
|
16
|
+
* The live tree is authoritative for the things the user adjusts directly —
|
|
17
|
+
* splitter positions, which tab is selected — so every structural change starts
|
|
18
|
+
* by reading them back out of it (`_syncFromTree`). Rebuilding the subtree
|
|
19
|
+
* afterwards is cheap and non-destructive: `PartNode.dispose()` only detaches a
|
|
20
|
+
* part and `Part.mount()` re-parents it, so a part keeps its DOM, its state and
|
|
21
|
+
* its bus subscriptions across the rebuild.
|
|
22
|
+
*
|
|
23
|
+
* Selecting a tab does NOT rebuild, though — the pane elements are what scroll,
|
|
24
|
+
* and replacing them would throw away scroll position on every tab click.
|
|
25
|
+
*/
|
|
26
|
+
import { createLayout } from './layout/index.js';
|
|
27
|
+
import { el } from './dom.js';
|
|
28
|
+
import {
|
|
29
|
+
emptyWorkArea, listGroups, listOpenParts, findPart,
|
|
30
|
+
openPart, closePart, focusPart,
|
|
31
|
+
} from './workAreaModel.js';
|
|
32
|
+
|
|
33
|
+
export class WorkArea {
|
|
34
|
+
/**
|
|
35
|
+
* @param {object} [options]
|
|
36
|
+
* @param {object} [options.area] serialized area to restore
|
|
37
|
+
* @param {string} [options.activeGroupId] group new views open into
|
|
38
|
+
* @param {Function} [options.onChange] ({ area, activeGroupId, reason }) — persist here
|
|
39
|
+
* @param {*} [options.placeholder] el() spec shown when nothing is open
|
|
40
|
+
* @param {Element} [options.actionsHost] filled with the focused view's `actions`
|
|
41
|
+
* @param {Element} [options.outlineHost] filled with the focused view's outline part
|
|
42
|
+
* @param {Function} [options.outlineFor] (partId) => outline partId | null
|
|
43
|
+
* @param {*} [options.outlinePlaceholder] el() spec when a view has no outline
|
|
44
|
+
* @param {Function} [options.beforeClose] (partId) => false to keep a tab its close button was
|
|
45
|
+
* pressed on (unsaved work, say); the handler may close it later
|
|
46
|
+
*/
|
|
47
|
+
constructor({
|
|
48
|
+
area, activeGroupId, onChange, placeholder,
|
|
49
|
+
actionsHost, outlineHost, outlineFor, outlinePlaceholder, beforeClose,
|
|
50
|
+
} = {}) {
|
|
51
|
+
this.area = area ?? emptyWorkArea();
|
|
52
|
+
this.activeGroupId = activeGroupId ?? listGroups(this.area)[0]?.id ?? null;
|
|
53
|
+
this.onChange = onChange ?? null;
|
|
54
|
+
this.beforeClose = beforeClose ?? null;
|
|
55
|
+
this.placeholder = placeholder ?? { cls: 'ws-workarea-empty', text: 'Nothing open' };
|
|
56
|
+
this.host = null;
|
|
57
|
+
this.ctx = null;
|
|
58
|
+
this._tree = null;
|
|
59
|
+
this._outerCtx = null;
|
|
60
|
+
|
|
61
|
+
// Frame slots. Both are filled from whichever view has FOCUS, which is
|
|
62
|
+
// the whole reason they live here: the work area is the only thing that
|
|
63
|
+
// knows what that is.
|
|
64
|
+
this.actionsHost = actionsHost ?? null;
|
|
65
|
+
this.outlineHost = outlineHost ?? null;
|
|
66
|
+
this.outlineFor = outlineFor ?? null;
|
|
67
|
+
this.outlinePlaceholder = outlinePlaceholder ?? null;
|
|
68
|
+
this._outlinePart = null;
|
|
69
|
+
this._slotPartId = null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* ── Queries ────────────────────────────────────────────────────────── */
|
|
73
|
+
|
|
74
|
+
/** Every open part id, in group order. */
|
|
75
|
+
openParts() { return listOpenParts(this.area); }
|
|
76
|
+
|
|
77
|
+
isOpen(partId) { return !!findPart(this.area, partId); }
|
|
78
|
+
|
|
79
|
+
/** The part id selected in each group — i.e. what is actually on screen. */
|
|
80
|
+
visibleParts() {
|
|
81
|
+
return listGroups(this.area)
|
|
82
|
+
.map(g => g.children?.[g.active ?? 0]?.partId)
|
|
83
|
+
.filter(Boolean);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The view with focus: the selected tab of the active group. This is what a
|
|
88
|
+
* shell keys its chrome off — window title, breadcrumb, which toolbar
|
|
89
|
+
* belongs to what — and it is not the same as "the last thing opened",
|
|
90
|
+
* because clicking into another group changes it.
|
|
91
|
+
*/
|
|
92
|
+
activePart() {
|
|
93
|
+
const groups = listGroups(this.area);
|
|
94
|
+
const group = groups.find(g => g.id === this.activeGroupId) ?? groups[0];
|
|
95
|
+
return group?.children?.[group.active ?? 0]?.partId ?? null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
groupCount() { return listGroups(this.area).length; }
|
|
99
|
+
|
|
100
|
+
/** `{ area, activeGroupId }` — hand straight to a store, pass back to the ctor. */
|
|
101
|
+
serialize() {
|
|
102
|
+
this._syncFromTree();
|
|
103
|
+
return { area: this.area, activeGroupId: this.activeGroupId };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ── Rendering ──────────────────────────────────────────────────────── */
|
|
107
|
+
|
|
108
|
+
render(host, ctx = {}) {
|
|
109
|
+
this.host = host;
|
|
110
|
+
this._outerCtx = ctx;
|
|
111
|
+
this.ctx = {
|
|
112
|
+
...ctx,
|
|
113
|
+
// Closing and group focus are decisions about the AREA, not about
|
|
114
|
+
// one tab strip, so they are intercepted here rather than left to
|
|
115
|
+
// TabsNode's own handling.
|
|
116
|
+
onTabClose: ({ partId }) => { if (partId && this.beforeClose?.(partId) !== false) this.close(partId); },
|
|
117
|
+
onGroupFocus: (id) => this._setActiveGroup(id),
|
|
118
|
+
onLayoutChange: () => this._changed('layout'),
|
|
119
|
+
};
|
|
120
|
+
this._build();
|
|
121
|
+
// Both slots explicitly: nothing has focus yet as far as they are
|
|
122
|
+
// concerned, so a restored area would otherwise come back with an empty
|
|
123
|
+
// action bar and an empty rail.
|
|
124
|
+
this._syncSlots({ actions: true, outline: true });
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_build() {
|
|
129
|
+
if (!this.host) return;
|
|
130
|
+
this._tree?.dispose();
|
|
131
|
+
this._tree = null;
|
|
132
|
+
this.host.textContent = '';
|
|
133
|
+
|
|
134
|
+
if (!listOpenParts(this.area).length) {
|
|
135
|
+
if (this.placeholder) this.host.appendChild(el(this.placeholder));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this._tree = createLayout(this.area);
|
|
140
|
+
this._tree.render(this.host, this.ctx);
|
|
141
|
+
this._tree.activate();
|
|
142
|
+
this._markActiveGroup();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* ── Frame slots ────────────────────────────────────────────────────── */
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Point the action bar and outline rail at whatever now has focus.
|
|
149
|
+
*
|
|
150
|
+
* A slot is re-read when focus MOVED, or when the caller asks for that slot
|
|
151
|
+
* specifically. The two are asked for separately because their costs
|
|
152
|
+
* differ: re-reading actions is a handful of buttons, whereas re-showing an
|
|
153
|
+
* outline re-parents an element, which resets its scroll position — so a
|
|
154
|
+
* `refreshActions()` must not quietly scroll a long tree back to the top.
|
|
155
|
+
*/
|
|
156
|
+
_syncSlots({ actions = false, outline = false } = {}) {
|
|
157
|
+
const partId = this.activePart();
|
|
158
|
+
const moved = partId !== this._slotPartId;
|
|
159
|
+
if (!moved && !actions && !outline) return;
|
|
160
|
+
this._slotPartId = partId;
|
|
161
|
+
if (moved || actions) this._renderActions(partId);
|
|
162
|
+
if (moved || outline) this._showOutline(partId);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
_renderActions(partId) {
|
|
166
|
+
if (!this.actionsHost) return;
|
|
167
|
+
this.actionsHost.textContent = '';
|
|
168
|
+
const actions = partId ? (this.ctx?.registry?.get(partId)?.actions ?? []) : [];
|
|
169
|
+
this.actionsHost.classList.toggle('ws-actionbar-empty', !actions.length);
|
|
170
|
+
for (const action of actions) {
|
|
171
|
+
if (!action) continue;
|
|
172
|
+
// An already-built element is passed straight through. This is the
|
|
173
|
+
// AdapterPart bargain again: a panel that already has a working
|
|
174
|
+
// toolbar — with selects, labels and its own rebuild logic — joins
|
|
175
|
+
// the frame's action bar by handing it over, not by being rewritten
|
|
176
|
+
// into a button array that could not express it anyway.
|
|
177
|
+
if (typeof action.nodeType === 'number' || typeof action.appendChild === 'function') {
|
|
178
|
+
this.actionsHost.appendChild(action);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (action.separator) { this.actionsHost.appendChild(el({ cls: 'ws-action-sep' })); continue; }
|
|
182
|
+
this.actionsHost.appendChild(el({
|
|
183
|
+
tag: 'button',
|
|
184
|
+
cls: ['ws-action', action.cls],
|
|
185
|
+
text: action.label ?? '',
|
|
186
|
+
attrs: {
|
|
187
|
+
type: 'button',
|
|
188
|
+
title: action.title ?? action.label ?? null,
|
|
189
|
+
disabled: action.disabled || null,
|
|
190
|
+
},
|
|
191
|
+
on: { click: (e) => action.onClick?.(e, this) },
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Show the focused view's outline.
|
|
198
|
+
*
|
|
199
|
+
* An outline is an ordinary Part, resolved through the registry — not a new
|
|
200
|
+
* kind of thing. That is what gets it pooling, mount-once-then-re-parent and
|
|
201
|
+
* lifecycle for free: a tree keeps its expanded nodes and scroll position
|
|
202
|
+
* while you work in another view and come back.
|
|
203
|
+
*/
|
|
204
|
+
_showOutline(partId) {
|
|
205
|
+
if (!this.outlineHost) return;
|
|
206
|
+
const outlineId = partId && this.outlineFor ? this.outlineFor(partId) : null;
|
|
207
|
+
const next = outlineId ? this.ctx?.registry?.get(outlineId) : null;
|
|
208
|
+
|
|
209
|
+
// Detached by name rather than by clearing the host: the outgoing
|
|
210
|
+
// outline is a pooled part, and `detach()` is the call that says so.
|
|
211
|
+
this._outlinePart?.detach();
|
|
212
|
+
this._outlinePart = null;
|
|
213
|
+
this._placeholderEl?.remove();
|
|
214
|
+
this._placeholderEl = null;
|
|
215
|
+
this.outlineHost.classList.toggle('ws-outline-empty', !next);
|
|
216
|
+
|
|
217
|
+
if (!next) {
|
|
218
|
+
if (this.outlinePlaceholder) {
|
|
219
|
+
this._placeholderEl = this.outlineHost.appendChild(el(this.outlinePlaceholder));
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
next.mount(this.outlineHost, this.ctx);
|
|
225
|
+
this._outlinePart = next;
|
|
226
|
+
} catch (err) {
|
|
227
|
+
console.error(`WorkArea: outline part "${outlineId}" failed to mount`, err);
|
|
228
|
+
this.outlineHost.classList.add('ws-outline-empty');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Re-read the focused view's actions — call when a panel's state changes. */
|
|
233
|
+
refreshActions() {
|
|
234
|
+
this._syncSlots({ actions: true });
|
|
235
|
+
return this;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Re-read both slots. For the case a lazily-mounted view creates the very
|
|
240
|
+
* things the slots are meant to show *after* it took focus — the action bar
|
|
241
|
+
* and outline were resolved against a panel that did not exist yet.
|
|
242
|
+
*/
|
|
243
|
+
refreshSlots() {
|
|
244
|
+
this._syncSlots({ actions: true, outline: true });
|
|
245
|
+
return this;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** The outline part currently on show, if any. */
|
|
249
|
+
get outlinePart() { return this._outlinePart; }
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Read the live tree back into the model. Splitter drags and tab selections
|
|
253
|
+
* only ever happened to the DOM nodes; skipping this would silently undo
|
|
254
|
+
* them the next time anything opens or closes.
|
|
255
|
+
*/
|
|
256
|
+
_syncFromTree() {
|
|
257
|
+
if (this._tree) this.area = this._tree.serialize();
|
|
258
|
+
return this.area;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Show every tab's label as its part's title is now (a file name that changed, say). */
|
|
262
|
+
refreshLabels() {
|
|
263
|
+
const walk = (node) => {
|
|
264
|
+
if (node?.constructor?.type === 'tabs') node.refreshLabels?.();
|
|
265
|
+
for (const child of node?.children ?? []) walk(child);
|
|
266
|
+
};
|
|
267
|
+
if (this._tree) walk(this._tree);
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** Live TabsNode for a group id, or null when nothing is rendered. */
|
|
272
|
+
_liveGroup(groupId) {
|
|
273
|
+
const walk = (node) => {
|
|
274
|
+
if (node?.id === groupId && node.constructor?.type === 'tabs') return node;
|
|
275
|
+
for (const child of node?.children ?? []) {
|
|
276
|
+
const hit = walk(child);
|
|
277
|
+
if (hit) return hit;
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
};
|
|
281
|
+
return this._tree ? walk(this._tree) : null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Tint the active group's tab strip, but only once there is more than one
|
|
286
|
+
* group — with a single group "where the next view opens" is not a question
|
|
287
|
+
* anyone is asking, and the accent would just be noise.
|
|
288
|
+
*/
|
|
289
|
+
_markActiveGroup() {
|
|
290
|
+
const many = this.groupCount() > 1;
|
|
291
|
+
for (const group of listGroups(this.area)) {
|
|
292
|
+
const live = this._liveGroup(group.id);
|
|
293
|
+
live?.el?.classList.toggle('ws-group-active', many && group.id === this.activeGroupId);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
_setActiveGroup(id) {
|
|
298
|
+
if (!id || id === this.activeGroupId) return;
|
|
299
|
+
this.activeGroupId = id;
|
|
300
|
+
this._markActiveGroup();
|
|
301
|
+
this._changed('focus-group');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
_changed(reason) {
|
|
305
|
+
this._syncFromTree();
|
|
306
|
+
// Before onChange, so a consumer reacting to the change (updating its
|
|
307
|
+
// own chrome, persisting) sees a frame that already agrees with focus.
|
|
308
|
+
this._syncSlots();
|
|
309
|
+
this.onChange?.({ area: this.area, activeGroupId: this.activeGroupId, reason });
|
|
310
|
+
this._outerCtx?.onLayoutChange?.();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* ── Commands ───────────────────────────────────────────────────────── */
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Open a view.
|
|
317
|
+
*
|
|
318
|
+
* `where`: `'tab'` (default) adds it to the active group; `'right'`,
|
|
319
|
+
* `'left'`, `'below'` or `'above'` opens a new group beside it — which is
|
|
320
|
+
* what actually gets two views on screen together.
|
|
321
|
+
*
|
|
322
|
+
* Already open anywhere -> focused, not duplicated: a part instance owns one
|
|
323
|
+
* DOM element, so a second tab for it would necessarily be empty.
|
|
324
|
+
*/
|
|
325
|
+
open(partId, { where = 'tab' } = {}) {
|
|
326
|
+
if (!partId) return this;
|
|
327
|
+
if (this.isOpen(partId)) return this.focus(partId);
|
|
328
|
+
this._syncFromTree();
|
|
329
|
+
const next = openPart(this.area, partId, { where, activeGroupId: this.activeGroupId });
|
|
330
|
+
this.area = next.area;
|
|
331
|
+
this.activeGroupId = next.activeGroupId;
|
|
332
|
+
this._build();
|
|
333
|
+
this._changed('open');
|
|
334
|
+
return this;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Open beside the active group — the "split editor" gesture. */
|
|
338
|
+
openToSide(partId, side = 'right') { return this.open(partId, { where: side }); }
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Select an already-open view. Deliberately does not rebuild: it drives the
|
|
342
|
+
* live TabsNode, so pane scroll positions and the DOM survive a tab click.
|
|
343
|
+
*/
|
|
344
|
+
focus(partId) {
|
|
345
|
+
const found = findPart(this._syncFromTree(), partId);
|
|
346
|
+
if (!found) return this;
|
|
347
|
+
const live = this._liveGroup(found.group.id);
|
|
348
|
+
if (live) {
|
|
349
|
+
live.select(found.index, true);
|
|
350
|
+
this.activeGroupId = found.group.id;
|
|
351
|
+
this._markActiveGroup();
|
|
352
|
+
this._changed('focus');
|
|
353
|
+
return this;
|
|
354
|
+
}
|
|
355
|
+
const next = focusPart(this.area, partId, { activeGroupId: this.activeGroupId });
|
|
356
|
+
this.area = next.area;
|
|
357
|
+
this.activeGroupId = next.activeGroupId;
|
|
358
|
+
this._build();
|
|
359
|
+
this._changed('focus');
|
|
360
|
+
return this;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Close a view. Its group disappears when it held the last tab, and a split
|
|
365
|
+
* left with one child collapses into it — so closing back down to one view
|
|
366
|
+
* leaves a plain tab strip rather than a split with an empty half.
|
|
367
|
+
*/
|
|
368
|
+
close(partId) {
|
|
369
|
+
this._syncFromTree();
|
|
370
|
+
const next = closePart(this.area, partId, { activeGroupId: this.activeGroupId });
|
|
371
|
+
if (!next.closed) return this;
|
|
372
|
+
this.area = next.area;
|
|
373
|
+
this.activeGroupId = next.activeGroupId;
|
|
374
|
+
this._build();
|
|
375
|
+
this._changed('close');
|
|
376
|
+
return this;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Close everything, leaving one empty group. */
|
|
380
|
+
closeAll() {
|
|
381
|
+
for (const partId of this.openParts()) this.close(partId);
|
|
382
|
+
return this;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Replace the whole area — restoring a saved one, or switching perspective.
|
|
387
|
+
* Parts the new area does not reference are left in the registry pool, so
|
|
388
|
+
* switching back finds them as they were.
|
|
389
|
+
*/
|
|
390
|
+
setArea(area, activeGroupId) {
|
|
391
|
+
this.area = area ?? emptyWorkArea();
|
|
392
|
+
this.activeGroupId = activeGroupId ?? listGroups(this.area)[0]?.id ?? null;
|
|
393
|
+
this._build();
|
|
394
|
+
this._changed('set-area');
|
|
395
|
+
return this;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
dispose() {
|
|
399
|
+
this._tree?.dispose();
|
|
400
|
+
this._tree = null;
|
|
401
|
+
// Detached, not disposed — the outline is a pooled part like any other,
|
|
402
|
+
// and destroying it here would take it out of the registry for good.
|
|
403
|
+
this._outlinePart?.detach();
|
|
404
|
+
this._outlinePart = null;
|
|
405
|
+
this._placeholderEl = null;
|
|
406
|
+
this._slotPartId = null;
|
|
407
|
+
if (this.host) this.host.textContent = '';
|
|
408
|
+
if (this.actionsHost) this.actionsHost.textContent = '';
|
|
409
|
+
if (this.outlineHost) this.outlineHost.textContent = '';
|
|
410
|
+
this.host = null;
|
|
411
|
+
this.ctx = null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workAreaModel — the pure half of an IDE-style work area.
|
|
3
|
+
*
|
|
4
|
+
* A work area is a layout subtree made only of TABS GROUPS, optionally
|
|
5
|
+
* arranged by SPLITS: one or more tab strips, each holding several open parts,
|
|
6
|
+
* side by side or stacked. That is the difference between "one view at a time"
|
|
7
|
+
* and an IDE: a part being OPEN (a tab exists) is separate from it being
|
|
8
|
+
* VISIBLE (its tab is selected, in a group that is on screen), and two parts in
|
|
9
|
+
* different groups are visible at once.
|
|
10
|
+
*
|
|
11
|
+
* Everything here is a pure function over plain JSON — the same descriptors
|
|
12
|
+
* `createLayout()` consumes — so the interesting behaviour (where a new tab
|
|
13
|
+
* lands, what collapses when the last tab in a group closes) is testable
|
|
14
|
+
* without a DOM. `WorkArea` (workArea.js) is the thin rendering shell on top.
|
|
15
|
+
*
|
|
16
|
+
* group := { type:'tabs', id, active, closable, children:[{type:'part', partId}] }
|
|
17
|
+
* area := group | { type:'split', dir:'row'|'col', sizes, children:[area, …] }
|
|
18
|
+
*
|
|
19
|
+
* Only `tabs` nodes carry an `id`, because a group is the only thing anything
|
|
20
|
+
* needs to address: "open this beside the active group", "this group is now
|
|
21
|
+
* active". Splits are structure, and they come and go as groups are added and
|
|
22
|
+
* removed.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const SIDES = { right: 'row', left: 'row', below: 'col', above: 'col' };
|
|
26
|
+
|
|
27
|
+
const clone = (x) => (typeof structuredClone === 'function' ? structuredClone(x) : JSON.parse(JSON.stringify(x)));
|
|
28
|
+
|
|
29
|
+
const isGroup = (n) => n?.type === 'tabs';
|
|
30
|
+
const isSplit = (n) => n?.type === 'split';
|
|
31
|
+
|
|
32
|
+
/** A work area with one empty group — what an area collapses to when the last tab closes. */
|
|
33
|
+
export function emptyWorkArea(groupId = 'g1') {
|
|
34
|
+
return { type: 'tabs', id: groupId, active: 0, closable: true, children: [] };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Every tabs group in the area, in left-to-right/top-to-bottom order. */
|
|
38
|
+
export function listGroups(area) {
|
|
39
|
+
if (!area) return [];
|
|
40
|
+
if (isGroup(area)) return [area];
|
|
41
|
+
return (area.children ?? []).flatMap(listGroups);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Every open part id, in group order. */
|
|
45
|
+
export function listOpenParts(area) {
|
|
46
|
+
return listGroups(area).flatMap(g => (g.children ?? []).map(c => c.partId));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** `{ group, index }` for whichever group holds `partId`, or null. */
|
|
50
|
+
export function findPart(area, partId) {
|
|
51
|
+
for (const group of listGroups(area)) {
|
|
52
|
+
const index = (group.children ?? []).findIndex(c => c.partId === partId);
|
|
53
|
+
if (index >= 0) return { group, index };
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The group `groupId` names, else the first group, else null. */
|
|
59
|
+
export function resolveGroup(area, groupId) {
|
|
60
|
+
const groups = listGroups(area);
|
|
61
|
+
return groups.find(g => g.id === groupId) ?? groups[0] ?? null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** An id no existing group is using — `g1`, `g2`, … */
|
|
65
|
+
function nextGroupId(area) {
|
|
66
|
+
const taken = new Set(listGroups(area).map(g => g.id));
|
|
67
|
+
for (let i = 1; ; i++) {
|
|
68
|
+
const id = `g${i}`;
|
|
69
|
+
if (!taken.has(id)) return id;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Open `partId`.
|
|
75
|
+
*
|
|
76
|
+
* Already open anywhere -> focus it instead of opening a second copy: a part
|
|
77
|
+
* instance owns one DOM element (see Part), so the same part cannot be in two
|
|
78
|
+
* panes at once, and silently cloning the tab would leave one of them empty.
|
|
79
|
+
*
|
|
80
|
+
* `where`:
|
|
81
|
+
* 'tab' — another tab in the active group (default)
|
|
82
|
+
* 'right'|'left'|'below'|'above' — a NEW group beside the active one, which
|
|
83
|
+
* is what actually puts two parts on screen
|
|
84
|
+
* together
|
|
85
|
+
*
|
|
86
|
+
* Returns `{ area, activeGroupId, opened }` — `opened` false when it was
|
|
87
|
+
* already open, so a caller can tell a fresh open from a focus.
|
|
88
|
+
*/
|
|
89
|
+
export function openPart(area, partId, { where = 'tab', activeGroupId } = {}) {
|
|
90
|
+
const existing = findPart(area, partId);
|
|
91
|
+
if (existing) return focusPart(area, partId, { activeGroupId });
|
|
92
|
+
|
|
93
|
+
const next = clone(area ?? emptyWorkArea());
|
|
94
|
+
const target = resolveGroup(next, activeGroupId) ?? resolveGroup(next);
|
|
95
|
+
const partNode = { type: 'part', partId };
|
|
96
|
+
|
|
97
|
+
if (where === 'tab' || !SIDES[where]) {
|
|
98
|
+
target.children = [...(target.children ?? []), partNode];
|
|
99
|
+
target.active = target.children.length - 1;
|
|
100
|
+
return { area: next, activeGroupId: target.id, opened: true };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const dir = SIDES[where];
|
|
104
|
+
const before = where === 'left' || where === 'above';
|
|
105
|
+
const group = { type: 'tabs', id: nextGroupId(next), active: 0, closable: true, children: [partNode] };
|
|
106
|
+
const placed = _placeBeside(next, target, group, dir, before);
|
|
107
|
+
return { area: placed, activeGroupId: group.id, opened: true };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Put `group` next to `target`. When target's own parent split already runs in
|
|
112
|
+
* the same direction the group joins it as a sibling rather than nesting a
|
|
113
|
+
* second split inside it — otherwise opening three views to the right builds a
|
|
114
|
+
* right-leaning tower of two-child splits, which serializes badly and resizes
|
|
115
|
+
* worse (each splitter would only move two of the panes).
|
|
116
|
+
*/
|
|
117
|
+
function _placeBeside(area, target, group, dir, before) {
|
|
118
|
+
const walk = (node, parentDir) => {
|
|
119
|
+
if (node === target) {
|
|
120
|
+
// No parent split in this direction — wrap the target in a new one.
|
|
121
|
+
if (parentDir !== dir) {
|
|
122
|
+
return { type: 'split', dir, sizes: [null, null], children: before ? [group, node] : [node, group] };
|
|
123
|
+
}
|
|
124
|
+
return node; // handled by the parent split below
|
|
125
|
+
}
|
|
126
|
+
if (!isSplit(node)) return node;
|
|
127
|
+
|
|
128
|
+
const children = node.children ?? [];
|
|
129
|
+
const at = children.indexOf(target);
|
|
130
|
+
if (at >= 0 && node.dir === dir) {
|
|
131
|
+
const insertAt = before ? at : at + 1;
|
|
132
|
+
const nextChildren = [...children];
|
|
133
|
+
nextChildren.splice(insertAt, 0, group);
|
|
134
|
+
const sizes = (node.sizes ?? []).slice();
|
|
135
|
+
while (sizes.length < children.length) sizes.push(null);
|
|
136
|
+
sizes.splice(insertAt, 0, null);
|
|
137
|
+
return { ...node, children: nextChildren, sizes };
|
|
138
|
+
}
|
|
139
|
+
return { ...node, children: children.map(c => walk(c, node.dir)) };
|
|
140
|
+
};
|
|
141
|
+
return walk(area, null);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Close `partId`. An emptied group is removed, and a split left holding a
|
|
146
|
+
* single child collapses into that child — so closing back down to one view
|
|
147
|
+
* leaves a plain tab strip, not a split with an empty half. The area never
|
|
148
|
+
* disappears entirely: closing the last tab anywhere leaves one empty group.
|
|
149
|
+
*/
|
|
150
|
+
export function closePart(area, partId, { activeGroupId } = {}) {
|
|
151
|
+
const found = findPart(area, partId);
|
|
152
|
+
if (!found) return { area, activeGroupId, closed: false };
|
|
153
|
+
const closedGroupId = found.group.id;
|
|
154
|
+
|
|
155
|
+
const prune = (node) => {
|
|
156
|
+
if (isGroup(node)) {
|
|
157
|
+
const children = (node.children ?? []).filter(c => c.partId !== partId);
|
|
158
|
+
if (!children.length) return null;
|
|
159
|
+
const removedAt = (node.children ?? []).findIndex(c => c.partId === partId);
|
|
160
|
+
let active = node.active ?? 0;
|
|
161
|
+
// Keep the neighbour selected rather than jumping to tab 0 — closing
|
|
162
|
+
// the active tab should land on what was next to it.
|
|
163
|
+
if (removedAt >= 0 && removedAt < active) active -= 1;
|
|
164
|
+
return { ...node, children, active: Math.max(0, Math.min(active, children.length - 1)) };
|
|
165
|
+
}
|
|
166
|
+
if (!isSplit(node)) return node;
|
|
167
|
+
const sizes = (node.sizes ?? []).slice();
|
|
168
|
+
const kept = [];
|
|
169
|
+
const keptSizes = [];
|
|
170
|
+
(node.children ?? []).forEach((child, i) => {
|
|
171
|
+
const pruned = prune(child);
|
|
172
|
+
if (pruned) { kept.push(pruned); keptSizes.push(sizes[i] ?? null); }
|
|
173
|
+
});
|
|
174
|
+
if (!kept.length) return null;
|
|
175
|
+
if (kept.length === 1) return kept[0]; // collapse the split away
|
|
176
|
+
return { ...node, children: kept, sizes: keptSizes };
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const pruned = prune(clone(area));
|
|
180
|
+
const next = pruned ?? emptyWorkArea(closedGroupId);
|
|
181
|
+
const stillThere = listGroups(next).some(g => g.id === activeGroupId);
|
|
182
|
+
return {
|
|
183
|
+
area: next,
|
|
184
|
+
activeGroupId: stillThere ? activeGroupId : (listGroups(next)[0]?.id ?? null),
|
|
185
|
+
closed: true,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Select `partId`'s tab and make its group active. No-op if it isn't open. */
|
|
190
|
+
export function focusPart(area, partId, { activeGroupId } = {}) {
|
|
191
|
+
const found = findPart(area, partId);
|
|
192
|
+
if (!found) return { area, activeGroupId, opened: false };
|
|
193
|
+
const next = clone(area);
|
|
194
|
+
const group = listGroups(next).find(g => g.id === found.group.id);
|
|
195
|
+
group.active = (group.children ?? []).findIndex(c => c.partId === partId);
|
|
196
|
+
return { area: next, activeGroupId: group.id, opened: false };
|
|
197
|
+
}
|
package/src/workspace.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compatibility shim.
|
|
3
|
+
*
|
|
4
|
+
* This path used to hold a standalone prototype shell: a fixed five-region
|
|
5
|
+
* slot layout (navigation/context/workspace/details/tabs) with an EventBus,
|
|
6
|
+
* a hard-coded perspective state machine, and a plugin manager whose plugins
|
|
7
|
+
* never actually contributed any UI. It could not express either target
|
|
8
|
+
* consumer — sqlengine workbench's sidebar+main+tool-panels, or its 12-column
|
|
9
|
+
* dashboard grid — so it was replaced by the part/layout/context model in
|
|
10
|
+
* `src/workspace/`. See WORKSPACE_ARCHITECTURE.md.
|
|
11
|
+
*
|
|
12
|
+
* Kept as a re-export so `import '.../src/workspace.js'` still registers the
|
|
13
|
+
* `<ws-workspace>` element. Both files defining the same tag would otherwise
|
|
14
|
+
* collide, with whichever imported first silently winning.
|
|
15
|
+
*/
|
|
16
|
+
export * from './workspace/index.js';
|