Skip to content

Commit

Permalink
Add @searchFieldPosition argument (#1864)
Browse files Browse the repository at this point in the history
* Add @searchFieldPosition argument

* Add option `searchFieldPosition=trigger` for single select

* Make position value strict

* Add `searchFieldPosition` in docs

* Fix lint

* Handle blur directly in input component instead global

* Fix lint

* Fix input clear

* Fix searchFieldPosition typing
  • Loading branch information
mkszepp authored Dec 11, 2024
1 parent 05bd330 commit 5a97bea
Show file tree
Hide file tree
Showing 24 changed files with 380 additions and 22 deletions.
1 change: 1 addition & 0 deletions docs/app/components/snippets/the-search-2.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<br>
<br>
<PowerSelect
@searchEnabled={{true}}
@options={{this.diacritics}}
@selected={{this.fellow}}
@labelText="Name"
Expand Down
9 changes: 9 additions & 0 deletions docs/app/components/snippets/the-search-8.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<PowerSelect
@searchEnabled={{true}}
@searchFieldPosition="trigger"
@options={{this.diacritics}}
@selected={{this.selectedDiacritic}}
@labelText="Name"
@onChange={{fn (mut this.selectedDiacritic)}} as |name|>
{{name}}
</PowerSelect>
5 changes: 5 additions & 0 deletions docs/app/components/snippets/the-search-8.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Component from '@glimmer/component';

export default class extends Component {
diacritics = ['María', 'Søren Larsen', 'João', 'Saša Jurić', 'Íñigo'];
}
9 changes: 9 additions & 0 deletions docs/app/components/snippets/the-search-9.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<PowerSelectMultiple
@searchEnabled={{true}}
@searchFieldPosition="before-options"
@options={{this.diacritics}}
@selected={{this.selectedDiacritic}}
@labelText="Name"
@onChange={{fn (mut this.selectedDiacritic)}} as |name|>
{{name}}
</PowerSelectMultiple>
5 changes: 5 additions & 0 deletions docs/app/components/snippets/the-search-9.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Component from '@glimmer/component';

export default class extends Component {
diacritics = ['María', 'Søren Larsen', 'João', 'Saša Jurić', 'Íñigo'];
}
4 changes: 4 additions & 0 deletions docs/app/controllers/public-pages/docs/the-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import TheSearch4 from '../../../components/snippets/the-search-4';
import TheSearch5 from '../../../components/snippets/the-search-5';
import TheSearch6 from '../../../components/snippets/the-search-6';
import TheSearch7 from '../../../components/snippets/the-search-7';
import TheSearch8 from '../../../components/snippets/the-search-8';
import TheSearch9 from '../../../components/snippets/the-search-9';

export default class TheSearch extends Controller {
theSearch1 = TheSearch1;
Expand All @@ -15,6 +17,8 @@ export default class TheSearch extends Controller {
theSearch5 = TheSearch5;
theSearch6 = TheSearch6;
theSearch7 = TheSearch7;
theSearch8 = TheSearch8;
theSearch9 = TheSearch9;
names = [
'Stefan',
'Miguel',
Expand Down
5 changes: 5 additions & 0 deletions docs/app/templates/public-pages/docs/api-reference.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@
<td><code>string</code></td>
<td>When the options are objects and no custom matches function is provided, this option tells the component what property of the options should the default matches use to filter</td>
</tr>
<tr>
<td>searchFieldPosition</td>
<td><code>string</code></td>
<td>Allows to change the position of search field. Possible values: 'before-options' or 'trigger'. Default is single is before-options, default for multiple is trigger</td>
</tr>
<tr>
<td>searchMessage</td>
<td><code>string</code></td>
Expand Down
16 changes: 16 additions & 0 deletions docs/app/templates/public-pages/docs/the-search.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@
{{component (ensure-safe-component this.theSearch2)}}
</CodeExample>

<h2 class="t3">Search field position</h2>

<p>
The default search field position for single select is inside the dropdown and only visible when the dropdown is open (<code>@searchFieldPosition="before-options"</code>).<br />
In multiple selection you will find the search field inside trigger box (<code>@searchFieldPosition="trigger"</code>).<br />
By passing <code>@searchFieldPosition</code> you can change this logic for single and multiple selection.
</p>

<CodeExample @hbs="the-search-8.hbs" @js="the-search-8.js">
{{component (ensure-safe-component this.theSearch8)}}
</CodeExample>

<CodeExample @hbs="the-search-9.hbs" @js="the-search-9.js">
{{component (ensure-safe-component this.theSearch9)}}
</CodeExample>

<h2 class="t3">Customize the search field</h2>

<p>
Expand Down
21 changes: 21 additions & 0 deletions ember-power-select/less/base.less
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
display:table;
clear:both;
}

.ember-power-select-input {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
}
.ember-power-select-trigger:focus,
.ember-power-select-trigger--active {
Expand Down Expand Up @@ -152,6 +160,19 @@
}
}
}
.ember-power-select-search-input-field {
width: 100%;
height: 100%;
padding: 0 8px;
font-family: inherit;
font-size: inherit;
border: none;
display: block;
line-height: inherit;
-webkit-appearance: none;
outline: none;
background-color: transparent;
}

// Dropdown
.ember-power-select-dropdown {
Expand Down
1 change: 1 addition & 0 deletions ember-power-select/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
"./components/power-select-multiple/trigger.js": "./dist/_app_/components/power-select-multiple/trigger.js",
"./components/power-select.js": "./dist/_app_/components/power-select.js",
"./components/power-select/before-options.js": "./dist/_app_/components/power-select/before-options.js",
"./components/power-select/input.js": "./dist/_app_/components/power-select/input.js",
"./components/power-select/label.js": "./dist/_app_/components/power-select/label.js",
"./components/power-select/no-matches-message.js": "./dist/_app_/components/power-select/no-matches-message.js",
"./components/power-select/options.js": "./dist/_app_/components/power-select/options.js",
Expand Down
21 changes: 21 additions & 0 deletions ember-power-select/scss/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@
display:table;
clear:both;
}

.ember-power-select-input {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
}
.ember-power-select-trigger:focus,
.ember-power-select-trigger--active {
Expand Down Expand Up @@ -155,6 +163,19 @@
}
}
}
.ember-power-select-search-input-field {
width: 100%;
height: 100%;
padding: 0 8px;
font-family: inherit;
font-size: inherit;
border: none;
display: block;
line-height: inherit;
-webkit-appearance: none;
outline: none;
background-color: transparent;
}

// Dropdown
.ember-power-select-dropdown {
Expand Down
3 changes: 2 additions & 1 deletion ember-power-select/src/components/power-select-multiple.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@labelComponent={{ensure-safe-component @labelComponent}}
@afterOptionsComponent={{ensure-safe-component @afterOptionsComponent}}
@allowClear={{@allowClear}}
@beforeOptionsComponent={{if @beforeOptionsComponent (ensure-safe-component @beforeOptionsComponent) null}}
@beforeOptionsComponent={{if @beforeOptionsComponent (ensure-safe-component @beforeOptionsComponent)}}
@buildSelection={{or @buildSelection this.defaultBuildSelection}}
@calculatePosition={{@calculatePosition}}
@closeOnSelect={{@closeOnSelect}}
Expand Down Expand Up @@ -50,6 +50,7 @@
@search={{@search}}
@searchEnabled={{@searchEnabled}}
@searchField={{@searchField}}
@searchFieldPosition={{or @searchFieldPosition 'trigger'}}
@searchMessage={{@searchMessage}}
@searchMessageComponent={{@searchMessageComponent}}
@searchPlaceholder={{@searchPlaceholder}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
</li>
{{/if}}
{{/each}}
{{#if @searchEnabled}}
{{#if (and @searchEnabled (eq @searchFieldPosition 'trigger'))}}
<li class="ember-power-select-trigger-multiple-input-container">
{{#let
(component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Component from '@glimmer/component';
import { action } from '@ember/object';
import { get } from '@ember/object';
import { scheduleTask } from 'ember-lifeline';
import type { Select } from '../power-select';
import type { Select, TSearchFieldPosition } from '../power-select';
import type { ComponentLike } from '@glint/template';
import { modifier } from 'ember-modifier';
import { deprecate } from '@ember/debug';
Expand All @@ -14,6 +14,7 @@ interface PowerSelectMultipleTriggerSignature {
searchEnabled: boolean;
placeholder?: string;
searchField: string;
searchFieldPosition?: TSearchFieldPosition;
listboxId?: string;
tabindex?: string;
ariaLabel?: string;
Expand Down
2 changes: 2 additions & 0 deletions ember-power-select/src/components/power-select.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
@select={{publicAPI}}
@searchEnabled={{@searchEnabled}}
@searchField={{@searchField}}
@searchFieldPosition={{this.searchFieldPosition}}
@onFocus={{this.handleFocus}}
@onBlur={{this.handleBlur}}
@extra={{@extra}}
Expand Down Expand Up @@ -133,6 +134,7 @@
@ariaActiveDescendant={{if this.highlightedIndex (concat publicAPI.uniqueId "-" this.highlightedIndex)}}
@selectedItemComponent={{ensure-safe-component @selectedItemComponent}}
@searchPlaceholder={{@searchPlaceholder}}
@searchFieldPosition={{this.searchFieldPosition}}
@ariaLabel={{@ariaLabel}}
@ariaLabelledBy={{this.ariaLabelledBy}}
@ariaDescribedBy={{@ariaDescribedBy}}
Expand Down
29 changes: 29 additions & 0 deletions ember-power-select/src/components/power-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export interface PowerSelectArgs {
animationEnabled?: boolean;
tabindex?: number | string;
searchPlaceholder?: string;
searchFieldPosition?: TSearchFieldPosition;
verticalPosition?: string;
horizontalPosition?: string;
triggerId?: string;
Expand Down Expand Up @@ -134,6 +135,7 @@ export interface PowerSelectArgs {
}

export type TLabelClickAction = 'focus' | 'open';
export type TSearchFieldPosition = 'before-options' | 'trigger';

export interface PowerSelectSignature {
Element: HTMLElement;
Expand Down Expand Up @@ -368,6 +370,12 @@ export default class PowerSelectComponent extends Component<PowerSelectSignature
return '';
}

get searchFieldPosition(): string {
return this.args.searchFieldPosition === undefined
? 'before-options'
: this.args.searchFieldPosition;
}

// Actions
@action
handleOpen(_select: Select, e: Event): boolean | void {
Expand Down Expand Up @@ -418,6 +426,15 @@ export default class PowerSelectComponent extends Component<PowerSelectSignature
) {
return false;
}
if (
this.searchFieldPosition === 'trigger' &&
!this.storedAPI.isOpen &&
e.keyCode !== 9 && // TAB
e.keyCode !== 13 && // ENTER
e.keyCode !== 27 // ESC
) {
this.storedAPI.actions.open(e);
}
return this._routeKeydown(this.storedAPI, e);
}

Expand Down Expand Up @@ -479,6 +496,15 @@ export default class PowerSelectComponent extends Component<PowerSelectSignature
if (!this.isDestroying) {
scheduleTask(this, 'actions', this._updateIsActive, true);
}
if (this.searchFieldPosition === 'trigger') {
if (event.target) {
const target = event.target as HTMLElement;
const input = target.querySelector(
'input[type="search"]',
) as HTMLInputElement | null;
input?.focus();
}
}
if (this.args.onFocus) {
this.args.onFocus(this.storedAPI, event);
}
Expand Down Expand Up @@ -578,6 +604,9 @@ export default class PowerSelectComponent extends Component<PowerSelectSignature
this.storedAPI.actions.select(selection, e);
if (this.args.closeOnSelect !== false) {
this.storedAPI.actions.close(e);
if (this.searchFieldPosition === 'trigger') {
this.searchText = '';
}
// return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{#if @searchEnabled}}
{{#if (and @searchEnabled (eq @searchFieldPosition 'before-options'))}}
<div class="ember-power-select-search">
{{!-- template-lint-disable require-input-label --}}
<input type="search"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Component from '@glimmer/component';
import { runTask } from 'ember-lifeline';
import { action } from '@ember/object';
import { modifier } from 'ember-modifier';
import type { Select } from '../power-select';
import type { Select, TSearchFieldPosition } from '../power-select';
import { deprecate } from '@ember/debug';

interface PowerSelectBeforeOptionsSignature {
Expand All @@ -15,6 +15,7 @@ interface PowerSelectBeforeOptionsSignature {
ariaDescribedBy?: string;
role?: string;
searchPlaceholder?: string;
searchFieldPosition?: TSearchFieldPosition;
ariaActiveDescendant?: string;
listboxId?: string;
onKeydown: (e: KeyboardEvent) => false | void;
Expand Down
27 changes: 27 additions & 0 deletions ember-power-select/src/components/power-select/input.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<div class="ember-power-select-input">
{{!-- template-lint-disable require-input-label --}}
<input type="search"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck={{false}}
class="ember-power-select-search-input-field"
value={{@select.searchText}}
role={{or @role "combobox"}}
aria-activedescendant={{@ariaActiveDescendant}}
aria-controls={{@listboxId}}
aria-owns={{@listboxId}}
aria-autocomplete="list"
aria-haspopup="listbox"
aria-expanded={{if @select.isOpen "true" "false"}}
placeholder={{@searchPlaceholder}}
aria-label={{@ariaLabel}}
aria-labelledby={{@ariaLabelledBy}}
aria-describedby={{@ariaDescribedBy}}
{{on "input" this.handleInput}}
{{on "focus" @onFocus}}
{{on "blur" this.handleBlur}}
{{on "keydown" this.handleKeydown}}
{{this.setupInput}}
>
</div>
Loading

0 comments on commit 5a97bea

Please sign in to comment.