Skip to main content
Glama
nrwl

Nx MCP Server

Official
by nrwl
field-list.ts7.41 kB
import { Option } from '@nx-console/shared-schema'; import { LitElement, TemplateResult, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { extractItemOptions } from '../utils/generator-schema-utils'; import { EditorContext } from '@nx-console/shared-ui-components'; import { GeneratorContextContext } from '../contexts/generator-context-context'; type OptionWithMetadata = { option: Option; isInSearchResults: boolean; isImportant: boolean; }; @customElement('field-list') export class FieldList extends GeneratorContextContext( EditorContext(LitElement), ) { @property() options: Option[]; @property() searchValue: string | undefined; @state() private showMore = false; private toggleShowMore() { this.showMore = !this.showMore; } protected render() { const { optionsWithMetadata, numOfImportantOptions, numOfOtherOptions } = getOptionsWithMetadata(this.options, this.searchValue); const shouldShowMoreOptions = this.showMore || !!this.searchValue || numOfImportantOptions === 0; const shouldHideShowMoreButton = !!this.searchValue || numOfOtherOptions === 0 || numOfImportantOptions === 0; return html` <div class="flex h-full w-full"> <div class="border-separator ${this.editor === 'intellij' ? 'hidden md:block' : 'max-sm:hidden md:w-64'} fixed h-full w-52 overflow-y-auto border-r-2 p-6" > ${this.renderOptionNav(optionsWithMetadata, shouldShowMoreOptions)} </div> <div class="${this.editor === 'intellij' ? 'md:ml-52 md:p-6' : 'sm:ml-52 sm:p-6 md:ml-64'} w-full pt-6" > ${this.renderOptionsWithDivider( optionsWithMetadata, shouldShowMoreOptions, shouldHideShowMoreButton, )} </div> </div> `; } private renderOptionNav( optionsWithMetadata: OptionWithMetadata[], shouldShowMoreOptions: boolean, ): TemplateResult { return html` <ul> ${optionsWithMetadata.map((optionWithMetadata) => { const hidden = this.searchValue && !optionWithMetadata.isInSearchResults; const greyedOut = !shouldShowMoreOptions && !optionWithMetadata.isImportant; return html`<field-nav-item class="${hidden ? 'hidden' : ''}" .option="${optionWithMetadata.option}" .greyedOut="${greyedOut}" @click=${(e: Event) => this.handleTreeClickEvent(e, greyedOut)} ></field-nav-item>`; })} </ul> `; } private renderOptionsWithDivider( optionsWithMetadata: OptionWithMetadata[], shouldShowMoreOptions: boolean, shouldHideShowMoreButton: boolean, ): TemplateResult { // we need to render all options but hide some so the component instances are persisted const renderOption = ( optionWithMetadata: OptionWithMetadata, hidden = false, ) => { const componentTag = getFieldComponent(optionWithMetadata.option); return html` <div class="${hidden ? 'hidden' : ''} mb-4" id="option-${optionWithMetadata.option.name}" > ${componentTag} </div>`; }; if ( this.searchValue && optionsWithMetadata.every((opt) => !opt.isInSearchResults) ) { return html` <div class="flex flex-col"> <div class="m-auto"> Couldn't find any options matching "${this.searchValue}" </div> <button-element appearance="secondary" text="Clear Search" class="m-auto pt-2" @click=${() => this.clearSearch()} ></button-element> <div></div> </div>`; } // if there is a search value, show all matching options regardless of importance if (this.searchValue) { return html`<div> ${optionsWithMetadata.map((opt) => renderOption(opt, !opt.isInSearchResults), )} </div>`; } const importantOptions = optionsWithMetadata.filter( (opt) => opt.isImportant, ); const otherOptions = optionsWithMetadata.filter((opt) => !opt.isImportant); return html` ${importantOptions.map((opt) => renderOption(opt, false))} <show-more-divider .showMore=${this.showMore} @show-more=${this.toggleShowMore} class="${shouldHideShowMoreButton ? 'hidden' : ''}" ></show-more-divider> ${otherOptions.map((opt) => renderOption(opt, !shouldShowMoreOptions))} `; } protected firstUpdated(): void { this.updateComplete.then(() => { const field = Array.from(this.renderRoot.querySelectorAll('*')).find( (el) => el.id.toLowerCase().endsWith('-field') && el instanceof HTMLElement, ); if (field) { (field as HTMLElement).focus(); } }); } private handleTreeClickEvent(event: Event, wasGreyedOut: boolean) { const optionName = (event.target as HTMLElement).innerText; if (wasGreyedOut) { this.showMore = true; } setTimeout(() => { const element = this.querySelector(`#option-${optionName}`); if (!element) { return; } element.scrollIntoView({ behavior: 'smooth', block: 'start' }); // focus field after scrolling to option const fieldElement = this.querySelector(`#${optionName}-field`); if (!fieldElement) { return; } const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting) { (fieldElement as HTMLElement).focus(); observer.disconnect(); } }, { rootMargin: '0px', threshold: 1.0 }, ); observer.observe(element); }, 100); } private clearSearch() { const event = new CustomEvent('clear-search', {}); this.dispatchEvent(event); } protected createRenderRoot(): Element | ShadowRoot { return this; } } const getFieldComponent = (option: Option) => { if (option.type === 'boolean') { return html` <checkbox-field .option=${option}></checkbox-field>`; } if (option.type === 'array') { if (option.items) { return html` <multiselect-field .option=${option}></multiselect-field> `; } else { return html` <array-field .option=${option}></array-field>`; } } if (option.items) { if (extractItemOptions(option).length > 10) { return html`<autocomplete-field .option=${option}></autocomplete-field>`; } else { return html` <select-field .option=${option}></select-field>`; } } return html` <input-field .option=${option}></input-field>`; }; const getOptionsWithMetadata = ( options: Option[], searchValue: string | undefined, ): { optionsWithMetadata: OptionWithMetadata[]; numOfImportantOptions: number; numOfOtherOptions: number; } => { const optionsWithMetadata = options.map((option) => ({ option, isInSearchResults: !searchValue || option.name.includes(searchValue), isImportant: option.isRequired || option['x-priority'] === 'important', })); return { optionsWithMetadata, numOfImportantOptions: optionsWithMetadata.filter( (option) => option.isImportant, ).length, numOfOtherOptions: optionsWithMetadata.filter( (option) => !option.isImportant, ).length, }; };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nrwl/nx-console'

If you have feedback or need assistance with the MCP directory API, please join our Discord server