Skip to content

Commit

Permalink
Add select-search
Browse files Browse the repository at this point in the history
  • Loading branch information
vinimarcili committed Aug 30, 2023
1 parent 1dde704 commit daa7e2f
Show file tree
Hide file tree
Showing 14 changed files with 406 additions and 8 deletions.
12 changes: 12 additions & 0 deletions docs/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@
[name]="'teste-4'"
placeholder="teste"
></sq-input-file>
<sq-select-search
[id]="'search-influencer-organization-filter'"
[name]="'search-influencer-organization-filter'"
[options]="[{label: 'teste', value: 'teste'}, {label: 'teste2', value: 'teste2'}, {label: 'teste3', value: 'teste3'}, {label: 'teste4', value: 'teste4'}]"
label="teste4"
tooltipMessage="teste"
tooltipPlacement="left center"
[readonly]="false"
[(value)]="option"
[loading]="false"
></sq-select-search>
{{ option | json }}
<!-- <sq-selector
[id]="'teste-1'"
[name]="'teste-1'"
Expand Down
2 changes: 2 additions & 0 deletions docs/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component } from '@angular/core'
import { Option } from '@squidit/ngx-css'

@Component({
selector: 'app-root',
Expand All @@ -11,6 +12,7 @@ export class AppComponent {
overlay = false
date = '2023-08-30T14:24:08.292Z'
val = '0'
option?: Option

onScroll() {
console.log('scroll')
Expand Down
2 changes: 2 additions & 0 deletions docs/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"formErrors": {
"search": "Search",
"seachSelectEmpty": "There are no options to select",
"fileSize": "File too large",
"required": "Required field",
"email": "Invalid email",
Expand Down
2 changes: 2 additions & 0 deletions docs/src/assets/i18n/es.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"formErrors": {
"search": "Buscar",
"seachSelectEmpty": "No hay opciones a seleccionar",
"fileSize": "Archivo demasiado grande",
"required": "Campo obligatorio",
"email": "E-mail inválido",
Expand Down
2 changes: 2 additions & 0 deletions docs/src/assets/i18n/pt.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"formErrors": {
"search": "Buscar",
"seachSelectEmpty": "Não há opções a serem selecionadas",
"fileSize": "O arquivo enviado é muito grande, tente diminuir a qualidade ou fazer o upload novamente.",
"required": "Campo obrigatório",
"email": "E-mail inválido",
Expand Down
99 changes: 99 additions & 0 deletions src/components/sq-select-search/sq-select-search.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<div
class="wrapper-all-inside-input wrapper-select-search {{ customClass }}"
[ngClass]="{
error: (externalError && externalError !== '') || (error && error !== ''),
readonly: readonly,
loading: loading
}"
>
<label
class="display-flex"
*ngIf="label?.length || tooltipMessage"
[ngClass]="{
readonly: readonly
}"
[for]="id"
>
<div *ngIf="label" [ngStyle]="{ 'color': labelColor }" [innerHtml]="label | universalSafe"></div>
<sq-tooltip
*ngIf="tooltipMessage"
class="ml-1"
[message]="tooltipMessage"
[placement]="tooltipPlacement"
[color]="tooltipColor"
[icon]="tooltipIcon"
></sq-tooltip>
</label>
<div
[class]="'input-fake col border-' + borderColor"
style="min-height: auto"
[ngStyle]="{ 'border-color': borderColor }"
[ngClass]="{
'no-label': !label.length,
'has-icon': error || externalError,
disabled: disabled,
readonly: readonly
}"
(clickOutside)="closeDropdown()"
[clickOutsideEnabled]="open"
>
<div
class="input-fake-content"
[ngClass]="{
disabled: disabled
}"
(click)="open = !open; !open ? closeDropdown() : null"
>
<div class="loading-wrapper" *ngIf="loading">
<sq-loader></sq-loader>
</div>
<span *ngIf="!value">{{ placeholder }}</span>
<div class="input-fake-content-text" *ngIf="value">
<span class="text" *ngIf="value">
{{ value!.label }}
</span>
</div>
<i *ngIf="!loading" class="icon-down fas fa-chevron-down"></i>
</div>
<div
class="input-window scrollbar"
*ngIf="!disabled"
[ngClass]="{
open: open && !disabled
}"
>
<sq-input
[name]="name || timeStamp"
[id]="id || timeStamp"
[borderColor]="borderColor"
[value]="searchText"
[hasTimeout]="hasTimeout"
[placeholder]="placeholderSearch || (getTranslation('search') | async)"
(sharedValue)="onTipSearchChange($event)"
[errorSpan]="false"
[externalIcon]="'<i class=\'fa-solid fa-search\'></i>'"
class="input-search"
></sq-input>
<ng-container *ngIf="open && !loading">
<ul class="list" *ngFor="let opt of options | search : getSearchValue(); let i = index">
<ng-container *ngTemplateOutlet="option; context: { opt: opt, i: i }"></ng-container>
</ul>
<p class="mb-0 mt-3" *ngIf="!options?.length">
{{ getTranslation('seachSelectEmpty') | async }}
</p>
</ng-container>
</div>
</div>
<div class="box-validation box-invalid show" *ngIf="errorSpan">
<ng-container *ngIf="externalError || error">
<i class="fa-solid fa-triangle-exclamation"></i>
</ng-container>
{{ externalError ? externalError : '' }}
{{ error && !externalError ? error : '' }}
</div>
</div>
<ng-template #option let-opt="opt" let-i="i">
<li (click)="emit(opt)">
<span class="text m-0 display-inline-block">{{ opt?.label }}</span>
</li>
</ng-template>
103 changes: 103 additions & 0 deletions src/components/sq-select-search/sq-select-search.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
.wrapper-select-search {
margin: 0;
padding: 0;
text-align: left;
display: flex;
flex-direction: column;
position: relative;
&.loading {
cursor: wait;
}
.input-fake-content-text {
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
gap: 5px;
max-width: calc(100% - 40px);
.text {
margin: 0;
display: flex;
width: 100%;
height: 100%;
font-weight: 700;
align-items: center;
justify-content: flex-start;
}
}
.input-fake-content {
min-height: 44px;
border: 1px solid var(--border_color);
background: var(--background);
padding: 0.75rem 1rem;
color: var(--text_color);
border-radius: 5px;
.icon-down {
right: 5px;
top: 18px;
position: absolute;
font-size: 0.86rem;
}
}
.loading-wrapper {
position: absolute;
right: 5px;
bottom: 10px;
}
.input-search {
display: inline-block;
position: sticky;
top: 0;
z-index: 1;
width: 100%;
padding-top: 1.1rem;
::ng-deep {
.wrapper-all-inside-input {
.icon.no-label {
right: 11px;
top: 27px;
}
}
}
}
.input-window {
width: 100%;
position: absolute;
top: 100%;
max-height: 0;
height: 0;
transition: all 0.3s ease;
overflow: hidden;
z-index: 1;
&.open {
height: auto;
max-height: 400px;
background: var(--background_secondary);
padding: 0 0.7rem 1.1rem;
overflow-y: auto;
box-shadow: var(--box-shadow);
}
}
.list {
padding: 0;
list-style: none;
margin: 0;
width: 100%;
margin: 0.7rem 0 0;
li {
margin: 0;
cursor: pointer;
min-height: 30px;
flex-flow: row wrap;
display: flex;
align-items: center;
justify-content: flex-start;
transition: var(--transition);
padding: 0.35rem;
border-radius: 5px;
&:hover {
background-color: var(--border_color);
}
}
}
}
104 changes: 104 additions & 0 deletions src/components/sq-select-search/sq-select-search.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Component, ElementRef, EventEmitter, Input, Optional, Output } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { Option } from '../../interfaces/option.interface'
import { useMemo } from '../../helpers/memo.helper'

@Component({
selector: 'sq-select-search',
templateUrl: './sq-select-search.component.html',
styleUrls: ['./sq-select-search.component.scss'],
providers: [],
})
export class SqSelectSearchComponent {
@Input() name = ''
@Input() value?: Option
@Input() id = ''
@Input() label = ''
@Input() customClass = ''

@Input() placeholder = ''
@Input() externalError = ''
@Input() externalIcon = ''
@Input() placeholderSearch = ''

@Input() disabled = false
@Input() readonly = false
@Input() required = false
@Input() loading = false
@Input() useFormErrors = true
@Input() errorSpan = true
@Input() hasTimeout = false

@Input() options: Array<Option> = []

@Input() backgroundColor = ''
@Input() borderColor = ''
@Input() labelColor = ''

@Input() tooltipMessage = ''
@Input() tooltipPlacement: 'center top' | 'center bottom' | 'left center' | 'right center' = 'right center'
@Input() tooltipColor = 'inherit'
@Input() tooltipIcon = ''

@Output() valueChange: EventEmitter<Option> = new EventEmitter()
@Output() searchChange: EventEmitter<string> = new EventEmitter()
@Output() sharedValid: EventEmitter<boolean> = new EventEmitter()

error: boolean | string = false
timeoutInput!: ReturnType<typeof setTimeout>
timeOutInputTime = 800
timeStamp = `random-id-${(1 + Date.now() + Math.random()).toString().replace('.', '')}`
nativeElement: ElementRef
searchText = ''
open = false

constructor(public element: ElementRef, @Optional() private translate: TranslateService) {
this.nativeElement = element.nativeElement
}

emit(event: any) {
this.value = event
this.valueChange.emit(this.value)
this.validate()
this.closeDropdown()
}

validate() {
if (this.externalError) {
this.error = false
} else if (this.required && !this.value) {
this.setError('formErrors.required')
this.sharedValid.emit(false)
} else {
this.sharedValid.emit(true)
this.error = ''
}
}

getSearchValue() {
return this.searchText || ''
}

closeDropdown() {
this.open = false
this.searchText = ''
}

onTipSearchChange(event: string) {
this.searchText = event
this.searchChange.emit(event)
}

async setError(key: string) {
if (this.useFormErrors && this.translate) {
this.error = await this.translate.instant(key)
}
}

getTranslation = useMemo(async (key: string) => {
if (this.translate) {
return await this.translate.instant(key)
}
return ''
})
}
7 changes: 1 addition & 6 deletions src/components/sq-select/sq-select.component.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { Component, ContentChild, EventEmitter, Input, Output, TemplateRef, Optional, ElementRef } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'

export interface Option {
value: any
label: string
disabled?: boolean
}
import { Option } from '../../interfaces/option.interface'

@Component({
selector: 'sq-select',
Expand Down
Loading

0 comments on commit daa7e2f

Please sign in to comment.