diff --git a/projects/demo/src/app/pages/modules/search/search.page.ts b/projects/demo/src/app/pages/modules/search/search.page.ts index d0e93cd09..bd01cd3ee 100644 --- a/projects/demo/src/app/pages/modules/search/search.page.ts +++ b/projects/demo/src/app/pages/modules/search/search.page.ts @@ -6,12 +6,14 @@ import { AlertModal } from "../../../modals/alert.modal"; const exampleStandardTemplate = `
Has icon? + Allow empty query?
`; @@ -57,6 +59,18 @@ export class SearchPage { description: "Sets whether or not the search displays an icon.", defaultValue: "true" }, + { + name: "allowEmptyQuery", + type: "boolean", + description: "Sets whether the search element display result with empty query.", + defaultValue: "false" + }, + { + name: "resetQueryOnChange", + type: "boolean", + description: "Sets whether the query is reset if options change.", + defaultValue: "true" + }, { name: "options", type: "T[]", @@ -165,6 +179,7 @@ export class SearchExampleStandard { "Yellow", "Zebra"]; public hasIcon:boolean = true; + public allowEmptyQuery:boolean = true; public get options():string[] { return SearchExampleStandard.standardOptions; diff --git a/projects/lib/src/lib/modules/popup/public.ts b/projects/lib/src/lib/modules/popup/public.ts index 21536b4c0..35f60f172 100644 --- a/projects/lib/src/lib/modules/popup/public.ts +++ b/projects/lib/src/lib/modules/popup/public.ts @@ -8,6 +8,8 @@ export { PopupTrigger, SuiPopupDirective, SuiPopup, + SuiPopupController, + SuiPopupTemplateController, } from "./internal"; import { PositioningPlacement } from "../../misc/util/internal"; diff --git a/projects/lib/src/lib/modules/search/components/search.ts b/projects/lib/src/lib/modules/search/components/search.ts index d0e92ce59..1f3279dcb 100644 --- a/projects/lib/src/lib/modules/search/components/search.ts +++ b/projects/lib/src/lib/modules/search/components/search.ts @@ -77,6 +77,23 @@ export class SuiSearch implements AfterViewInit { @Input() public hasIcon:boolean; + // Sets whether the query is reset if options change. + @Input() + public set resetQueryOnChange(resetQueryOnChange:boolean) { + this.searchService.resetQueryOnChange = resetQueryOnChange; + } + + // Sets whether the search element display result with empty query. + @Input() + public set allowEmptyQuery(allowEmptyQuery:boolean) { + this._allowEmptyQuery = allowEmptyQuery; + this.searchService.allowEmptyQuery = allowEmptyQuery; + } + public get allowEmptyQuery():boolean { + return this._allowEmptyQuery; + } + + private _allowEmptyQuery!:boolean; private _placeholder!:string; // Gets & sets the placeholder text displayed inside the text input. @@ -106,7 +123,7 @@ export class SuiSearch implements AfterViewInit { // Initialise a delayed search. this.searchService.updateQueryDelayed(query, () => // Set the results open state depending on whether a query has been entered. - this.dropdownService.setOpenState(this.searchService.query.length > 0)); + this.dropdownService.setOpenState(this.searchService.query.length > 0 || this.allowEmptyQuery)); } @Input() @@ -196,6 +213,8 @@ export class SuiSearch implements AfterViewInit { this.hasClasses = true; this.tabindex = 0; this.hasIcon = true; + this.allowEmptyQuery = false; + this.resetQueryOnChange = true; this.retainSelectedResult = true; this.searchDelay = 200; this.maxResults = 7; @@ -239,7 +258,7 @@ export class SuiSearch implements AfterViewInit { } private open():void { - if (this.searchService.query.length > 0) { + if (this.searchService.query.length > 0 || this.allowEmptyQuery) { // Only open on click when there is a query entered. this.dropdownService.setOpenState(true); } @@ -247,7 +266,6 @@ export class SuiSearch implements AfterViewInit { @HostListener("focusout", ["$event"]) public onFocusOut(e:IFocusEvent):void { - console.log(e); if (!this._element.nativeElement.contains(e.relatedTarget)) { this.dropdownService.setOpenState(false); } diff --git a/projects/lib/src/lib/modules/search/services/search.service.ts b/projects/lib/src/lib/modules/search/services/search.service.ts index be71997ea..a83a96b74 100644 --- a/projects/lib/src/lib/modules/search/services/search.service.ts +++ b/projects/lib/src/lib/modules/search/services/search.service.ts @@ -59,7 +59,9 @@ export class SearchService { return this._results; } - private _query!:string; + private _query:string; + // Allows the query to be empty when the options change. + public resetQueryOnChange:boolean; // Allows the empty query to produce results. public allowEmptyQuery:boolean; // How long to delay the search for when using updateQueryDelayed. Stored in ms. @@ -77,7 +79,7 @@ export class SearchService { return this._isSearching; } - constructor(allowEmptyQuery:boolean = false) { + constructor(allowEmptyQuery = false) { this._options = []; this.optionsFilter = (os, q) => { // Convert the query string to a RegExp. @@ -99,6 +101,8 @@ export class SearchService { // Set default values and reset. this.allowEmptyQuery = allowEmptyQuery; + this._query = ""; + this.resetQueryOnChange = true; this.searchDelay = 0; this.reset(); } @@ -203,6 +207,10 @@ export class SearchService { this._results = []; this._resultsCache = {}; this._isSearching = false; - this.updateQuery(""); + if (this.resetQueryOnChange || !this.query) { + this.updateQuery(""); + } else { + this.updateQuery(this.query); + } } } diff --git a/projects/lib/src/lib/modules/select/classes/select-base.ts b/projects/lib/src/lib/modules/select/classes/select-base.ts index 0fb73e419..f5e7774d8 100644 --- a/projects/lib/src/lib/modules/select/classes/select-base.ts +++ b/projects/lib/src/lib/modules/select/classes/select-base.ts @@ -1,6 +1,6 @@ import { ViewChild, HostBinding, ElementRef, HostListener, Input, ContentChildren, QueryList, - AfterContentInit, TemplateRef, ViewContainerRef, ContentChild, EventEmitter, Output, OnDestroy, Directive + AfterViewInit, TemplateRef, ViewContainerRef, ContentChild, EventEmitter, Output, OnDestroy, Directive } from "@angular/core"; import { Subscription } from "rxjs"; import { DropdownService, SuiDropdownMenu } from "../../dropdown/internal"; @@ -17,11 +17,11 @@ export interface IOptionContext extends ITemplateRefContext { // We use generic type T to specify the type of the options we are working with, // and U to specify the type of the property of the option used as the value. @Directive() -export abstract class SuiSelectBase implements AfterContentInit, OnDestroy { +export abstract class SuiSelectBase implements AfterViewInit, OnDestroy { public dropdownService:DropdownService; public searchService:SearchService; - @ViewChild(SuiDropdownMenu, { static: true }) + @ViewChild(SuiDropdownMenu) protected _menu!:SuiDropdownMenu; // Keep track of all of the rendered select options. (Rendered by the user using *ngFor). @@ -43,7 +43,10 @@ export abstract class SuiSelectBase implements AfterContentInit, OnDestroy @HostBinding("class.visible") public get isVisible():boolean { - return this._menu.isVisible; + if (this._menu) { + return this._menu.isVisible; + } + return false; } @Input() @@ -246,7 +249,7 @@ export abstract class SuiSelectBase implements AfterContentInit, OnDestroy this.hasClasses = true; } - public ngAfterContentInit():void { + public ngAfterViewInit():void { this._menu.service = this.dropdownService; // We manually specify the menu items to the menu because the @ContentChildren doesn't pick up our dynamically rendered items. this._menu.items = this._renderedOptions; @@ -287,7 +290,7 @@ export abstract class SuiSelectBase implements AfterContentInit, OnDestroy // The search delay is set to the transition duration to ensure results // aren't rendered as the select closes as that causes a sudden flash. if (delayed) { - this.searchService.searchDelay = this._menu.menuTransitionDuration; + this.searchService.searchDelay = this._menu?.menuTransitionDuration; this.searchService.updateQueryDelayed(""); } else { this.searchService.updateQuery("");