drf-to-mkdoc 0.2.2__tar.gz → 0.2.3__tar.gz
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.
Potentially problematic release.
This version of drf-to-mkdoc might be problematic. Click here for more details.
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/PKG-INFO +1 -1
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/docs/customizing_endpoints.md +0 -1
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/conf/defaults.py +1 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/form-manager.js +172 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/main.js +22 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/modal.js +79 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/request-executor.js +111 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/suggestions.js +216 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/tabs.js +34 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/buttons.css +71 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/fab.css +47 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/form.css +124 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/key-value.css +161 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/main.css +57 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/modal.css +112 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/response.css +158 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/tabs.css +62 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/variables.css +38 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/detail/base.html +3 -1
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/detail/query_parameters.html +1 -8
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/detail/responses.html +4 -4
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/templates/try-out/fab.html +4 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/templates/try-out/form.html +113 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/templates/try-out/main.html +4 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/templates/try-out/modal.html +14 -0
- drf_to_mkdoc-0.2.3/drf_to_mkdoc/templates/try-out/response-modal.html +20 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templatetags/custom_filters.py +33 -1
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/commons/schema_utils.py +5 -14
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/endpoint_detail_generator.py +140 -21
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/extractors/query_parameter_extractors.py +0 -15
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc.egg-info/PKG-INFO +1 -1
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc.egg-info/SOURCES.txt +20 -2
- drf_to_mkdoc-0.2.2/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out-sidebar.js +0 -879
- drf_to_mkdoc-0.2.2/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out-sidebar.css +0 -728
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/.github/workflows/publish.yaml +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/.pre-commit-config.yaml +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/CONTRIBUTING.md +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/LICENSE +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/MANIFEST.in +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/README.md +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/conf/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/conf/defaults.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/conf/settings.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/docs/mkdocs.yml +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/docs/serving_mkdocs_with_django.md +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/apps.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/conf/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/conf/settings.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/management/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/management/commands/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/management/commands/build_docs.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/management/commands/build_endpoint_docs.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/management/commands/build_model_docs.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/management/commands/extract_model_data.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/management/commands/generate_doc_json.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/management/commands/update_doc_schema.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/endpoints-filter.js +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/accessibility.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/animations.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/badges.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/base.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/endpoint-content.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/endpoints-grid.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/filter-section.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/fixes.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/layout.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/loading.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/responsive.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/sections.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/stats.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/tags.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/theme-toggle.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/variables.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/animations.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/base.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-cards.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-tables.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/responsive.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/variables.css +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/detail/path_parameters.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/detail/request_body.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/list/base.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/list/endpoint_card.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/list/filter_section.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/list/filters/app.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/list/filters/method.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/list/filters/path.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/endpoints/list/filters/search.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/model_detail/base.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/model_detail/choices.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/model_detail/fields.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/model_detail/meta.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/model_detail/methods.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/model_detail/relationships.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/templates/models_index.html +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/ai_tools/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/ai_tools/enums.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/ai_tools/exceptions.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/ai_tools/providers/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/ai_tools/providers/base_provider.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/ai_tools/providers/gemini_provider.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/ai_tools/types.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/commons/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/commons/code_extractor.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/commons/file_utils.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/commons/model_utils.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/commons/operation_utils.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/commons/path_utils.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/endpoint_list_generator.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/extractors/__init__.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/model_detail_generator.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/model_list_generator.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc/utils/schema.py +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc.egg-info/dependency_links.txt +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc.egg-info/requires.txt +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/drf_to_mkdoc.egg-info/top_level.txt +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/pyproject.toml +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/setup.cfg +0 -0
- {drf_to_mkdoc-0.2.2 → drf_to_mkdoc-0.2.3}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: drf-to-mkdoc
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Generate Markdown API docs from Django/DRF OpenAPI schema for MkDocs
|
|
5
5
|
Author-email: Hossein Shayesteh <shayestehhs1@gmail.com>
|
|
6
6
|
Maintainer-email: Hossein Shayesteh <shayestehhs1@gmail.com>
|
|
@@ -137,7 +137,6 @@ Supported `queryparam_type` values:
|
|
|
137
137
|
* `search_fields` → Used for search filters
|
|
138
138
|
* `filter_fields` → Standard filters
|
|
139
139
|
* `ordering_fields` → Sort fields
|
|
140
|
-
* `filter_backends` → Backend-specific filters
|
|
141
140
|
* `pagination_fields` → Pagination-related fields
|
|
142
141
|
|
|
143
142
|
> ⚠️ If `queryparam_type` is missing or invalid, the generator will raise an error.
|
|
@@ -7,6 +7,7 @@ DEFAULTS = {
|
|
|
7
7
|
"CUSTOM_SCHEMA_FILE": "docs/configs/custom_schema.json", # Path to custom schema file
|
|
8
8
|
"PATH_PARAM_SUBSTITUTE_FUNCTION": None,
|
|
9
9
|
"PATH_PARAM_SUBSTITUTE_MAPPING": {},
|
|
10
|
+
"FIELD_GENERATORS": {},
|
|
10
11
|
# AI documentation settings
|
|
11
12
|
"ENABLE_AI_DOCS": False,
|
|
12
13
|
"AI_CONFIG_DIR_NAME": "ai_code", # Directory name for AI-generated code files
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Form management functionality
|
|
2
|
+
const FormManager = {
|
|
3
|
+
addQueryParam: function() {
|
|
4
|
+
const container = document.getElementById('queryParams');
|
|
5
|
+
if (!container) return;
|
|
6
|
+
|
|
7
|
+
const kvItem = this.createKvItem('Parameter name', 'Parameter value', true);
|
|
8
|
+
container.appendChild(kvItem);
|
|
9
|
+
|
|
10
|
+
// Focus on the first input
|
|
11
|
+
const firstInput = kvItem.querySelector('input');
|
|
12
|
+
if (firstInput) {
|
|
13
|
+
firstInput.focus();
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
addHeader: function() {
|
|
18
|
+
const container = document.getElementById('requestHeaders');
|
|
19
|
+
if (!container) return;
|
|
20
|
+
|
|
21
|
+
const kvItem = this.createKvItem('Header name', 'Header value', true);
|
|
22
|
+
container.appendChild(kvItem);
|
|
23
|
+
|
|
24
|
+
// Focus on the first input
|
|
25
|
+
const firstInput = kvItem.querySelector('input');
|
|
26
|
+
if (firstInput) {
|
|
27
|
+
firstInput.focus();
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
createKvItem: function(namePlaceholder, valuePlaceholder, removable = true) {
|
|
32
|
+
const kvItem = document.createElement('div');
|
|
33
|
+
kvItem.className = 'kv-item';
|
|
34
|
+
|
|
35
|
+
const nameInput = document.createElement('input');
|
|
36
|
+
nameInput.type = 'text';
|
|
37
|
+
nameInput.placeholder = namePlaceholder;
|
|
38
|
+
|
|
39
|
+
const valueInput = document.createElement('input');
|
|
40
|
+
valueInput.type = 'text';
|
|
41
|
+
valueInput.placeholder = valuePlaceholder;
|
|
42
|
+
|
|
43
|
+
kvItem.appendChild(nameInput);
|
|
44
|
+
kvItem.appendChild(valueInput);
|
|
45
|
+
|
|
46
|
+
if (removable) {
|
|
47
|
+
const removeBtn = document.createElement('button');
|
|
48
|
+
removeBtn.className = 'remove-btn';
|
|
49
|
+
removeBtn.textContent = '✕';
|
|
50
|
+
removeBtn.addEventListener('click', () => this.removeKvItem(removeBtn));
|
|
51
|
+
kvItem.appendChild(removeBtn);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return kvItem;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
removeKvItem: function(button) {
|
|
58
|
+
if (button && button.parentElement) {
|
|
59
|
+
button.parentElement.remove();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
validateRequiredParams: function() {
|
|
64
|
+
const requiredInputs = document.querySelectorAll('#pathParams input[required]');
|
|
65
|
+
const errors = [];
|
|
66
|
+
|
|
67
|
+
requiredInputs.forEach(input => {
|
|
68
|
+
const errorElement = input.parentElement.querySelector('.error-message');
|
|
69
|
+
|
|
70
|
+
if (!input.value.trim()) {
|
|
71
|
+
const paramName = input.getAttribute('data-param');
|
|
72
|
+
errors.push(paramName);
|
|
73
|
+
input.classList.add('error');
|
|
74
|
+
|
|
75
|
+
if (errorElement) {
|
|
76
|
+
errorElement.textContent = `${paramName} is required`;
|
|
77
|
+
errorElement.classList.add('show');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Remove error on input
|
|
81
|
+
input.addEventListener('input', () => {
|
|
82
|
+
input.classList.remove('error');
|
|
83
|
+
if (errorElement) {
|
|
84
|
+
errorElement.classList.remove('show');
|
|
85
|
+
}
|
|
86
|
+
}, { once: true });
|
|
87
|
+
} else {
|
|
88
|
+
input.classList.remove('error');
|
|
89
|
+
if (errorElement) {
|
|
90
|
+
errorElement.classList.remove('show');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return errors;
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
addSuggestion: function(input, suggestion) {
|
|
99
|
+
input.value = suggestion;
|
|
100
|
+
input.focus();
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
buildRequestUrl: function() {
|
|
104
|
+
const baseUrl = document.getElementById('baseUrl').value.trim();
|
|
105
|
+
const pathDisplay = document.querySelector('.path-display').textContent.trim();
|
|
106
|
+
|
|
107
|
+
let url = baseUrl + pathDisplay;
|
|
108
|
+
|
|
109
|
+
// Replace path parameters
|
|
110
|
+
const pathParams = document.querySelectorAll('#pathParams input');
|
|
111
|
+
pathParams.forEach(input => {
|
|
112
|
+
const paramName = input.getAttribute('data-param');
|
|
113
|
+
const paramValue = input.value.trim();
|
|
114
|
+
if (paramName && paramValue) {
|
|
115
|
+
url = url.replace(`{${paramName}}`, encodeURIComponent(paramValue));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Add query parameters
|
|
120
|
+
const queryParams = [];
|
|
121
|
+
const queryInputs = document.querySelectorAll('#queryParams .kv-item');
|
|
122
|
+
queryInputs.forEach(item => {
|
|
123
|
+
const inputs = item.querySelectorAll('input');
|
|
124
|
+
if (inputs.length === 2) {
|
|
125
|
+
const name = inputs[0].value.trim();
|
|
126
|
+
const value = inputs[1].value.trim();
|
|
127
|
+
if (name && value) {
|
|
128
|
+
queryParams.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (queryParams.length > 0) {
|
|
134
|
+
url += '?' + queryParams.join('&');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return url;
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
getRequestHeaders: function() {
|
|
141
|
+
const headers = {};
|
|
142
|
+
const headerInputs = document.querySelectorAll('#requestHeaders .kv-item');
|
|
143
|
+
|
|
144
|
+
headerInputs.forEach(item => {
|
|
145
|
+
const inputs = item.querySelectorAll('input');
|
|
146
|
+
if (inputs.length === 2) {
|
|
147
|
+
const name = inputs[0].value.trim();
|
|
148
|
+
const value = inputs[1].value.trim();
|
|
149
|
+
if (name && value) {
|
|
150
|
+
headers[name] = value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return headers;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
getRequestBody: function() {
|
|
159
|
+
const bodyTextarea = document.getElementById('requestBody');
|
|
160
|
+
if (bodyTextarea && bodyTextarea.value.trim()) {
|
|
161
|
+
try {
|
|
162
|
+
return JSON.parse(bodyTextarea.value);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
return bodyTextarea.value;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Export for global access
|
|
172
|
+
window.FormManager = FormManager;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Main try-out functionality - combines all components
|
|
2
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
3
|
+
// Initialize tabs
|
|
4
|
+
TabManager.init();
|
|
5
|
+
|
|
6
|
+
// Initialize suggestions if available
|
|
7
|
+
if (window.TryOutSuggestions) {
|
|
8
|
+
TryOutSuggestions.init();
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// Legacy compatibility - maintain old interface
|
|
13
|
+
window.TryOutSidebar = {
|
|
14
|
+
openTryOut: () => ModalManager.openTryOut(),
|
|
15
|
+
closeTryOut: () => ModalManager.closeTryOut(),
|
|
16
|
+
closeResponseModal: () => ModalManager.closeResponseModal(),
|
|
17
|
+
showResponseModal: (status, responseText, responseTime) => ModalManager.showResponseModal(status, responseText, responseTime),
|
|
18
|
+
addQueryParam: () => FormManager.addQueryParam(),
|
|
19
|
+
addHeader: () => FormManager.addHeader(),
|
|
20
|
+
removeKvItem: (button) => FormManager.removeKvItem(button),
|
|
21
|
+
validateRequiredParams: () => FormManager.validateRequiredParams()
|
|
22
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Modal management functionality
|
|
2
|
+
const ModalManager = {
|
|
3
|
+
openTryOut: function() {
|
|
4
|
+
const modal = document.getElementById('tryOutModal');
|
|
5
|
+
if (modal) {
|
|
6
|
+
modal.classList.add('show');
|
|
7
|
+
modal.style.display = 'flex';
|
|
8
|
+
document.body.classList.add('modal-open');
|
|
9
|
+
|
|
10
|
+
// Focus management
|
|
11
|
+
const firstInput = modal.querySelector('input, button');
|
|
12
|
+
if (firstInput) {
|
|
13
|
+
firstInput.focus();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
closeTryOut: function() {
|
|
19
|
+
const modal = document.getElementById('tryOutModal');
|
|
20
|
+
if (modal) {
|
|
21
|
+
modal.classList.remove('show');
|
|
22
|
+
modal.style.display = 'none';
|
|
23
|
+
document.body.classList.remove('modal-open');
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
openResponseModal: function() {
|
|
28
|
+
const modal = document.getElementById('responseModal');
|
|
29
|
+
if (modal) {
|
|
30
|
+
modal.classList.add('show');
|
|
31
|
+
modal.style.display = 'flex';
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
closeResponseModal: function() {
|
|
36
|
+
const modal = document.getElementById('responseModal');
|
|
37
|
+
if (modal) {
|
|
38
|
+
modal.classList.remove('show');
|
|
39
|
+
modal.style.display = 'none';
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
showResponseModal: function(status, responseText, responseTime) {
|
|
44
|
+
const modal = document.getElementById('responseModal');
|
|
45
|
+
const statusBadge = document.getElementById('modalStatusBadge');
|
|
46
|
+
const responseBody = document.getElementById('modalResponseBody');
|
|
47
|
+
const responseInfo = document.getElementById('responseInfo');
|
|
48
|
+
|
|
49
|
+
if (modal && statusBadge && responseBody) {
|
|
50
|
+
statusBadge.textContent = String(status);
|
|
51
|
+
const code = Number(status);
|
|
52
|
+
statusBadge.className = 'status-badge' + (Number.isFinite(code) ? ` status-${Math.floor(code/100)*100}` : '');
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const jsonResponse = JSON.parse(responseText);
|
|
56
|
+
responseBody.textContent = JSON.stringify(jsonResponse, null, 2);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
responseBody.textContent = responseText;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (responseInfo && responseTime) {
|
|
62
|
+
responseInfo.textContent = `Response time: ${responseTime}ms`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.openResponseModal();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Keyboard navigation
|
|
71
|
+
document.addEventListener('keydown', function(e) {
|
|
72
|
+
if (e.key === 'Escape') {
|
|
73
|
+
ModalManager.closeTryOut();
|
|
74
|
+
ModalManager.closeResponseModal();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Export for global access
|
|
79
|
+
window.ModalManager = ModalManager;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Request execution functionality
|
|
2
|
+
const RequestExecutor = {
|
|
3
|
+
async executeRequest() {
|
|
4
|
+
const executeBtn = document.getElementById('executeBtn');
|
|
5
|
+
if (!executeBtn) return;
|
|
6
|
+
|
|
7
|
+
// Validate required parameters
|
|
8
|
+
const emptyParams = FormManager.validateRequiredParams();
|
|
9
|
+
if (emptyParams.length > 0) {
|
|
10
|
+
// Switch to parameters tab if we're on a different tab
|
|
11
|
+
const activeTab = document.querySelector('.try-out-form .tab.active');
|
|
12
|
+
if (activeTab && activeTab.getAttribute('data-tab') !== 'parameters') {
|
|
13
|
+
const parametersTab = document.querySelector('.try-out-form .tab[data-tab="parameters"]');
|
|
14
|
+
if (parametersTab) {
|
|
15
|
+
TabManager.switchTab(parametersTab);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.showValidationError(`Please fill in the required parameters: ${emptyParams.join(', ')}`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Show loading state
|
|
24
|
+
this.setLoadingState(executeBtn, true);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const startTime = Date.now();
|
|
28
|
+
const url = FormManager.buildRequestUrl();
|
|
29
|
+
const headers = FormManager.getRequestHeaders();
|
|
30
|
+
const body = FormManager.getRequestBody();
|
|
31
|
+
const method = document.querySelector('.try-out-form')?.getAttribute('data-method') || 'GET';
|
|
32
|
+
|
|
33
|
+
const requestOptions = {
|
|
34
|
+
method: method.toUpperCase(),
|
|
35
|
+
headers: headers
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Add body for non-GET requests
|
|
39
|
+
if (body && !['GET', 'HEAD'].includes(method.toUpperCase())) {
|
|
40
|
+
if (typeof body === 'string') {
|
|
41
|
+
requestOptions.body = body;
|
|
42
|
+
} else {
|
|
43
|
+
requestOptions.body = JSON.stringify(body);
|
|
44
|
+
if (!headers['Content-Type']) {
|
|
45
|
+
requestOptions.headers['Content-Type'] = 'application/json';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const response = await fetch(url, requestOptions);
|
|
51
|
+
const responseTime = Date.now() - startTime;
|
|
52
|
+
const responseText = await response.text();
|
|
53
|
+
|
|
54
|
+
ModalManager.showResponseModal(response.status, responseText, responseTime);
|
|
55
|
+
|
|
56
|
+
} catch (error) {
|
|
57
|
+
let errorMessage = error.message || 'Unknown error occurred';
|
|
58
|
+
ModalManager.showResponseModal('Error', errorMessage);
|
|
59
|
+
} finally {
|
|
60
|
+
this.setLoadingState(executeBtn, false);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
setLoadingState(button, loading) {
|
|
65
|
+
button.disabled = loading;
|
|
66
|
+
button.innerHTML = '';
|
|
67
|
+
|
|
68
|
+
if (loading) {
|
|
69
|
+
const spinner = document.createElement('div');
|
|
70
|
+
spinner.className = 'spinner';
|
|
71
|
+
const text = document.createTextNode(' Sending...');
|
|
72
|
+
button.appendChild(spinner);
|
|
73
|
+
button.appendChild(text);
|
|
74
|
+
} else {
|
|
75
|
+
const playIcon = document.createElement('span');
|
|
76
|
+
playIcon.textContent = '▶';
|
|
77
|
+
const text = document.createTextNode(' Execute Request');
|
|
78
|
+
button.appendChild(playIcon);
|
|
79
|
+
button.appendChild(text);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
showValidationError(message) {
|
|
84
|
+
// Create or update validation error display
|
|
85
|
+
let errorDiv = document.getElementById('validation-error');
|
|
86
|
+
if (!errorDiv) {
|
|
87
|
+
errorDiv = document.createElement('div');
|
|
88
|
+
errorDiv.id = 'validation-error';
|
|
89
|
+
errorDiv.className = 'error-message show';
|
|
90
|
+
|
|
91
|
+
const executeBtn = document.getElementById('executeBtn');
|
|
92
|
+
if (executeBtn) {
|
|
93
|
+
executeBtn.parentNode.insertBefore(errorDiv, executeBtn);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
errorDiv.textContent = message;
|
|
98
|
+
errorDiv.classList.add('show');
|
|
99
|
+
|
|
100
|
+
// Auto-hide after 5 seconds
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
errorDiv.classList.remove('show');
|
|
103
|
+
}, 5000);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Global function for onclick handlers
|
|
108
|
+
window.executeRequest = () => RequestExecutor.executeRequest();
|
|
109
|
+
|
|
110
|
+
// Export for global access
|
|
111
|
+
window.RequestExecutor = RequestExecutor;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// Query parameter suggestions functionality
|
|
2
|
+
const TryOutSuggestions = {
|
|
3
|
+
init: function() {
|
|
4
|
+
this.suggestions = this.getAvailableSuggestions();
|
|
5
|
+
this.setupAutocomplete();
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
setupAutocomplete: function() {
|
|
9
|
+
// Setup event listeners for all query parameter inputs
|
|
10
|
+
document.addEventListener('click', (e) => {
|
|
11
|
+
// Hide all suggestion dropdowns when clicking outside
|
|
12
|
+
if (!e.target.matches('#queryParams input')) {
|
|
13
|
+
this.hideAllSuggestions();
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Initial setup for existing inputs
|
|
18
|
+
this.setupExistingInputs();
|
|
19
|
+
|
|
20
|
+
// Setup for the add button to attach listeners to new inputs
|
|
21
|
+
const addBtn = document.querySelector('.add-btn');
|
|
22
|
+
if (addBtn) {
|
|
23
|
+
addBtn.addEventListener('click', () => {
|
|
24
|
+
// Wait for DOM to update
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
this.setupExistingInputs();
|
|
27
|
+
}, 10);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
setupExistingInputs: function() {
|
|
33
|
+
// Find all parameter name inputs
|
|
34
|
+
const paramInputs = document.querySelectorAll('#queryParams .kv-item input:first-child');
|
|
35
|
+
paramInputs.forEach(input => {
|
|
36
|
+
// Skip if already initialized
|
|
37
|
+
if (input.dataset.autocompleteInitialized) return;
|
|
38
|
+
|
|
39
|
+
// Mark as initialized
|
|
40
|
+
input.dataset.autocompleteInitialized = 'true';
|
|
41
|
+
|
|
42
|
+
// Create suggestions container for this input
|
|
43
|
+
const suggestionsContainer = document.createElement('div');
|
|
44
|
+
suggestionsContainer.className = 'param-suggestions';
|
|
45
|
+
suggestionsContainer.id = 'suggestions-' + Math.random().toString(36).substr(2, 9);
|
|
46
|
+
input.parentNode.style.position = 'relative';
|
|
47
|
+
input.parentNode.appendChild(suggestionsContainer);
|
|
48
|
+
|
|
49
|
+
// Store reference to container
|
|
50
|
+
input.dataset.suggestionsContainer = suggestionsContainer.id;
|
|
51
|
+
|
|
52
|
+
// Add event listeners
|
|
53
|
+
input.addEventListener('focus', () => this.showSuggestions(input));
|
|
54
|
+
input.addEventListener('input', () => this.filterSuggestions(input));
|
|
55
|
+
input.addEventListener('keydown', (e) => this.handleKeyNavigation(e, input));
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
getAvailableSuggestions: function() {
|
|
60
|
+
const suggestions = [];
|
|
61
|
+
|
|
62
|
+
// Try to get query parameters from the page context
|
|
63
|
+
if (window.queryParametersData) {
|
|
64
|
+
const data = window.queryParametersData;
|
|
65
|
+
|
|
66
|
+
// Add filter fields
|
|
67
|
+
if (data.filter_fields && data.filter_fields.length > 0) {
|
|
68
|
+
suggestions.push(...data.filter_fields);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Add search if available
|
|
72
|
+
if (data.search_fields && data.search_fields.length > 0) {
|
|
73
|
+
suggestions.push('search');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Add ordering if available
|
|
77
|
+
if (data.ordering_fields && data.ordering_fields.length > 0) {
|
|
78
|
+
suggestions.push('ordering');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Add pagination
|
|
82
|
+
if (data.pagination_fields && data.pagination_fields.length > 0) {
|
|
83
|
+
suggestions.push(...data.pagination_fields);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Default common parameters
|
|
88
|
+
if (suggestions.length === 0) {
|
|
89
|
+
suggestions.push('search', 'ordering', 'page', 'page_size');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Remove duplicates and return
|
|
93
|
+
return [...new Set(suggestions)];
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
showSuggestions: function(input) {
|
|
97
|
+
const container = document.getElementById(input.dataset.suggestionsContainer);
|
|
98
|
+
if (!container) return;
|
|
99
|
+
|
|
100
|
+
// Clear existing suggestions
|
|
101
|
+
container.innerHTML = '';
|
|
102
|
+
|
|
103
|
+
// Filter suggestions based on input value
|
|
104
|
+
const inputValue = input.value.toLowerCase();
|
|
105
|
+
const filteredSuggestions = this.suggestions.filter(suggestion =>
|
|
106
|
+
suggestion.toLowerCase().includes(inputValue)
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
if (filteredSuggestions.length === 0) {
|
|
110
|
+
container.classList.remove('show');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Add suggestions to container
|
|
115
|
+
filteredSuggestions.forEach(suggestion => {
|
|
116
|
+
const suggestionElement = document.createElement('div');
|
|
117
|
+
suggestionElement.className = 'param-suggestion';
|
|
118
|
+
suggestionElement.textContent = suggestion;
|
|
119
|
+
suggestionElement.addEventListener('click', (e) => {
|
|
120
|
+
e.stopPropagation();
|
|
121
|
+
this.selectSuggestion(input, suggestion);
|
|
122
|
+
});
|
|
123
|
+
container.appendChild(suggestionElement);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Show suggestions
|
|
127
|
+
container.classList.add('show');
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
filterSuggestions: function(input) {
|
|
131
|
+
// Just re-show suggestions with current filter
|
|
132
|
+
this.showSuggestions(input);
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
hideAllSuggestions: function() {
|
|
136
|
+
document.querySelectorAll('.param-suggestions').forEach(container => {
|
|
137
|
+
container.classList.remove('show');
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
selectSuggestion: function(input, suggestion) {
|
|
142
|
+
// Set input value
|
|
143
|
+
input.value = suggestion;
|
|
144
|
+
|
|
145
|
+
// Hide suggestions
|
|
146
|
+
const container = document.getElementById(input.dataset.suggestionsContainer);
|
|
147
|
+
if (container) {
|
|
148
|
+
container.classList.remove('show');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Focus on value input
|
|
152
|
+
const valueInput = input.nextElementSibling;
|
|
153
|
+
if (valueInput) {
|
|
154
|
+
valueInput.focus();
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
handleKeyNavigation: function(event, input) {
|
|
159
|
+
const container = document.getElementById(input.dataset.suggestionsContainer);
|
|
160
|
+
if (!container || !container.classList.contains('show')) return;
|
|
161
|
+
|
|
162
|
+
const suggestions = container.querySelectorAll('.param-suggestion');
|
|
163
|
+
if (suggestions.length === 0) return;
|
|
164
|
+
|
|
165
|
+
// Find currently selected suggestion
|
|
166
|
+
const selectedIndex = Array.from(suggestions).findIndex(el => el.classList.contains('selected'));
|
|
167
|
+
|
|
168
|
+
switch (event.key) {
|
|
169
|
+
case 'ArrowDown':
|
|
170
|
+
event.preventDefault();
|
|
171
|
+
this.navigateSuggestion(suggestions, selectedIndex, 1);
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case 'ArrowUp':
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
this.navigateSuggestion(suggestions, selectedIndex, -1);
|
|
177
|
+
break;
|
|
178
|
+
|
|
179
|
+
case 'Enter':
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
if (selectedIndex >= 0) {
|
|
182
|
+
this.selectSuggestion(input, suggestions[selectedIndex].textContent);
|
|
183
|
+
} else if (suggestions.length > 0) {
|
|
184
|
+
this.selectSuggestion(input, suggestions[0].textContent);
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
|
|
188
|
+
case 'Escape':
|
|
189
|
+
event.preventDefault();
|
|
190
|
+
container.classList.remove('show');
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
navigateSuggestion: function(suggestions, currentIndex, direction) {
|
|
196
|
+
// Remove current selection
|
|
197
|
+
if (currentIndex >= 0) {
|
|
198
|
+
suggestions[currentIndex].classList.remove('selected');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Calculate new index
|
|
202
|
+
let newIndex;
|
|
203
|
+
if (currentIndex < 0) {
|
|
204
|
+
newIndex = direction > 0 ? 0 : suggestions.length - 1;
|
|
205
|
+
} else {
|
|
206
|
+
newIndex = (currentIndex + direction + suggestions.length) % suggestions.length;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Select new suggestion
|
|
210
|
+
suggestions[newIndex].classList.add('selected');
|
|
211
|
+
suggestions[newIndex].scrollIntoView({ block: 'nearest' });
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Export for global access
|
|
216
|
+
window.TryOutSuggestions = TryOutSuggestions;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Tab management functionality
|
|
2
|
+
const TabManager = {
|
|
3
|
+
init: function() {
|
|
4
|
+
document.querySelectorAll('.try-out-form .tab').forEach(tab => {
|
|
5
|
+
tab.addEventListener('click', () => {
|
|
6
|
+
this.switchTab(tab);
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
switchTab: function(activeTab) {
|
|
12
|
+
// Remove active class from all tabs and contents
|
|
13
|
+
document.querySelectorAll('.try-out-form .tab').forEach(t => t.classList.remove('active'));
|
|
14
|
+
document.querySelectorAll('.try-out-form .tab-content').forEach(c => c.classList.remove('active'));
|
|
15
|
+
|
|
16
|
+
// Add active class to clicked tab
|
|
17
|
+
activeTab.classList.add('active');
|
|
18
|
+
|
|
19
|
+
// Show corresponding content
|
|
20
|
+
const tabName = activeTab.getAttribute('data-tab');
|
|
21
|
+
const content = document.getElementById(tabName + 'Tab');
|
|
22
|
+
if (content) {
|
|
23
|
+
content.classList.add('active');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Initialize tabs when DOM is ready
|
|
29
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
30
|
+
TabManager.init();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Export for global access
|
|
34
|
+
window.TabManager = TabManager;
|