@@ -717,6 +717,9 @@ document.addEventListener('DOMContentLoaded', function() {
717717 const nodeTypes = Array . from ( document . querySelectorAll ( '.node-filter:checked' ) ) . map ( el => el . value ) ;
718718 const linkTypes = Array . from ( document . querySelectorAll ( '.link-filter:checked' ) ) . map ( el => el . value ) ;
719719
720+ // Get namespace filters
721+ const namespaceFilters = Array . from ( document . querySelectorAll ( '.namespace-filter:checked' ) ) . map ( el => el . value ) ;
722+
720723 // Get usage filters (used/unused)
721724 const showUsed = document . getElementById ( 'filter-used' ) ?. checked ?? true ;
722725 const showUnused = document . getElementById ( 'filter-unused' ) ?. checked ?? true ;
@@ -728,10 +731,29 @@ document.addEventListener('DOMContentLoaded', function() {
728731 console . log ( "Active filters:" , {
729732 nodeTypes,
730733 linkTypes,
734+ namespaces : namespaceFilters ,
731735 usage : { showUsed, showUnused } ,
732736 origin : { showInternal, showExternal }
733737 } ) ;
734738
739+ // Helper function to check if a node belongs to a namespace
740+ const nodeInFilteredNamespace = ( nodeId ) => {
741+ // If no namespace filters are selected, show all nodes
742+ if ( namespaceFilters . length === 0 ) return true ;
743+
744+ // For namespace nodes, check direct match
745+ const node = graph . nodes . find ( n => n . id === nodeId ) ;
746+ if ( node && node . group === 'namespace' ) {
747+ return namespaceFilters . some ( ns => ns === nodeId ) ;
748+ }
749+
750+ // For other nodes, check if they belong to any of the filtered namespaces
751+ return namespaceFilters . some ( namespace => {
752+ // Check if the node ID starts with the namespace followed by a dot
753+ return nodeId === namespace || nodeId . startsWith ( namespace + '.' ) ;
754+ } ) ;
755+ } ;
756+
735757 // Apply node filters
736758 gContainer . selectAll ( '.node' ) . each ( function ( d ) {
737759 const node = d3 . select ( this ) ;
@@ -742,6 +764,11 @@ document.addEventListener('DOMContentLoaded', function() {
742764 visible = false ;
743765 }
744766
767+ // Check namespace filter
768+ if ( visible && ! nodeInFilteredNamespace ( d . id ) ) {
769+ visible = false ;
770+ }
771+
745772 // Check usage filter
746773 if ( visible ) {
747774 if ( d . used && ! showUsed ) visible = false ;
@@ -1116,6 +1143,130 @@ document.addEventListener('DOMContentLoaded', function() {
11161143 d3 . select ( event . currentTarget ) . classed ( 'hover' , false ) ;
11171144 }
11181145
1146+ // Initialize filters based on the graph data
1147+ function initializeFilters ( ) {
1148+ console . log ( "Initializing filters..." ) ;
1149+
1150+ // Extract all namespaces from the graph
1151+ const namespaces = new Set ( ) ;
1152+
1153+ // Extract namespace from node IDs
1154+ graph . nodes . forEach ( node => {
1155+ if ( node . group === 'namespace' ) {
1156+ namespaces . add ( node . id ) ;
1157+ } else {
1158+ // For non-namespace nodes, extract the namespace part
1159+ const parts = node . id . split ( '.' ) ;
1160+ if ( parts . length > 1 ) {
1161+ // Find the namespace by looking at the first part(s) of the ID
1162+ // We consider everything before the last part as potentially part of the namespace
1163+ const potentialNamespace = parts . slice ( 0 , - 1 ) . join ( '.' ) ;
1164+
1165+ // Check if this potential namespace exists as a node
1166+ const namespaceNode = graph . nodes . find ( n =>
1167+ n . group === 'namespace' && ( n . id === potentialNamespace || potentialNamespace . startsWith ( n . id + '.' ) )
1168+ ) ;
1169+
1170+ if ( namespaceNode ) {
1171+ namespaces . add ( namespaceNode . id ) ;
1172+ }
1173+ }
1174+ }
1175+ } ) ;
1176+
1177+ // Sort namespaces alphabetically
1178+ const sortedNamespaces = Array . from ( namespaces ) . sort ( ) ;
1179+
1180+ console . log ( "Found namespaces:" , sortedNamespaces ) ;
1181+
1182+ // Generate namespace filter checkboxes
1183+ const namespaceFiltersContainer = document . getElementById ( 'namespace-filters' ) ;
1184+ if ( namespaceFiltersContainer ) {
1185+ // Clear loading placeholder
1186+ namespaceFiltersContainer . innerHTML = '' ;
1187+
1188+ // Add "Select All" checkbox
1189+ const selectAllDiv = document . createElement ( 'div' ) ;
1190+ selectAllDiv . className = 'filter-item select-all' ;
1191+
1192+ const selectAllLabel = document . createElement ( 'label' ) ;
1193+
1194+ const selectAllCheckbox = document . createElement ( 'input' ) ;
1195+ selectAllCheckbox . type = 'checkbox' ;
1196+ selectAllCheckbox . className = 'namespace-select-all' ;
1197+ selectAllCheckbox . checked = true ;
1198+
1199+ const selectAllText = document . createElement ( 'span' ) ;
1200+ selectAllText . textContent = 'Select All' ;
1201+
1202+ selectAllLabel . appendChild ( selectAllCheckbox ) ;
1203+ selectAllLabel . appendChild ( selectAllText ) ;
1204+ selectAllDiv . appendChild ( selectAllLabel ) ;
1205+ namespaceFiltersContainer . appendChild ( selectAllDiv ) ;
1206+
1207+ // Add individual namespace checkboxes
1208+ sortedNamespaces . forEach ( namespace => {
1209+ const div = document . createElement ( 'div' ) ;
1210+ div . className = 'filter-item' ;
1211+
1212+ const label = document . createElement ( 'label' ) ;
1213+
1214+ const checkbox = document . createElement ( 'input' ) ;
1215+ checkbox . type = 'checkbox' ;
1216+ checkbox . className = 'namespace-filter' ;
1217+ checkbox . value = namespace ;
1218+ checkbox . checked = true ;
1219+ checkbox . setAttribute ( 'data-namespace' , namespace ) ;
1220+
1221+ // Extract the last part of the namespace for display
1222+ const displayName = namespace . split ( '.' ) . pop ( ) ;
1223+
1224+ const text = document . createElement ( 'span' ) ;
1225+ text . textContent = displayName ;
1226+ text . title = namespace ; // Show full namespace on hover
1227+
1228+ label . appendChild ( checkbox ) ;
1229+ label . appendChild ( text ) ;
1230+ div . appendChild ( label ) ;
1231+ namespaceFiltersContainer . appendChild ( div ) ;
1232+ } ) ;
1233+
1234+ // Set up event listeners for namespace filters
1235+ document . querySelectorAll ( '.namespace-filter' ) . forEach ( filter => {
1236+ filter . addEventListener ( 'change' , ( ) => {
1237+ console . log ( 'Namespace filter changed:' , filter . value , filter . checked ) ;
1238+ applyFilters ( ) ;
1239+
1240+ // Update the "Select All" checkbox state
1241+ updateSelectAllCheckbox ( ) ;
1242+ } ) ;
1243+ } ) ;
1244+
1245+ // Set up the "Select All" checkbox event listener
1246+ document . querySelector ( '.namespace-select-all' ) ?. addEventListener ( 'change' , function ( ) {
1247+ const isChecked = this . checked ;
1248+ document . querySelectorAll ( '.namespace-filter' ) . forEach ( checkbox => {
1249+ checkbox . checked = isChecked ;
1250+ } ) ;
1251+ applyFilters ( ) ;
1252+ } ) ;
1253+ }
1254+ }
1255+
1256+ // Update the "Select All" checkbox based on individual checkbox states
1257+ function updateSelectAllCheckbox ( ) {
1258+ const selectAllCheckbox = document . querySelector ( '.namespace-select-all' ) ;
1259+ const namespaceCheckboxes = document . querySelectorAll ( '.namespace-filter' ) ;
1260+
1261+ if ( selectAllCheckbox && namespaceCheckboxes . length > 0 ) {
1262+ const allChecked = Array . from ( namespaceCheckboxes ) . every ( checkbox => checkbox . checked ) ;
1263+ const someChecked = Array . from ( namespaceCheckboxes ) . some ( checkbox => checkbox . checked ) ;
1264+
1265+ selectAllCheckbox . checked = allChecked ;
1266+ selectAllCheckbox . indeterminate = ! allChecked && someChecked ;
1267+ }
1268+ }
1269+
11191270 // Add CSS styles for proper visualization
11201271 function addStyles ( ) {
11211272 const styleElement = document . createElement ( 'style' ) ;
@@ -1234,13 +1385,63 @@ document.addEventListener('DOMContentLoaded', function() {
12341385 .unused {
12351386 color: var(--unused-color);
12361387 }
1388+
1389+ /* Namespace filter styles */
1390+ #namespace-filters {
1391+ max-height: 200px;
1392+ overflow-y: auto;
1393+ margin-bottom: 15px;
1394+ border: 1px solid #eee;
1395+ border-radius: 4px;
1396+ padding: 5px;
1397+ }
1398+
1399+ .loading-placeholder {
1400+ font-style: italic;
1401+ color: #999;
1402+ text-align: center;
1403+ padding: 10px;
1404+ }
1405+
1406+ .filter-item {
1407+ margin: 5px 0;
1408+ }
1409+
1410+ .filter-item label {
1411+ display: flex;
1412+ align-items: center;
1413+ }
1414+
1415+ .select-all {
1416+ font-weight: bold;
1417+ border-bottom: 1px solid #eee;
1418+ padding-bottom: 5px;
1419+ margin-bottom: 8px;
1420+ }
1421+
1422+ /* Reset button styling */
1423+ #reset-filters {
1424+ margin-left: 10px;
1425+ font-size: 0.7em;
1426+ padding: 3px 8px;
1427+ background: #f5f5f5;
1428+ border: 1px solid #ddd;
1429+ border-radius: 3px;
1430+ cursor: pointer;
1431+ transition: all 0.2s;
1432+ }
1433+
1434+ #reset-filters:hover {
1435+ background: #e0e0e0;
1436+ }
12371437 ` ;
12381438
12391439 document . head . appendChild ( styleElement ) ;
12401440 }
12411441
12421442 // Initialize the visualization
12431443 initVisualization ( ) ;
1444+ initializeFilters ( ) ;
12441445 addStyles ( ) ;
12451446} ) ;
12461447
@@ -1282,3 +1483,56 @@ document.addEventListener('DOMContentLoaded', function() {
12821483
12831484 console . log ( "Additional event listeners setup complete" ) ;
12841485} ) ;
1486+
1487+ // Add event listener for reset filters button
1488+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
1489+ console . log ( "Setting up reset filters button" ) ;
1490+
1491+ // Add reset filters functionality
1492+ document . getElementById ( 'reset-filters' ) ?. addEventListener ( 'click' , function ( ) {
1493+ console . log ( "Reset filters button clicked" ) ;
1494+ resetAllFilters ( ) ;
1495+ } ) ;
1496+ } ) ;
1497+
1498+ // Function to reset all filters to default state
1499+ function resetAllFilters ( ) {
1500+ console . log ( "Resetting all filters to defaults" ) ;
1501+
1502+ // Reset node type filters
1503+ document . querySelectorAll ( '.node-filter' ) . forEach ( checkbox => {
1504+ checkbox . checked = true ;
1505+ } ) ;
1506+
1507+ // Reset link type filters
1508+ document . querySelectorAll ( '.link-filter' ) . forEach ( checkbox => {
1509+ checkbox . checked = true ;
1510+ } ) ;
1511+
1512+ // Reset namespace filters
1513+ document . querySelectorAll ( '.namespace-filter' ) . forEach ( checkbox => {
1514+ checkbox . checked = true ;
1515+ } ) ;
1516+
1517+ // Reset "Select All" namespace checkbox
1518+ const selectAllCheckbox = document . querySelector ( '.namespace-select-all' ) ;
1519+ if ( selectAllCheckbox ) {
1520+ selectAllCheckbox . checked = true ;
1521+ selectAllCheckbox . indeterminate = false ;
1522+ }
1523+
1524+ // Reset usage filters
1525+ document . getElementById ( 'filter-used' ) . checked = true ;
1526+ document . getElementById ( 'filter-unused' ) . checked = true ;
1527+
1528+ // Reset library filters
1529+ document . getElementById ( 'filter-internal' ) . checked = true ;
1530+ document . getElementById ( 'filter-external' ) . checked = true ;
1531+
1532+ // Apply filter changes
1533+ if ( typeof applyFilters === 'function' ) {
1534+ applyFilters ( ) ;
1535+ }
1536+
1537+ console . log ( "All filters have been reset" ) ;
1538+ }
0 commit comments