diff --git a/src/components/fuzzySearch.ts b/src/components/fuzzySearch.ts
index f5ace0c..f6609a3 100644
--- a/src/components/fuzzySearch.ts
+++ b/src/components/fuzzySearch.ts
@@ -3,15 +3,23 @@ import { WebviewApi } from "vscode-webview";
export enum SearchType {
variables = 'variables',
- features = 'features'
+ features = 'features',
+ projects = 'projects',
}
+
+type LocalStorageKey = `${SearchType}${string}` | SearchType
+
//View Provider
-export const getCustomDropdown = (optionElements: string, selectedValue: string) => {
+export const getCustomDropdown = (optionElements: string, selectedValue: string, folderIndex?: number, dataType?: string) => {
+ const folderAttribute = folderIndex !== undefined ? `data-folder="${folderIndex}"` : ''
+ const dataTypeAttribute = dataType ? `data-type="${dataType}"` : ''
+ const inputId = folderIndex !== undefined ? `id="dropdown-input-${folderIndex}"` : ''
+ const optionsListId = folderIndex !== undefined ? `id="dropdown-optionsList-${folderIndex}"` : ''
return `
-
+
^
-
`
@@ -28,11 +36,13 @@ export const getCustomDropdown = (optionElements: string, selectedValue: string)
*/
export function handleFuzzySearchDropdown(
vscode: WebviewApi
,
- searchType: SearchType,
+ localStorageKey: LocalStorageKey,
fuseOptions: Fuse.IFuseOptions = {
keys: ['label', 'value'],
threshold: 0.5,
- }
+ },
+ inputSelector: string = '.dropdown-input',
+ optionsListSelector: string = '.dropdown-options'
) {
window.addEventListener('message', (event) => {
const message = event.data
@@ -42,17 +52,10 @@ export function handleFuzzySearchDropdown(
}
})
- const localStorageData = localStorage.getItem(searchType)
-
- if (!localStorageData) {
- return
- }
-
- const input = document.querySelector('.dropdown-input') as HTMLInputElement
- const optionsList = document.querySelector('.dropdown-options') as HTMLDivElement
-
- const searchData: { label: string, value?: string }[] = JSON.parse(localStorageData)
- const fuse = new Fuse([...searchData], fuseOptions)
+ const input = document.querySelector(inputSelector) as HTMLInputElement
+ const optionsList = document.querySelector(optionsListSelector) as HTMLDivElement
+ const folderIndex = optionsList?.getAttribute('data-folder')
+ const selectedValueKey = `selectedValue${folderIndex || ''}`
const createAndAppendOption = (displayValue: string, dataAttributeValue?: string) => {
const option = document.createElement('div')
@@ -63,15 +66,57 @@ export function handleFuzzySearchDropdown(
// Event listeners to handle dropdown behavior
document.addEventListener('click', () => {
+ if (!optionsList?.classList.contains('visible')) {
+ return
+ }
+ const selectedValue = localStorage.getItem(selectedValueKey)
+ if (selectedValue) {
+ input.value = selectedValue
+ }
optionsList?.classList.remove('visible')
})
+ optionsList?.addEventListener('click', (event) => {
+ const selectedOption = event.target as HTMLElement
+ if (selectedOption.tagName === 'DIV') {
+ const selectedValue = selectedOption.getAttribute('data-value')
+ const selectedType = optionsList.getAttribute('data-type')
+ if (selectedValue) {
+ input.value = selectedOption.innerText
+ vscode.postMessage({
+ type: selectedType || 'key',
+ value: selectedValue,
+ ...(optionsList.dataset.folder && { folderIndex: optionsList.dataset.folder })
+ })
+ optionsList.classList.remove('visible')
+ }
+ }
+ })
+
input?.addEventListener('click', (event) => {
+ const { value } = event.target as HTMLInputElement
+ localStorage.setItem(selectedValueKey, value)
event.stopPropagation()
optionsList?.classList.toggle('visible')
})
+
+ let localStorageData: string | null = localStorage.getItem(localStorageKey)
+ const refreshLocalStorageData = () => {
+ localStorageData = localStorage.getItem(localStorageKey)
+ }
input?.addEventListener('input', () => {
+ if (!localStorageData) {
+ vscode.postMessage({
+ type: 'setFuseData',
+ ...(folderIndex && { folderIndex: folderIndex })
+ })
+ refreshLocalStorageData()
+ return
+ }
+
+ const searchData: { label: string, value?: string }[] = JSON.parse(localStorageData)
+ const fuse = new Fuse([...searchData], fuseOptions)
const inputValue = input?.value.toLowerCase().trim()
optionsList.innerHTML = ''
@@ -95,18 +140,4 @@ export function handleFuzzySearchDropdown(
}
})
- optionsList.addEventListener('click', (event) => {
- const selectedOption = event.target as HTMLElement
- if (selectedOption.tagName === 'DIV') {
- const selectedValue = selectedOption.getAttribute('data-value')
- if (selectedValue) {
- vscode.postMessage({
- type: 'key',
- value: selectedValue,
- })
- optionsList.classList.remove('visible')
- }
- }
- })
-
}
\ No newline at end of file
diff --git a/src/views/home/HomeViewProvider.ts b/src/views/home/HomeViewProvider.ts
index 801fdae..d943cfe 100644
--- a/src/views/home/HomeViewProvider.ts
+++ b/src/views/home/HomeViewProvider.ts
@@ -1,24 +1,27 @@
import * as vscode from 'vscode'
import { getNonce } from '../../utils/getNonce'
-import { BaseCLIController, OrganizationsCLIController, ProjectsCLIController } from '../../cli'
+import { BaseCLIController, OrganizationsCLIController, Project, ProjectsCLIController } from '../../cli'
import path from 'path'
import { COMMAND_LOGOUT } from '../../commands/logout'
import { KEYS, StateManager } from '../../StateManager'
import { updateRepoConfig } from '../../utils/updateRepoConfigProject'
import { executeRefreshAllCommand } from '../../commands'
import { loginAndRefresh } from '../../utils/loginAndRefresh'
+import { SearchType, getCustomDropdown } from '../../components/fuzzySearch'
type HomeViewMessage =
| { type: 'project' | 'organization', value: string, folderIndex: number }
| { type: 'config', folderIndex: number }
| { type: 'login', folderIndex: number }
| { type: 'logout' }
+ | { type: 'setFuseData', folderIndex: number }
export class HomeViewProvider implements vscode.WebviewViewProvider {
_view?: vscode.WebviewView
_doc?: vscode.TextDocument
isRefreshing: boolean = false
webviewIsDisposed: boolean = false
+ projectOptions: Record = []
constructor(private readonly _extensionUri: vscode.Uri) {}
@@ -46,6 +49,11 @@ export class HomeViewProvider implements vscode.WebviewViewProvider {
const folder = vscode.workspace.workspaceFolders?.[data.folderIndex]
if (!folder) { return }
+ if (data.type === 'setFuseData') {
+ this.setFuseSearchData(this.projectOptions[folder.index], folder)
+ return
+ }
+
if (data.type === 'login') {
await loginAndRefresh([folder])
webviewView.webview.html = await this._getHtmlForWebview(webviewView.webview)
@@ -116,6 +124,21 @@ export class HomeViewProvider implements vscode.WebviewViewProvider {
await this.refreshAll()
}
+ private postMessageToWebview(message: unknown) {
+ this._view?.webview.postMessage(message)
+ }
+
+ private setFuseSearchData(data: Data[], folder: vscode.WorkspaceFolder) {
+ this.postMessageToWebview({ type: 'searchData', searchType: `${SearchType.projects}${folder.index}`, value: JSON.stringify(data)})
+ }
+
+ private getSelectedProject(activeProjectKey: string | undefined, projects: Record) {
+ if (!activeProjectKey) {
+ return ''
+ }
+ return projects[activeProjectKey]?.name || projects[activeProjectKey]?.key
+ }
+
private async getBodyHtml(folder: vscode.WorkspaceFolder, showHeader?: boolean): Promise {
const organizationsController = new OrganizationsCLIController(folder)
const projectsController = new ProjectsCLIController(folder)
@@ -125,7 +148,13 @@ export class HomeViewProvider implements vscode.WebviewViewProvider {
const activeOrganizationName = StateManager.getFolderState(folder.name, KEYS.ORGANIZATION)?.name
const isLoggedIn = StateManager.getFolderState(folder.name, KEYS.LOGGED_IN)
- const projectId = `project${folder.index}`
+ const projectOptions = Object.values(projects).map((project) => ({
+ label: project.name || project.key,
+ value: project.key,
+ }))
+ this.projectOptions[folder.index] = projectOptions
+ this.setFuseSearchData(projectOptions, folder)
+
const organizationId = `organization${folder.index}`
const editButtonId = `editConfigButton${folder.index}`
@@ -133,13 +162,10 @@ export class HomeViewProvider implements vscode.WebviewViewProvider {
const orgOptions = Object.values(organizations).map((organization) =>
`${organization.display_name || organization.name}`
)
- const projectOptions = Object.values(projects).map((project) =>
- `${project.name}`
+ const projectOptionElements = Object.values(projects).map((project) =>
+ `${project.name}
`
)
- if (!activeProjectKey) {
- projectOptions.unshift(`Select a project...`)
- }
-
+ const selectedProject = this.getSelectedProject(activeProjectKey, projects)
return `
${
showHeader ? `
@@ -162,9 +188,7 @@ export class HomeViewProvider implements vscode.WebviewViewProvider {
-
- ${projectOptions.join('')}
-
+ ${getCustomDropdown(projectOptionElements.join(''), selectedProject, folder.index, 'project')}
@@ -207,6 +231,14 @@ export class HomeViewProvider implements vscode.WebviewViewProvider {
const homeViewStylesUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, 'media', 'styles', 'homeView.css'),
)
+ const dropdownStylesUri = webview.asWebviewUri(
+ vscode.Uri.joinPath(
+ this._extensionUri,
+ 'media',
+ 'styles',
+ 'customDropdown.css',
+ ),
+ )
const webViewUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, 'out', 'homeView.js'),
)
@@ -236,6 +268,7 @@ export class HomeViewProvider implements vscode.WebviewViewProvider {
+
diff --git a/src/views/inspector/InspectorViewProvider.ts b/src/views/inspector/InspectorViewProvider.ts
index a70cc3c..8be1e37 100644
--- a/src/views/inspector/InspectorViewProvider.ts
+++ b/src/views/inspector/InspectorViewProvider.ts
@@ -32,6 +32,7 @@ type InspectorViewMessage =
}
| { type: 'folder'; value: number }
| { type: 'jsonReadonly'; value: string }
+ | { type: 'setFuseData' }
const htmlMessage = (message: string) => `
@@ -96,16 +97,8 @@ export class InspectorViewProvider implements vscode.WebviewViewProvider {
this.selectedKey = ''
- // Send variables data to the webview to initialize fuse
- const variableOptions = this.orderedVariables.map(variable => ({
- label: variable.key
- }))
- const featureOptions = this.orderedFeatures.map(feature => ({
- label: feature.name || feature.key,
- value: feature._id
- }))
- this.postMessageToWebview({type: 'searchData', searchType: SearchType.variables, value: JSON.stringify(variableOptions)})
- this.postMessageToWebview({type: 'searchData', searchType: SearchType.features, value: JSON.stringify(featureOptions)})
+ // Send data to the webview to initialize Fuse
+ this.setFuseSearchData()
} catch (e) {
vscode.window.showErrorMessage(
`Error initializing features and variables in inspector: ${e}`,
@@ -133,6 +126,11 @@ export class InspectorViewProvider implements vscode.WebviewViewProvider {
webviewView.webview.onDidReceiveMessage(
async (data: InspectorViewMessage) => {
+ if (data.type === 'setFuseData') {
+ this.setFuseSearchData()
+ return
+ }
+
if (data.type === 'variableOrFeature') {
this.selectedType = data.value
this.selectedKey = ''
@@ -175,6 +173,18 @@ export class InspectorViewProvider implements vscode.WebviewViewProvider {
this.webviewIsDisposed = false
}
+ private setFuseSearchData() {
+ const variableOptions = this.orderedVariables.map(variable => ({
+ label: variable.key
+ }))
+ const featureOptions = this.orderedFeatures.map(feature => ({
+ label: feature.name || feature.key,
+ value: feature._id
+ }))
+ this.postMessageToWebview({ type: 'searchData', searchType: SearchType.variables, value: JSON.stringify(variableOptions) })
+ this.postMessageToWebview({ type: 'searchData', searchType: SearchType.features, value: JSON.stringify(featureOptions) })
+ }
+
private async refreshInspectorView() {
if (!this._view?.webview) {
return
diff --git a/src/webview/homeView.ts b/src/webview/homeView.ts
index 34a88d0..ceb54b2 100644
--- a/src/webview/homeView.ts
+++ b/src/webview/homeView.ts
@@ -1,4 +1,5 @@
import { provideVSCodeDesignSystem, vsCodeDropdown, vsCodeOption, Dropdown } from "@vscode/webview-ui-toolkit";
+import { SearchType, handleFuzzySearchDropdown } from "../components/fuzzySearch";
provideVSCodeDesignSystem().register(vsCodeDropdown(), vsCodeOption());
@@ -46,6 +47,18 @@ function main() {
dropdown.addEventListener('change', handleDropdownValueChange);
}
+ const optionsLists = document.querySelectorAll('.dropdown-options') as NodeListOf
+ optionsLists.forEach(optionsList => {
+ const folderIndexAttribute = optionsList.getAttribute('data-folder')
+ const folderIndex = folderIndexAttribute ? folderIndexAttribute : null
+
+ if (folderIndex) {
+ handleFuzzySearchDropdown(vscode, `${SearchType.projects}${folderIndex}`, undefined, `#dropdown-input-${folderIndex}`, `#dropdown-optionsList-${folderIndex}`)
+ } else {
+ handleFuzzySearchDropdown(vscode, SearchType.projects)
+ }
+ })
+
const editConfigButtons = document.getElementsByClassName("edit-config-button")
for (let i = 0; i < editConfigButtons.length; i++) {
// Edit config button
diff --git a/src/webview/inspectorView.ts b/src/webview/inspectorView.ts
index 7d6d1a4..0cc8453 100644
--- a/src/webview/inspectorView.ts
+++ b/src/webview/inspectorView.ts
@@ -20,6 +20,10 @@ if (focusedElement) {
window.addEventListener('message', (event) => {
const message = event.data
+ // if message type is 'searchData' then we don't want to post it back to the extension
+ if (message.type === 'searchData') {
+ return
+ }
vscode.postMessage(message)
})