dbdoc_engine 0.1.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.
- checksums.yaml +7 -0
- data/README.md +331 -0
- data/Rakefile +8 -0
- data/app/assets/builds/dbdoc_engine/application.css +5 -0
- data/app/assets/images/dbdoc_engine/arrowdown.svg +3 -0
- data/app/assets/images/dbdoc_engine/arrowhorizontal.svg +3 -0
- data/app/assets/images/dbdoc_engine/arrowleft.svg +3 -0
- data/app/assets/images/dbdoc_engine/changelog.svg +3 -0
- data/app/assets/images/dbdoc_engine/column_stats_dbdocs.svg +23 -0
- data/app/assets/images/dbdoc_engine/diagram.svg +3 -0
- data/app/assets/images/dbdoc_engine/double_arrow.svg +4 -0
- data/app/assets/images/dbdoc_engine/group_bu.svg +3 -0
- data/app/assets/images/dbdoc_engine/japan_circle.png +0 -0
- data/app/assets/images/dbdoc_engine/log_in_image.png +0 -0
- data/app/assets/images/dbdoc_engine/logo.svg +12 -0
- data/app/assets/images/dbdoc_engine/orange_changelog.svg +3 -0
- data/app/assets/images/dbdoc_engine/orange_fields.svg +23 -0
- data/app/assets/images/dbdoc_engine/orange_logo.svg +12 -0
- data/app/assets/images/dbdoc_engine/orange_table.svg +21 -0
- data/app/assets/images/dbdoc_engine/orange_updates.svg +43 -0
- data/app/assets/images/dbdoc_engine/orange_wiki.svg +3 -0
- data/app/assets/images/dbdoc_engine/search.svg +3 -0
- data/app/assets/images/dbdoc_engine/setting.svg +3 -0
- data/app/assets/images/dbdoc_engine/table_dbdocs.svg +21 -0
- data/app/assets/images/dbdoc_engine/uk_circle_transparent.png +0 -0
- data/app/assets/images/dbdoc_engine/update_stats_dbdocs.svg +43 -0
- data/app/assets/images/dbdoc_engine/wiki.svg +3 -0
- data/app/assets/stylesheets/dbdoc_engine/admin.css +176 -0
- data/app/assets/stylesheets/dbdoc_engine/admin_header.css +179 -0
- data/app/assets/stylesheets/dbdoc_engine/application.scss +1 -0
- data/app/assets/stylesheets/dbdoc_engine/changelog.css +173 -0
- data/app/assets/stylesheets/dbdoc_engine/dashboard.css +513 -0
- data/app/assets/stylesheets/dbdoc_engine/dbdoc_application.css +117 -0
- data/app/assets/stylesheets/dbdoc_engine/ecommerce.css +253 -0
- data/app/assets/stylesheets/dbdoc_engine/group_details.css +178 -0
- data/app/assets/stylesheets/dbdoc_engine/header.css +212 -0
- data/app/assets/stylesheets/dbdoc_engine/loading_spinner.css +127 -0
- data/app/assets/stylesheets/dbdoc_engine/login.css +213 -0
- data/app/assets/stylesheets/dbdoc_engine/schema_diagram.css +149 -0
- data/app/assets/stylesheets/dbdoc_engine/sidebar.css +296 -0
- data/app/assets/stylesheets/dbdoc_engine/table_details.css +417 -0
- data/app/controllers/dbdoc_engine/admin/base_controller.rb +23 -0
- data/app/controllers/dbdoc_engine/admin/dashboard_controller.rb +16 -0
- data/app/controllers/dbdoc_engine/admin/data_transfer_controller.rb +63 -0
- data/app/controllers/dbdoc_engine/admin/db_design_dynamic_tables_controller.rb +198 -0
- data/app/controllers/dbdoc_engine/admin/db_design_table_groups_controller.rb +107 -0
- data/app/controllers/dbdoc_engine/application_controller.rb +65 -0
- data/app/controllers/dbdoc_engine/concerns/internationalization.rb +57 -0
- data/app/controllers/dbdoc_engine/db_doc_sessions_controller.rb +33 -0
- data/app/controllers/dbdoc_engine/home_controller.rb +79 -0
- data/app/controllers/dbdoc_engine/schema_diagram_controller.rb +293 -0
- data/app/helper/dbdoc_engine/application_helper.rb +35 -0
- data/app/helpers/dbdoc_engine/application_helper.rb +4 -0
- data/app/helpers/dbdoc_engine/changelogs_helper.rb +27 -0
- data/app/helpers/dbdoc_engine/column_helper.rb +30 -0
- data/app/helpers/dbdoc_engine/db_design_dynamic_tables_helper.rb +15 -0
- data/app/helpers/dbdoc_engine/home_helper.rb +75 -0
- data/app/javascript/dbdoc_engine/application.js +12 -0
- data/app/javascript/dbdoc_engine/controllers/application.js +29 -0
- data/app/javascript/dbdoc_engine/controllers/auto_submit_controller.js +17 -0
- data/app/javascript/dbdoc_engine/controllers/chart_controller.js +58 -0
- data/app/javascript/dbdoc_engine/controllers/column-type_controller.js +149 -0
- data/app/javascript/dbdoc_engine/controllers/column_controller.js +362 -0
- data/app/javascript/dbdoc_engine/controllers/column_search_controller.js +42 -0
- data/app/javascript/dbdoc_engine/controllers/dbdoc_accordion_controller.js +42 -0
- data/app/javascript/dbdoc_engine/controllers/ecommerce_controller.js +73 -0
- data/app/javascript/dbdoc_engine/controllers/group_details_controller.js +88 -0
- data/app/javascript/dbdoc_engine/controllers/import_export_controller.js +200 -0
- data/app/javascript/dbdoc_engine/controllers/index.js +9 -0
- data/app/javascript/dbdoc_engine/controllers/language_controller.js +100 -0
- data/app/javascript/dbdoc_engine/controllers/loading_spinner_controller.js +48 -0
- data/app/javascript/dbdoc_engine/controllers/login_controller.js +75 -0
- data/app/javascript/dbdoc_engine/controllers/notification_controller.js +15 -0
- data/app/javascript/dbdoc_engine/controllers/schema_diagram_controller.js +1129 -0
- data/app/javascript/dbdoc_engine/controllers/select2_controller.js +67 -0
- data/app/javascript/dbdoc_engine/controllers/sidebar_controller.js +943 -0
- data/app/javascript/dbdoc_engine/controllers/table_details_controller.js +245 -0
- data/app/javascript/dbdoc_engine/controllers/table_group_validation_controller.js +148 -0
- data/app/javascript/dbdoc_engine/controllers/table_validation_controller.js +423 -0
- data/app/jobs/dbdoc_engine/application_job.rb +4 -0
- data/app/mailers/dbdoc_engine/application_mailer.rb +6 -0
- data/app/models/dbdoc_engine/application_record.rb +6 -0
- data/app/models/dbdoc_engine/concerns/soft_deletable.rb +30 -0
- data/app/models/dbdoc_engine/db_design_changelog.rb +44 -0
- data/app/models/dbdoc_engine/db_design_dynamic_column.rb +211 -0
- data/app/models/dbdoc_engine/db_design_dynamic_table.rb +124 -0
- data/app/models/dbdoc_engine/db_design_table_group.rb +88 -0
- data/app/models/dbdoc_engine/user.rb +21 -0
- data/app/queries/dbdoc_engine/admin_dashboard_queries.rb +71 -0
- data/app/queries/dbdoc_engine/db_design_changelog_queries.rb +68 -0
- data/app/queries/dbdoc_engine/db_design_dynamic_column_queries.rb +37 -0
- data/app/queries/dbdoc_engine/db_design_dynamic_table_commands.rb +106 -0
- data/app/queries/dbdoc_engine/db_design_dynamic_table_queries.rb +194 -0
- data/app/queries/dbdoc_engine/db_design_table_group_queries.rb +154 -0
- data/app/services/dbdoc_engine/db_design_dynamic_table_export_service.rb +38 -0
- data/app/services/dbdoc_engine/db_design_dynamic_table_handler_service.rb +49 -0
- data/app/services/dbdoc_engine/db_design_dynamic_tables_service.rb +21 -0
- data/app/services/dbdoc_engine/error_handler_service.rb +43 -0
- data/app/services/dbdoc_engine/schema_rb_import_service.rb +194 -0
- data/app/services/dbdoc_engine/schema_rb_parser_service.rb +339 -0
- data/app/services/dbdoc_engine/table_filter_service.rb +35 -0
- data/app/services/dbdoc_engine/table_groups_service.rb +199 -0
- data/app/services/dbdoc_engine/table_management_service.rb +192 -0
- data/app/views/dbdoc_engine/admin/dashboard/_action_badge.html.erb +11 -0
- data/app/views/dbdoc_engine/admin/dashboard/_changelog_rows.html.erb +22 -0
- data/app/views/dbdoc_engine/admin/dashboard/_changelog_table_headers.html.erb +8 -0
- data/app/views/dbdoc_engine/admin/dashboard/_filter_fields.html.erb +43 -0
- data/app/views/dbdoc_engine/admin/dashboard/index.html.erb +159 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_column_fields.html.erb +225 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_deleted_table_index.html.erb +110 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_foreign_key_fields.html.erb +51 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_form.html.erb +75 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_recent_activity.html.erb +39 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_columns.html.erb +127 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_index.html.erb +109 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_information.html.erb +99 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/deleted_tables.html.erb +95 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/edit.html.erb +23 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/export_all_to_excel.xlsx.axlsx +240 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/export_to_excel.xlsx.axlsx +135 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/index.html.erb +109 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/new.html.erb +25 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/show_table_info.html.erb +125 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/_deleted_table_groups_list.html.erb +75 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/_form.html.erb +88 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/_table_groups_list.html.erb +82 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/deleted_groups.html.erb +60 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/edit.html.erb +25 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/index.html.erb +85 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/new.html.erb +26 -0
- data/app/views/dbdoc_engine/db_doc_sessions/new.html.erb +59 -0
- data/app/views/dbdoc_engine/home/changelog_details.html.erb +80 -0
- data/app/views/dbdoc_engine/home/changelogs.html.erb +20 -0
- data/app/views/dbdoc_engine/home/group_details.html.erb +94 -0
- data/app/views/dbdoc_engine/home/index.html.erb +11 -0
- data/app/views/dbdoc_engine/home/partials/_action_badge.html.erb +11 -0
- data/app/views/dbdoc_engine/home/partials/_breadcrumb_navigation.html.erb +30 -0
- data/app/views/dbdoc_engine/home/partials/_changelog_rows.html.erb +35 -0
- data/app/views/dbdoc_engine/home/partials/_changelog_table_headers.html.erb +16 -0
- data/app/views/dbdoc_engine/home/partials/_column_headers.html.erb +23 -0
- data/app/views/dbdoc_engine/home/partials/_column_row.html.erb +157 -0
- data/app/views/dbdoc_engine/home/partials/_filter_form.html.erb +47 -0
- data/app/views/dbdoc_engine/home/partials/_group_section.html.erb +84 -0
- data/app/views/dbdoc_engine/home/partials/_pagination.html.erb +5 -0
- data/app/views/dbdoc_engine/home/partials/_stats_container.html.erb +46 -0
- data/app/views/dbdoc_engine/home/partials/_table_groups.html.erb +7 -0
- data/app/views/dbdoc_engine/home/partials/_table_information_section.html.erb +50 -0
- data/app/views/dbdoc_engine/home/partials/_table_section.html.erb +48 -0
- data/app/views/dbdoc_engine/home/table_details.html.erb +9 -0
- data/app/views/dbdoc_engine/schema_diagram/index.html.erb +102 -0
- data/app/views/dbdoc_engine/shared/_admin_header.html.erb +78 -0
- data/app/views/dbdoc_engine/shared/_header.html.erb +94 -0
- data/app/views/dbdoc_engine/shared/_js_translations.html.erb +3 -0
- data/app/views/dbdoc_engine/shared/_language_button.html.erb +14 -0
- data/app/views/dbdoc_engine/shared/_sidebar.html.erb +128 -0
- data/app/views/kaminari/dbdoc_engine/_first_page.html.erb +3 -0
- data/app/views/kaminari/dbdoc_engine/_gap.html.erb +3 -0
- data/app/views/kaminari/dbdoc_engine/_last_page.html.erb +3 -0
- data/app/views/kaminari/dbdoc_engine/_next_page.html.erb +3 -0
- data/app/views/kaminari/dbdoc_engine/_page.html.erb +9 -0
- data/app/views/kaminari/dbdoc_engine/_paginator.html.erb +17 -0
- data/app/views/kaminari/dbdoc_engine/_prev_page.html.erb +3 -0
- data/app/views/layouts/dbdoc_engine/application.html.erb +107 -0
- data/app/views/layouts/dbdoc_engine/header.html.erb +108 -0
- data/config/importmap.rb +11 -0
- data/config/locales/en.yml +307 -0
- data/config/locales/ja.yml +306 -0
- data/config/routes.rb +73 -0
- data/db/migrate/rails7/20250227060610_create_db_design_table_groups.rb +15 -0
- data/db/migrate/rails7/20250227094626_create_db_design_dynamic_tables.rb +19 -0
- data/db/migrate/rails7/20250228022732_create_db_design_dynamic_columns.rb +34 -0
- data/db/migrate/rails7/20250401051453_create_db_design_changelogs.rb +26 -0
- data/db/migrate/rails7/20250411040822_create_users.rb +14 -0
- data/db/migrate/rails7/20250421080851_add_missing_indexes_to_dbdoc_tables.rb +23 -0
- data/db/migrate/rails8/20250227060610_create_db_design_table_groups.rb +15 -0
- data/db/migrate/rails8/20250227094626_create_db_design_dynamic_tables.rb +19 -0
- data/db/migrate/rails8/20250228022732_create_db_design_dynamic_columns.rb +34 -0
- data/db/migrate/rails8/20250401051453_create_db_design_changelogs.rb +26 -0
- data/db/migrate/rails8/20250411040822_create_users.rb +14 -0
- data/db/migrate/rails8/20250421080851_add_missing_indexes_to_dbdoc_tables.rb +23 -0
- data/db/seeds.rb +28 -0
- data/lib/dbdoc_engine/engine.rb +57 -0
- data/lib/dbdoc_engine/version.rb +3 -0
- data/lib/dbdoc_engine.rb +9 -0
- data/lib/generators/dbdoc_engine/install/install_generator.rb +245 -0
- data/lib/generators/dbdoc_engine/uninstall/uninstall_generator.rb +196 -0
- data/lib/tasks/dbdoc_engine_tasks.rake +44 -0
- data/public/dbdoc_engine_assets/images/camel_chess_head.png +0 -0
- data/public/dbdoc_engine_assets/images/dblogo.svg +4 -0
- data/public/dbdoc_engine_assets/images/japan_circle.png +0 -0
- data/public/dbdoc_engine_assets/images/king_chess_head.png +0 -0
- data/public/dbdoc_engine_assets/images/login-bg.svg +44 -0
- data/public/dbdoc_engine_assets/images/logo.png +0 -0
- data/public/dbdoc_engine_assets/images/logo.svg +12 -0
- data/public/dbdoc_engine_assets/images/queen_chess_head.png +0 -0
- data/public/dbdoc_engine_assets/images/soldier_chess_headd.png +0 -0
- data/public/dbdoc_engine_assets/images/uk_circle_transparent.png +0 -0
- metadata +415 -0
|
@@ -0,0 +1,943 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sidebar Controller
|
|
5
|
+
* Manages the sidebar functionality including resizing, collapsing,
|
|
6
|
+
* searching, and navigation between groups and tables.
|
|
7
|
+
*/
|
|
8
|
+
export default class extends Controller {
|
|
9
|
+
// Define controller targets
|
|
10
|
+
static targets = ['sidebar', 'searchInput', 'groupList', 'toggleButton', 'resizer'];
|
|
11
|
+
|
|
12
|
+
// Define controller values for image paths
|
|
13
|
+
static values = {
|
|
14
|
+
arrowDownPath: String,
|
|
15
|
+
arrowHorizontalPath: String
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Connect method - runs when controller is connected to the DOM
|
|
20
|
+
* Initializes sidebar functionality
|
|
21
|
+
*/
|
|
22
|
+
connect() {
|
|
23
|
+
// Get arrow image paths from data attributes
|
|
24
|
+
this.arrowDownPathValue = this.element.dataset.arrowDownPath || '/assets/arrowdown.svg';
|
|
25
|
+
this.arrowHorizontalPathValue = this.element.dataset.arrowHorizontalPath || '/assets/arrowhorizontal.svg';
|
|
26
|
+
|
|
27
|
+
// Initialize sidebar state
|
|
28
|
+
this.isExpanded = false;
|
|
29
|
+
|
|
30
|
+
// Setup sidebar components and functionality
|
|
31
|
+
this.setupResizableSidebar();
|
|
32
|
+
this.storeOriginalContent();
|
|
33
|
+
this.hasStoredOriginalContent = true;
|
|
34
|
+
this.setupItemSelection();
|
|
35
|
+
this.restoreSidebarState();
|
|
36
|
+
this.setActiveItemFromCurrentPath();
|
|
37
|
+
this.setupTooltips();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sets up click handlers for selecting items in the sidebar
|
|
42
|
+
* Manages active state of items and stores selection in localStorage
|
|
43
|
+
*/
|
|
44
|
+
setupItemSelection() {
|
|
45
|
+
// Add click handlers for all clickable items in sidebar
|
|
46
|
+
document.querySelectorAll('.group-row, .table-row, .column-row').forEach(row => {
|
|
47
|
+
row.addEventListener('click', (event) => {
|
|
48
|
+
// Remove active class from all items
|
|
49
|
+
document.querySelectorAll('.sidebar-item-active').forEach(activeItem => {
|
|
50
|
+
activeItem.classList.remove('sidebar-item-active');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Add active class to clicked row
|
|
54
|
+
row.classList.add('sidebar-item-active');
|
|
55
|
+
|
|
56
|
+
// Store the active item ID in localStorage for persistence
|
|
57
|
+
const parentElement = row.closest('.group, .db-table, .column');
|
|
58
|
+
if (parentElement) {
|
|
59
|
+
const itemId = parentElement.dataset.groupId ||
|
|
60
|
+
parentElement.dataset.tableId ||
|
|
61
|
+
(parentElement.querySelector('.column-name') ?
|
|
62
|
+
parentElement.querySelector('.column-name').dataset.physicalColumnName : '');
|
|
63
|
+
|
|
64
|
+
if (itemId) {
|
|
65
|
+
localStorage.setItem('activeItemId', itemId);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Store sidebar state before navigation
|
|
70
|
+
this.storeSidebarState();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Highlights the active item based on current URL path
|
|
77
|
+
* This handles direct navigation to specific pages
|
|
78
|
+
*/
|
|
79
|
+
setActiveItemFromCurrentPath() {
|
|
80
|
+
const currentPath = window.location.pathname;
|
|
81
|
+
|
|
82
|
+
// Check if we're on the root path
|
|
83
|
+
// Define paths where nothing should be active
|
|
84
|
+
const noActiveSidebarRoutes = [
|
|
85
|
+
'/dbdoc/',
|
|
86
|
+
'/dbdoc/changelogs'
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
// Check if current path matches any "no active" route, including subpages
|
|
90
|
+
const isNoActiveSidebar = noActiveSidebarRoutes.some(route =>
|
|
91
|
+
currentPath === route || currentPath.startsWith(route + '/')
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (isNoActiveSidebar || currentPath === '') {
|
|
95
|
+
// Clear any active highlighting...
|
|
96
|
+
document.querySelectorAll('.sidebar-item-active').forEach(item => {
|
|
97
|
+
item.classList.remove('sidebar-item-active');
|
|
98
|
+
});
|
|
99
|
+
// Also clear the active item from localStorage
|
|
100
|
+
localStorage.removeItem('activeItemId');
|
|
101
|
+
return; // Exit the method early
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Match group details path
|
|
105
|
+
const groupMatch = currentPath.match(/\/group_details\/(\d+)/);
|
|
106
|
+
const tableMatch = currentPath.match(/\/table_details\/(\d+)/);
|
|
107
|
+
|
|
108
|
+
if (groupMatch) {
|
|
109
|
+
// Highlight the matching group
|
|
110
|
+
const groupId = groupMatch[1];
|
|
111
|
+
const groupItem = document.querySelector(`.group[data-group-id="${groupId}"]`);
|
|
112
|
+
if (groupItem && groupItem.querySelector('.group-row')) {
|
|
113
|
+
groupItem.querySelector('.group-row').classList.add('sidebar-item-active');
|
|
114
|
+
}
|
|
115
|
+
} else if (tableMatch) {
|
|
116
|
+
// Highlight the matching table
|
|
117
|
+
const tableId = tableMatch[1];
|
|
118
|
+
const tableItem = document.querySelector(`.db-table[data-table-id="${tableId}"]`);
|
|
119
|
+
if (tableItem && tableItem.querySelector('.table-row')) {
|
|
120
|
+
tableItem.querySelector('.table-row').classList.add('sidebar-item-active');
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
// Restore active item from localStorage if no match in URL
|
|
124
|
+
const activeItemId = localStorage.getItem('activeItemId');
|
|
125
|
+
if (activeItemId) {
|
|
126
|
+
const groupWithId = document.querySelector(`.group[data-group-id="${activeItemId}"]`);
|
|
127
|
+
const tableWithId = document.querySelector(`.db-table[data-table-id="${activeItemId}"]`);
|
|
128
|
+
|
|
129
|
+
if (groupWithId && groupWithId.querySelector('.group-row')) {
|
|
130
|
+
groupWithId.querySelector('.group-row').classList.add('sidebar-item-active');
|
|
131
|
+
} else if (tableWithId && tableWithId.querySelector('.table-row')) {
|
|
132
|
+
tableWithId.querySelector('.table-row').classList.add('sidebar-item-active');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Stores the current state of the sidebar in localStorage
|
|
140
|
+
* Includes sidebar width, collapsed state, and expanded items
|
|
141
|
+
*/
|
|
142
|
+
storeSidebarState() {
|
|
143
|
+
// Store whether sidebar is closed
|
|
144
|
+
localStorage.setItem('sidebarClosed', this.element.classList.contains('closed'));
|
|
145
|
+
|
|
146
|
+
// Store sidebar width
|
|
147
|
+
localStorage.setItem('sidebarWidth', this.element.style.width || '250px');
|
|
148
|
+
|
|
149
|
+
// Store expanded groups
|
|
150
|
+
const expandedGroups = [];
|
|
151
|
+
document.querySelectorAll('.group').forEach(group => {
|
|
152
|
+
const tableList = group.querySelector('.table-list');
|
|
153
|
+
if (tableList && tableList.classList.contains('visible')) {
|
|
154
|
+
const groupId = group.dataset.groupId;
|
|
155
|
+
if (groupId) expandedGroups.push(groupId);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
localStorage.setItem('expandedGroups', JSON.stringify(expandedGroups));
|
|
159
|
+
|
|
160
|
+
// Store expanded tables
|
|
161
|
+
const expandedTables = [];
|
|
162
|
+
document.querySelectorAll('.db-table').forEach(table => {
|
|
163
|
+
const columnList = table.querySelector('.column-list');
|
|
164
|
+
if (columnList && columnList.classList.contains('visible')) {
|
|
165
|
+
const tableId = table.dataset.tableId;
|
|
166
|
+
if (tableId) expandedTables.push(tableId);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
localStorage.setItem('expandedTables', JSON.stringify(expandedTables));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Restores the sidebar state from localStorage
|
|
174
|
+
* Applied when the page is loaded
|
|
175
|
+
*/
|
|
176
|
+
restoreSidebarState() {
|
|
177
|
+
// Restore sidebar closed state
|
|
178
|
+
const sidebarClosed = localStorage.getItem('sidebarClosed') === 'true';
|
|
179
|
+
if (sidebarClosed) {
|
|
180
|
+
this.element.classList.add('closed');
|
|
181
|
+
} else {
|
|
182
|
+
this.element.classList.remove('closed');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Restore sidebar width
|
|
186
|
+
const sidebarWidth = localStorage.getItem('sidebarWidth');
|
|
187
|
+
if (sidebarWidth) {
|
|
188
|
+
this.element.style.width = sidebarWidth;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Restore expanded groups
|
|
192
|
+
try {
|
|
193
|
+
const expandedGroups = JSON.parse(localStorage.getItem('expandedGroups') || '[]');
|
|
194
|
+
expandedGroups.forEach(groupId => {
|
|
195
|
+
const group = document.querySelector(`.group[data-group-id="${groupId}"]`);
|
|
196
|
+
if (group) {
|
|
197
|
+
const tableList = group.querySelector('.table-list');
|
|
198
|
+
if (tableList) {
|
|
199
|
+
tableList.classList.add('visible');
|
|
200
|
+
tableList.classList.remove('hidden');
|
|
201
|
+
|
|
202
|
+
// Update arrow rotation
|
|
203
|
+
const groupToggle = group.querySelector('.group-toggle svg');
|
|
204
|
+
if (groupToggle) {
|
|
205
|
+
groupToggle.style.transform = 'rotate(90deg)';
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
} catch (e) {
|
|
211
|
+
console.error('Error restoring expanded groups:', e);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Restore expanded tables
|
|
215
|
+
try {
|
|
216
|
+
const expandedTables = JSON.parse(localStorage.getItem('expandedTables') || '[]');
|
|
217
|
+
expandedTables.forEach(tableId => {
|
|
218
|
+
const table = document.querySelector(`.db-table[data-table-id="${tableId}"]`);
|
|
219
|
+
if (table) {
|
|
220
|
+
const columnList = table.querySelector('.column-list');
|
|
221
|
+
if (columnList) {
|
|
222
|
+
columnList.classList.add('visible');
|
|
223
|
+
columnList.classList.remove('hidden');
|
|
224
|
+
|
|
225
|
+
// Update arrow rotation
|
|
226
|
+
const tableToggle = table.querySelector('.table-toggle svg');
|
|
227
|
+
if (tableToggle) {
|
|
228
|
+
tableToggle.style.transform = 'rotate(90deg)';
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
} catch (e) {
|
|
234
|
+
console.error('Error restoring expanded tables:', e);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Sets up resizable sidebar functionality
|
|
240
|
+
* Allows user to adjust sidebar width by dragging
|
|
241
|
+
*/
|
|
242
|
+
setupResizableSidebar() {
|
|
243
|
+
const sidebar = this.element;
|
|
244
|
+
const resizer = this.resizerTarget;
|
|
245
|
+
|
|
246
|
+
let startX = 0;
|
|
247
|
+
let startWidth = 0;
|
|
248
|
+
|
|
249
|
+
// Function that starts the resize process
|
|
250
|
+
const startResize = (e) => {
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
startX = e.clientX;
|
|
253
|
+
startWidth = parseInt(document.defaultView.getComputedStyle(sidebar).width, 10);
|
|
254
|
+
document.addEventListener('mousemove', resize);
|
|
255
|
+
document.addEventListener('mouseup', stopResize);
|
|
256
|
+
document.body.style.cursor = 'ew-resize';
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Function that updates the sidebar width during resize
|
|
260
|
+
const resize = (e) => {
|
|
261
|
+
const newWidth = startWidth + (e.clientX - startX);
|
|
262
|
+
// Limit sidebar width within reasonable bounds
|
|
263
|
+
if (newWidth > 300 && newWidth < 500) {
|
|
264
|
+
sidebar.style.width = `${newWidth}px`;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// Function that ends the resize process
|
|
269
|
+
const stopResize = () => {
|
|
270
|
+
document.removeEventListener('mousemove', resize);
|
|
271
|
+
document.removeEventListener('mouseup', stopResize);
|
|
272
|
+
document.body.style.cursor = '';
|
|
273
|
+
|
|
274
|
+
// Store sidebar state after resizing
|
|
275
|
+
this.storeSidebarState();
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// Add event listener to start resizing
|
|
279
|
+
resizer.addEventListener('mousedown', startResize);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Toggles the sidebar between expanded and collapsed states
|
|
284
|
+
* Maintains toggle button and resizer visibility
|
|
285
|
+
*/
|
|
286
|
+
toggleSidebar() {
|
|
287
|
+
const sidebar = this.element;
|
|
288
|
+
const toggleButton = this.toggleButtonTarget;
|
|
289
|
+
|
|
290
|
+
if (sidebar.classList.contains('closed')) {
|
|
291
|
+
// Open the sidebar
|
|
292
|
+
sidebar.classList.remove('closed');
|
|
293
|
+
sidebar.style.width = sidebar.dataset.lastWidth || '250px';
|
|
294
|
+
} else {
|
|
295
|
+
// Store current width before closing
|
|
296
|
+
sidebar.dataset.lastWidth = sidebar.style.width || getComputedStyle(sidebar).width;
|
|
297
|
+
// Close the sidebar but keep a minimal width for the resizer and button
|
|
298
|
+
sidebar.classList.add('closed');
|
|
299
|
+
sidebar.style.width = '20px';
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Toggle the arrow direction
|
|
303
|
+
const arrowImg = toggleButton.querySelector('img');
|
|
304
|
+
if (arrowImg) {
|
|
305
|
+
arrowImg.style.transform = sidebar.classList.contains('closed') ? 'rotate(180deg)' : '';
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Store sidebar state
|
|
309
|
+
this.storeSidebarState();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Toggles a group's table list visibility
|
|
314
|
+
* Updates the arrow rotation indicator
|
|
315
|
+
*/
|
|
316
|
+
toggleGroup(event) {
|
|
317
|
+
const group = event.currentTarget.closest('.group');
|
|
318
|
+
const tableList = group.querySelector('.table-list');
|
|
319
|
+
tableList.classList.toggle('visible');
|
|
320
|
+
|
|
321
|
+
if (tableList.classList.contains('visible')) {
|
|
322
|
+
tableList.classList.remove('hidden');
|
|
323
|
+
} else {
|
|
324
|
+
tableList.classList.add('hidden');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const svg = event.currentTarget.querySelector('svg');
|
|
328
|
+
// Update rotation to indicate expanded/collapsed state
|
|
329
|
+
svg.style.transform = tableList.classList.contains('visible') ? 'rotate(90deg)' : '';
|
|
330
|
+
|
|
331
|
+
// Store sidebar state
|
|
332
|
+
this.storeSidebarState();
|
|
333
|
+
|
|
334
|
+
// Prevent event from bubbling to parent handlers
|
|
335
|
+
event.stopPropagation();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Toggles a table's column list visibility
|
|
340
|
+
* Updates the arrow rotation indicator
|
|
341
|
+
*/
|
|
342
|
+
toggleTable(event) {
|
|
343
|
+
const table = event.currentTarget.closest('.db-table');
|
|
344
|
+
const columnList = table.querySelector('.column-list');
|
|
345
|
+
columnList.classList.toggle('visible');
|
|
346
|
+
|
|
347
|
+
if (columnList.classList.contains('visible')) {
|
|
348
|
+
columnList.classList.remove('hidden');
|
|
349
|
+
} else {
|
|
350
|
+
columnList.classList.add('hidden');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const svg = event.currentTarget.querySelector('svg');
|
|
354
|
+
// Update rotation to indicate expanded/collapsed state
|
|
355
|
+
svg.style.transform = columnList.classList.contains('visible') ? 'rotate(90deg)' : '';
|
|
356
|
+
|
|
357
|
+
// Store sidebar state
|
|
358
|
+
this.storeSidebarState();
|
|
359
|
+
|
|
360
|
+
// Prevent event from bubbling to parent handlers
|
|
361
|
+
event.stopPropagation();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Toggles all groups and tables between expanded and collapsed states
|
|
366
|
+
*/
|
|
367
|
+
toggleGroupList() {
|
|
368
|
+
// Toggle the groupList collapsed class
|
|
369
|
+
this.groupListTarget.classList.toggle('collapsed');
|
|
370
|
+
|
|
371
|
+
// Toggle expanded state
|
|
372
|
+
this.isExpanded = !this.isExpanded;
|
|
373
|
+
|
|
374
|
+
if (this.isExpanded) {
|
|
375
|
+
// When clicking the collapse button once, expand everything
|
|
376
|
+
this.expandAll();
|
|
377
|
+
} else {
|
|
378
|
+
// When clicking again, collapse tables and columns but keep groups visible
|
|
379
|
+
this.collapseTablesAndColumns();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Expands all groups and tables in the sidebar
|
|
385
|
+
*/
|
|
386
|
+
expandAll() {
|
|
387
|
+
// Expand all group table lists
|
|
388
|
+
document.querySelectorAll('.group').forEach(group => {
|
|
389
|
+
const tableList = group.querySelector('.table-list');
|
|
390
|
+
tableList.classList.add('visible');
|
|
391
|
+
tableList.classList.remove('hidden');
|
|
392
|
+
const groupToggleSvg = group.querySelector('.group-toggle svg');
|
|
393
|
+
if (groupToggleSvg) {
|
|
394
|
+
groupToggleSvg.style.transform = 'rotate(90deg)';
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Expand all table column lists
|
|
399
|
+
document.querySelectorAll('.db-table').forEach(table => {
|
|
400
|
+
const columnList = table.querySelector('.column-list');
|
|
401
|
+
columnList.classList.add('visible');
|
|
402
|
+
columnList.classList.remove('hidden');
|
|
403
|
+
const tableToggleSvg = table.querySelector('.table-toggle svg');
|
|
404
|
+
if (tableToggleSvg) {
|
|
405
|
+
tableToggleSvg.style.transform = 'rotate(90deg)';
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Collapses all tables and columns in the sidebar
|
|
412
|
+
*/
|
|
413
|
+
collapseTablesAndColumns() {
|
|
414
|
+
// Collapse all table column lists
|
|
415
|
+
document.querySelectorAll('.db-table').forEach(table => {
|
|
416
|
+
const columnList = table.querySelector('.column-list');
|
|
417
|
+
columnList.classList.remove('visible');
|
|
418
|
+
columnList.classList.add('hidden');
|
|
419
|
+
const tableToggleSvg = table.querySelector('.table-toggle svg');
|
|
420
|
+
if (tableToggleSvg) {
|
|
421
|
+
tableToggleSvg.style.transform = '';
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Collapse all group table lists
|
|
426
|
+
document.querySelectorAll('.group').forEach(group => {
|
|
427
|
+
const tableList = group.querySelector('.table-list');
|
|
428
|
+
tableList.classList.remove('visible');
|
|
429
|
+
tableList.classList.add('hidden');
|
|
430
|
+
const groupToggleSvg = group.querySelector('.group-toggle svg');
|
|
431
|
+
if (groupToggleSvg) {
|
|
432
|
+
groupToggleSvg.style.transform = '';
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Stores original content of sidebar items for search reset functionality
|
|
439
|
+
*/
|
|
440
|
+
storeOriginalContent() {
|
|
441
|
+
// Store original group content
|
|
442
|
+
document.querySelectorAll('.group').forEach((groupEl) => {
|
|
443
|
+
const groupTextEl = groupEl.querySelector('.group-heading');
|
|
444
|
+
if (groupTextEl) {
|
|
445
|
+
groupEl.dataset.originalContent = groupTextEl.innerHTML;
|
|
446
|
+
groupEl.dataset.searchEnglish = groupTextEl.textContent;
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Store original table content
|
|
451
|
+
document.querySelectorAll('.db-table').forEach((tableEl) => {
|
|
452
|
+
const tableNameEl = tableEl.querySelector('.table-name');
|
|
453
|
+
if (tableNameEl) {
|
|
454
|
+
tableEl.dataset.originalContent = tableNameEl.innerHTML;
|
|
455
|
+
tableEl.dataset.searchEnglish = tableNameEl.dataset.originalTableName || tableNameEl.textContent;
|
|
456
|
+
tableEl.dataset.searchJapanese = tableNameEl.dataset.physicalTableName || '';
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Store original column content
|
|
461
|
+
document.querySelectorAll('.column').forEach((columnEl) => {
|
|
462
|
+
const columnNameEl = columnEl.querySelector('.column-name');
|
|
463
|
+
if (columnNameEl) {
|
|
464
|
+
columnEl.dataset.originalContent = columnNameEl.innerHTML;
|
|
465
|
+
columnEl.dataset.searchEnglish = columnNameEl.dataset.originalColumnName || columnNameEl.textContent;
|
|
466
|
+
columnEl.dataset.searchJapanese = columnNameEl.dataset.physicalColumnName || '';
|
|
467
|
+
columnEl.dataset.dataType = columnNameEl.dataset.dataType || '';
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Sidebar search functionality with highlight and filter
|
|
474
|
+
* @param {Event} event - The input event from the search field
|
|
475
|
+
*/
|
|
476
|
+
search(event) {
|
|
477
|
+
let filter = event.target.value.toLowerCase().trim();
|
|
478
|
+
let matchesFound = false;
|
|
479
|
+
|
|
480
|
+
// Get or create the "no matches" message element
|
|
481
|
+
let noMatchesElement = document.getElementById('no-matches-message');
|
|
482
|
+
if (!noMatchesElement) {
|
|
483
|
+
noMatchesElement = document.createElement('div');
|
|
484
|
+
noMatchesElement.id = 'no-matches-message';
|
|
485
|
+
noMatchesElement.className = 'text-center p-3 mt-3';
|
|
486
|
+
noMatchesElement.style.color = 'white';
|
|
487
|
+
noMatchesElement.style.fontStyle = 'italic';
|
|
488
|
+
noMatchesElement.textContent = 'No matches found';
|
|
489
|
+
this.groupListTarget.appendChild(noMatchesElement);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Initially hide the no matches message
|
|
493
|
+
noMatchesElement.style.display = 'none';
|
|
494
|
+
|
|
495
|
+
if (filter.length > 0) {
|
|
496
|
+
// First, expand all groups and tables to make everything visible
|
|
497
|
+
this.expandAll();
|
|
498
|
+
|
|
499
|
+
// Track visibility of elements
|
|
500
|
+
let visibleGroups = new Set();
|
|
501
|
+
let visibleTables = new Set();
|
|
502
|
+
let visibleColumns = new Set();
|
|
503
|
+
|
|
504
|
+
// Process all groups
|
|
505
|
+
document.querySelectorAll('.group').forEach((groupEl) => {
|
|
506
|
+
let groupMatch = false;
|
|
507
|
+
// Clear any previous highlighting in group text
|
|
508
|
+
const groupTextEl = groupEl.querySelector('.group-heading');
|
|
509
|
+
if (groupTextEl && groupEl.dataset.originalContent) {
|
|
510
|
+
groupTextEl.innerHTML = groupEl.dataset.originalContent;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Check group name (English only since groups don't have Japanese names)
|
|
514
|
+
const groupNameText = groupTextEl.textContent.toLowerCase();
|
|
515
|
+
if (groupNameText.includes(filter)) {
|
|
516
|
+
groupMatch = true;
|
|
517
|
+
// Highlight group name
|
|
518
|
+
const regex = new RegExp(`(${filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
519
|
+
groupTextEl.innerHTML = groupTextEl.textContent.replace(regex, '<span class="highlight">$1</span>');
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Process tables within this group
|
|
523
|
+
let hasVisibleTables = false;
|
|
524
|
+
groupEl.querySelectorAll('.db-table').forEach((tableEl) => {
|
|
525
|
+
let tableMatch = false;
|
|
526
|
+
// Clear any previous highlighting
|
|
527
|
+
const tableNameEl = tableEl.querySelector('.table-name');
|
|
528
|
+
if (tableNameEl && tableEl.dataset.originalContent) {
|
|
529
|
+
tableNameEl.innerHTML = tableEl.dataset.originalContent;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Get both English and Japanese table names
|
|
533
|
+
const tableName = tableNameEl.dataset.originalTableName || tableNameEl.textContent;
|
|
534
|
+
const physicalTableName = tableNameEl.dataset.physicalTableName || '';
|
|
535
|
+
|
|
536
|
+
// Convert to lowercase for case-insensitive search
|
|
537
|
+
const tableNameLower = tableName.toLowerCase();
|
|
538
|
+
const physicalTableNameLower = physicalTableName.toLowerCase();
|
|
539
|
+
|
|
540
|
+
// Check if filter matches either name
|
|
541
|
+
if (tableNameLower.includes(filter) || physicalTableNameLower.includes(filter)) {
|
|
542
|
+
tableMatch = true;
|
|
543
|
+
hasVisibleTables = true;
|
|
544
|
+
|
|
545
|
+
// Highlight the matching text
|
|
546
|
+
if (tableNameLower.includes(filter)) {
|
|
547
|
+
// If English name matches, highlight the specific match
|
|
548
|
+
const regex = new RegExp(`(${filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
549
|
+
tableNameEl.innerHTML = tableName.replace(regex, '<span class="highlight">$1</span>');
|
|
550
|
+
} else {
|
|
551
|
+
// If only Japanese name matches, highlight the entire English name
|
|
552
|
+
tableNameEl.innerHTML = `<span class="highlight">${tableName}</span>`;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
visibleTables.add(tableEl.dataset.tableId);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Process columns within this table
|
|
559
|
+
let hasVisibleColumns = false;
|
|
560
|
+
tableEl.querySelectorAll('.column').forEach((columnEl) => {
|
|
561
|
+
let columnMatch = false;
|
|
562
|
+
// Clear any previous highlighting
|
|
563
|
+
const columnNameEl = columnEl.querySelector('.column-name');
|
|
564
|
+
if (columnNameEl && columnEl.dataset.originalContent) {
|
|
565
|
+
columnNameEl.innerHTML = columnEl.dataset.originalContent;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Get both English and Japanese column names
|
|
569
|
+
const columnName = columnNameEl.dataset.originalColumnName || columnNameEl.textContent;
|
|
570
|
+
const physicalColumnName = columnNameEl.dataset.physicalColumnName || '';
|
|
571
|
+
|
|
572
|
+
// Convert to lowercase for case-insensitive search
|
|
573
|
+
const columnNameLower = columnName.toLowerCase();
|
|
574
|
+
const physicalColumnNameLower = physicalColumnName.toLowerCase();
|
|
575
|
+
|
|
576
|
+
// Check if filter matches either name
|
|
577
|
+
if (columnNameLower.includes(filter) || physicalColumnNameLower.includes(filter)) {
|
|
578
|
+
columnMatch = true;
|
|
579
|
+
hasVisibleColumns = true;
|
|
580
|
+
tableMatch = true; // Column match makes table visible too
|
|
581
|
+
|
|
582
|
+
// Highlight the matching text
|
|
583
|
+
if (columnNameLower.includes(filter)) {
|
|
584
|
+
// If English name matches, highlight the specific match
|
|
585
|
+
const regex = new RegExp(`(${filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
586
|
+
columnNameEl.innerHTML = columnName.replace(regex, '<span class="highlight">$1</span>');
|
|
587
|
+
} else {
|
|
588
|
+
// If only Japanese name matches, highlight the entire English name
|
|
589
|
+
columnNameEl.innerHTML = `<span class="highlight">${columnName}</span>`;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
visibleColumns.add(columnEl.querySelector('.column-name').dataset.physicalColumnName);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Show or hide column based on match
|
|
596
|
+
columnEl.style.display = columnMatch ? 'block' : 'none';
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// If any column matches, make table and its column list visible
|
|
600
|
+
if (hasVisibleColumns) {
|
|
601
|
+
tableEl.querySelector('.column-list').classList.add('visible');
|
|
602
|
+
tableEl.querySelector('.column-list').classList.remove('hidden');
|
|
603
|
+
tableEl.querySelector('.table-toggle svg').style.transform = 'rotate(90deg)';
|
|
604
|
+
hasVisibleTables = true;
|
|
605
|
+
tableMatch = true;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Show or hide table based on match
|
|
609
|
+
tableEl.style.display = tableMatch ? 'block' : 'none';
|
|
610
|
+
|
|
611
|
+
// If table matches, make sure its parent group is visible
|
|
612
|
+
if (tableMatch) {
|
|
613
|
+
groupMatch = true;
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
// If any table matches, make group and its table list visible
|
|
618
|
+
if (hasVisibleTables) {
|
|
619
|
+
groupEl.querySelector('.table-list').classList.add('visible');
|
|
620
|
+
groupEl.querySelector('.table-list').classList.remove('hidden');
|
|
621
|
+
groupEl.querySelector('.group-toggle svg').style.transform = 'rotate(90deg)';
|
|
622
|
+
groupMatch = true;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Show or hide group based on match
|
|
626
|
+
groupEl.style.display = groupMatch ? 'block' : 'none';
|
|
627
|
+
|
|
628
|
+
// Update matchesFound flag
|
|
629
|
+
if (groupMatch) {
|
|
630
|
+
matchesFound = true;
|
|
631
|
+
visibleGroups.add(groupEl.dataset.groupId);
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// Show "No matches found" message if no matches were found
|
|
636
|
+
if (!matchesFound) {
|
|
637
|
+
noMatchesElement.style.display = 'block';
|
|
638
|
+
}
|
|
639
|
+
} else {
|
|
640
|
+
// If the search is empty, restore original content and respect the current expand/collapse state
|
|
641
|
+
this.resetAndRespectExpandState();
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Update tooltips after search is complete
|
|
645
|
+
this.updateTooltips();
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Reset search highlighting and restore expand/collapse state
|
|
650
|
+
*/
|
|
651
|
+
resetAndRespectExpandState() {
|
|
652
|
+
// Hide the "no matches" message if it exists
|
|
653
|
+
const noMatchesElement = document.getElementById('no-matches-message');
|
|
654
|
+
if (noMatchesElement) {
|
|
655
|
+
noMatchesElement.style.display = 'none';
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Reset all groups to their original content and make them visible
|
|
659
|
+
document.querySelectorAll('.group').forEach(groupEl => {
|
|
660
|
+
const groupTextEl = groupEl.querySelector('.group-heading');
|
|
661
|
+
if (groupTextEl && groupEl.dataset.originalContent) {
|
|
662
|
+
groupTextEl.innerHTML = groupEl.dataset.originalContent;
|
|
663
|
+
}
|
|
664
|
+
groupEl.style.display = 'block';
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
// Reset all tables to their original content and make them visible
|
|
668
|
+
document.querySelectorAll('.db-table').forEach(tableEl => {
|
|
669
|
+
const tableNameEl = tableEl.querySelector('.table-name');
|
|
670
|
+
if (tableNameEl && tableEl.dataset.originalContent) {
|
|
671
|
+
tableNameEl.innerHTML = tableEl.dataset.originalContent;
|
|
672
|
+
}
|
|
673
|
+
tableEl.style.display = 'block';
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Reset all columns to their original content and make them visible
|
|
677
|
+
document.querySelectorAll('.column').forEach(columnEl => {
|
|
678
|
+
const columnNameEl = columnEl.querySelector('.column-name');
|
|
679
|
+
if (columnNameEl && columnEl.dataset.originalContent) {
|
|
680
|
+
columnNameEl.innerHTML = columnEl.dataset.originalContent;
|
|
681
|
+
}
|
|
682
|
+
columnEl.style.display = 'block';
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
// Respect the current expand/collapse state
|
|
686
|
+
if (this.isExpanded) {
|
|
687
|
+
this.expandAll();
|
|
688
|
+
} else {
|
|
689
|
+
this.collapseTablesAndColumns();
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Update tooltips after resetting
|
|
693
|
+
this.updateTooltips();
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Handles navigation to a group details page
|
|
698
|
+
* @param {Event} event - The click event
|
|
699
|
+
*/
|
|
700
|
+
navigateToGroup(event) {
|
|
701
|
+
// Find the group row and add active class
|
|
702
|
+
const groupRow = event.target.closest('.group-row');
|
|
703
|
+
if (groupRow) {
|
|
704
|
+
// Remove active class from all items
|
|
705
|
+
document.querySelectorAll('.sidebar-item-active').forEach(activeItem => {
|
|
706
|
+
activeItem.classList.remove('sidebar-item-active');
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// Add active class to this row
|
|
710
|
+
groupRow.classList.add('sidebar-item-active');
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Store sidebar state before navigation
|
|
714
|
+
this.storeSidebarState();
|
|
715
|
+
|
|
716
|
+
const groupItem = event.target.closest('.group');
|
|
717
|
+
const groupId = groupItem.dataset.groupId;
|
|
718
|
+
if (groupId) {
|
|
719
|
+
window.location.href = '/dbdoc/group_details/' + groupId;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Prevent default to handle navigation manually
|
|
723
|
+
event.preventDefault();
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Handles navigation to a table details page
|
|
728
|
+
* @param {Event} event - The click event
|
|
729
|
+
*/
|
|
730
|
+
navigateToTable(event) {
|
|
731
|
+
// Find the table row and add active class
|
|
732
|
+
const tableRow = event.target.closest('.table-row');
|
|
733
|
+
if (tableRow) {
|
|
734
|
+
// Remove active class from all items
|
|
735
|
+
document.querySelectorAll('.sidebar-item-active').forEach(activeItem => {
|
|
736
|
+
activeItem.classList.remove('sidebar-item-active');
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
// Add active class to this row
|
|
740
|
+
tableRow.classList.add('sidebar-item-active');
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Store sidebar state before navigation
|
|
744
|
+
this.storeSidebarState();
|
|
745
|
+
|
|
746
|
+
const tableItem = event.target.closest('.db-table');
|
|
747
|
+
const tableId = tableItem.dataset.tableId;
|
|
748
|
+
if (tableId) {
|
|
749
|
+
window.location.href = '/dbdoc/table_details/' + tableId;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Prevent default to handle navigation manually
|
|
753
|
+
event.preventDefault();
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Sets up tooltip functionality for sidebar items
|
|
758
|
+
*/
|
|
759
|
+
setupTooltips() {
|
|
760
|
+
// Create tooltip element if it doesn't exist
|
|
761
|
+
if (!document.getElementById('sidebar-tooltip')) {
|
|
762
|
+
const tooltip = document.createElement('div');
|
|
763
|
+
tooltip.id = 'sidebar-tooltip';
|
|
764
|
+
tooltip.classList.add('sidebar-tooltip');
|
|
765
|
+
|
|
766
|
+
// Create container for tooltip content
|
|
767
|
+
const tooltipContent = document.createElement('div');
|
|
768
|
+
tooltipContent.classList.add('tooltip-content');
|
|
769
|
+
|
|
770
|
+
tooltip.appendChild(tooltipContent);
|
|
771
|
+
document.body.appendChild(tooltip);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// Add event listeners to the row elements instead of just the text
|
|
775
|
+
const tooltipItems = document.querySelectorAll(
|
|
776
|
+
'.group-row, .table-row, .column-row'
|
|
777
|
+
);
|
|
778
|
+
|
|
779
|
+
tooltipItems.forEach(item => {
|
|
780
|
+
item.addEventListener('mouseover', this.showTooltip.bind(this));
|
|
781
|
+
item.addEventListener('mouseout', this.hideTooltip.bind(this));
|
|
782
|
+
item.addEventListener('mousemove', this.moveTooltip.bind(this));
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Shows tooltip with relevant information
|
|
788
|
+
* @param {Event} event - The mouseover event
|
|
789
|
+
*/
|
|
790
|
+
showTooltip(event) {
|
|
791
|
+
const tooltip = document.getElementById('sidebar-tooltip');
|
|
792
|
+
const tooltipContent = tooltip.querySelector('.tooltip-content');
|
|
793
|
+
|
|
794
|
+
// Clear previous content
|
|
795
|
+
tooltipContent.innerHTML = '';
|
|
796
|
+
|
|
797
|
+
// Find the closest relevant element
|
|
798
|
+
const groupRow = event.currentTarget.closest('.group-row');
|
|
799
|
+
const tableRow = event.currentTarget.closest('.table-row');
|
|
800
|
+
const columnRow = event.currentTarget.closest('.column-row');
|
|
801
|
+
|
|
802
|
+
if (groupRow) {
|
|
803
|
+
// Group tooltip
|
|
804
|
+
const groupElement = groupRow.closest('.group');
|
|
805
|
+
const groupName = groupRow.querySelector('.group-heading').textContent;
|
|
806
|
+
const tableCount = groupElement.querySelectorAll('.db-table').length;
|
|
807
|
+
|
|
808
|
+
tooltipContent.innerHTML = `
|
|
809
|
+
<div class="tooltip-line">
|
|
810
|
+
<span class="tooltip-label">Name:</span>
|
|
811
|
+
<span class="tooltip-value">${groupName}</span>
|
|
812
|
+
</div>
|
|
813
|
+
<div class="tooltip-line">
|
|
814
|
+
<span class="tooltip-label">Tables:</span>
|
|
815
|
+
<span class="tooltip-value">${tableCount}</span>
|
|
816
|
+
</div>
|
|
817
|
+
`;
|
|
818
|
+
} else if (tableRow) {
|
|
819
|
+
// Table tooltip
|
|
820
|
+
const tableElement = tableRow.closest('.db-table');
|
|
821
|
+
const tableNameElement = tableRow.querySelector('.table-name');
|
|
822
|
+
const originalName = tableNameElement.dataset.originalTableName || 'N/A';
|
|
823
|
+
const physicalName = tableNameElement.dataset.physicalTableName || 'N/A';
|
|
824
|
+
const columnCount = tableElement.querySelectorAll('.column').length;
|
|
825
|
+
|
|
826
|
+
tooltipContent.innerHTML = `
|
|
827
|
+
<div class="tooltip-line">
|
|
828
|
+
<span class="tooltip-label">Original:</span>
|
|
829
|
+
<span class="tooltip-value">${originalName}</span>
|
|
830
|
+
</div>
|
|
831
|
+
<div class="tooltip-line">
|
|
832
|
+
<span class="tooltip-label">Physical:</span>
|
|
833
|
+
<span class="tooltip-value">${physicalName}</span>
|
|
834
|
+
</div>
|
|
835
|
+
<div class="tooltip-line">
|
|
836
|
+
<span class="tooltip-label">Fields:</span>
|
|
837
|
+
<span class="tooltip-value">${columnCount}</span>
|
|
838
|
+
</div>
|
|
839
|
+
`;
|
|
840
|
+
} else if (columnRow) {
|
|
841
|
+
// Column tooltip
|
|
842
|
+
const columnNameElement = columnRow.querySelector('.column-name');
|
|
843
|
+
const originalName = columnNameElement.dataset.originalColumnName || 'N/A';
|
|
844
|
+
const physicalName = columnNameElement.dataset.physicalColumnName || 'N/A';
|
|
845
|
+
const dataType = columnNameElement.dataset.dataType || 'N/A';
|
|
846
|
+
|
|
847
|
+
tooltipContent.innerHTML = `
|
|
848
|
+
<div class="tooltip-line">
|
|
849
|
+
<span class="tooltip-label">Name:</span>
|
|
850
|
+
<span class="tooltip-value">${originalName}</span>
|
|
851
|
+
</div>
|
|
852
|
+
<div class="tooltip-line">
|
|
853
|
+
<span class="tooltip-label">Physical:</span>
|
|
854
|
+
<span class="tooltip-value">${physicalName}</span>
|
|
855
|
+
</div>
|
|
856
|
+
<div class="tooltip-line">
|
|
857
|
+
<span class="tooltip-label">Type:</span>
|
|
858
|
+
<span class="tooltip-value">${dataType}</span>
|
|
859
|
+
</div>
|
|
860
|
+
`;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
tooltip.classList.add('visible');
|
|
864
|
+
|
|
865
|
+
// Position the tooltip
|
|
866
|
+
this.moveTooltip(event);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Updates tooltip position based on mouse movement
|
|
871
|
+
* @param {Event} event - The mousemove event
|
|
872
|
+
*/
|
|
873
|
+
moveTooltip(event) {
|
|
874
|
+
const tooltip = document.getElementById('sidebar-tooltip');
|
|
875
|
+
const sidebar = document.getElementById('sidebar');
|
|
876
|
+
|
|
877
|
+
// Get the sidebar width and position
|
|
878
|
+
const sidebarRect = sidebar.getBoundingClientRect();
|
|
879
|
+
const sidebarRight = sidebarRect.right;
|
|
880
|
+
|
|
881
|
+
// Position tooltip to the right of the sidebar at the same vertical position as the mouse
|
|
882
|
+
let left = sidebarRight + 15; // Add some margin
|
|
883
|
+
let top = event.clientY;
|
|
884
|
+
|
|
885
|
+
// Ensure tooltip doesn't go off the screen
|
|
886
|
+
const viewportWidth = window.innerWidth;
|
|
887
|
+
const viewportHeight = window.innerHeight;
|
|
888
|
+
const tooltipRect = tooltip.getBoundingClientRect();
|
|
889
|
+
|
|
890
|
+
// Adjust horizontal position if needed
|
|
891
|
+
if (left + tooltipRect.width > viewportWidth) {
|
|
892
|
+
left = viewportWidth - tooltipRect.width - 10;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// Adjust vertical position to center the tooltip relative to the cursor
|
|
896
|
+
top = top - (tooltipRect.height / 2);
|
|
897
|
+
|
|
898
|
+
// Ensure tooltip doesn't go off the top or bottom of the screen
|
|
899
|
+
if (top < 10) {
|
|
900
|
+
top = 10;
|
|
901
|
+
} else if (top + tooltipRect.height > viewportHeight) {
|
|
902
|
+
top = viewportHeight - tooltipRect.height - 10;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
tooltip.style.left = `${left}px`;
|
|
906
|
+
tooltip.style.top = `${top}px`;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Hides the tooltip
|
|
911
|
+
*/
|
|
912
|
+
hideTooltip() {
|
|
913
|
+
const tooltip = document.getElementById('sidebar-tooltip');
|
|
914
|
+
tooltip.classList.remove('visible');
|
|
915
|
+
}
|
|
916
|
+
hideTooltipForce() {
|
|
917
|
+
const tooltip = document.getElementById('sidebar-tooltip');
|
|
918
|
+
if (tooltip) {
|
|
919
|
+
tooltip.classList.remove('visible');
|
|
920
|
+
tooltip.style.opacity = 0;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Updates tooltip listeners after DOM changes
|
|
926
|
+
* Call this after search or other DOM modifications
|
|
927
|
+
*/
|
|
928
|
+
updateTooltips() {
|
|
929
|
+
// Remove existing listeners
|
|
930
|
+
const tooltipItems = document.querySelectorAll(
|
|
931
|
+
'.group-text, .table-name, .column-name'
|
|
932
|
+
);
|
|
933
|
+
|
|
934
|
+
tooltipItems.forEach(item => {
|
|
935
|
+
item.removeEventListener('mouseover', this.showTooltip);
|
|
936
|
+
item.removeEventListener('mouseout', this.hideTooltip);
|
|
937
|
+
item.removeEventListener('mousemove', this.moveTooltip);
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
// Re-setup tooltips
|
|
941
|
+
this.setupTooltips();
|
|
942
|
+
}
|
|
943
|
+
}
|