From eef1e59fddbd79cb945d929b16eb68e4e65a98fe Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Sat, 21 Aug 2021 15:28:41 +0300 Subject: [PATCH] add input with action --- src/assets/icons/search.svg | 9 + src/assets/styles/_inputs.scss | 267 ++++++++++++++++++ src/assets/styles/_layout.scss | 3 + src/assets/styles/_mixins.scss | 29 ++ src/assets/styles/common.scss | 2 + src/index.ts | 2 + src/lib/common-ui.module.ts | 5 +- .../editable-input.component.html | 37 +-- .../input-with-action.component.html | 25 ++ .../input-with-action.component.scss | 14 + .../input-with-action.component.ts | 43 +++ 11 files changed, 416 insertions(+), 20 deletions(-) create mode 100644 src/assets/icons/search.svg create mode 100644 src/assets/styles/_inputs.scss create mode 100644 src/assets/styles/_layout.scss create mode 100644 src/assets/styles/_mixins.scss create mode 100644 src/lib/inputs/input-with-action/input-with-action.component.html create mode 100644 src/lib/inputs/input-with-action/input-with-action.component.scss create mode 100644 src/lib/inputs/input-with-action/input-with-action.component.ts diff --git a/src/assets/icons/search.svg b/src/assets/icons/search.svg new file mode 100644 index 0000000..8609f0d --- /dev/null +++ b/src/assets/icons/search.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/assets/styles/_inputs.scss b/src/assets/styles/_inputs.scss new file mode 100644 index 0000000..eefa5fe --- /dev/null +++ b/src/assets/styles/_inputs.scss @@ -0,0 +1,267 @@ +@import 'variables'; +@import 'mixins'; + +form .iqser-input-group:not(first-of-type) { + margin-top: 14px; +} + +.iqser-input-group { + display: flex; + flex-direction: column; + position: relative; + height: fit-content; + + .hint { + margin-top: 5px; + font-size: 11px; + line-height: 14px; + opacity: 0.7; + } + + .input-icon { + position: absolute; + right: 1px; + bottom: 1px; + background: $quick-filter-border; + height: 34px; + width: 34px; + border-left: 1px solid $quick-filter-border; + border-top-right-radius: 7px; + border-bottom-right-radius: 7px; + cursor: pointer; + transition: background-color 0.25s ease; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + background: $btn-bg; + } + + mat-icon { + width: 14px; + height: 14px; + color: $accent; + } + + &.disabled { + cursor: default; + } + } + + .mat-form-field-underline { + display: none; + } + + .mat-form-field-wrapper, + .mat-form-field-infix { + padding-bottom: 0; + } + + .mat-form-field-label { + opacity: 0.7 !important; + color: $accent !important; + transform: translateY(-1.34em) !important; + } + + &:first-child { + margin-top: 0; + } + + .icon-right { + width: 14px; + height: 14px; + position: absolute; + top: 10px; + right: 10px; + } + + .slider-row { + display: flex; + flex-direction: row; + align-items: center; + } + + .mat-button-toggle-checked { + background: $primary; + transition: background-color 0.25s ease; + color: $white; + } + + input, + textarea, + mat-select { + box-sizing: border-box; + padding-left: 11px; + padding-right: 11px; + border: 1px solid $quick-filter-border; + font-family: Inter, sans-serif; + font-size: 13px; + background-color: #ffffff; + border-radius: 8px; + outline: none; + margin-top: 3px; + min-height: 36px; + + &.with-icon { + padding-right: 34px; + } + + &:focus:not(:disabled):not(.mat-select-disabled) { + border-color: $accent; + } + + &::placeholder { + color: $accent; + opacity: 0.7; + } + + &.ng-invalid.ng-touched { + border-color: rgba($primary, 0.3); + + &:focus { + border-color: $primary; + } + } + + &:disabled, + &.mat-select-disabled { + background-color: $filter-bg; + color: rgba($accent, 0.3); + } + } + + textarea { + line-height: 18px; + } + + .hex-color-input { + width: 150px; + max-width: 150px; + } + + mat-select { + .mat-select-trigger { + height: 32px; + } + + .mat-select-value { + vertical-align: middle; + } + } + + textarea { + resize: vertical; + padding-top: 7px; + padding-bottom: 7px; + @include scroll-bar; + + &.has-scrollbar { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + } + + label:not(.mat-slide-toggle-label) { + opacity: 0.7; + font-size: 11px; + letter-spacing: 0; + line-height: 14px; + margin-bottom: 2px; + color: $accent; + + &.mat-checkbox-layout { + opacity: 1; + margin-bottom: 0; + } + } + + &.required label:after { + content: ' *'; + color: $primary; + } + + &.datepicker-wrapper { + position: relative; + display: flex; + margin-top: 0; + width: 120px; + + .mat-datepicker-input { + margin-top: 0; + width: 120px; + } + + .mat-datepicker-toggle { + position: absolute; + right: 0; + bottom: 0; + color: $accent; + + &.mat-datepicker-toggle-active { + color: $primary; + } + + .mat-icon-button { + width: 34px; + height: 34px; + line-height: 34px; + } + + mat-icon { + width: 14px; + height: 17px; + } + } + } + + &.w-75 { + width: 75px; + max-width: 75px; + } + + &.w-110 { + width: 110px; + max-width: 110px; + } + + &.w-150 { + max-width: 150px; + width: 150px; + } + + &.w-160 { + width: 160px; + max-width: 160px; + } + + &.w-200 { + width: 200px; + max-width: 200px; + } + + &.w-250 { + width: 250px; + max-width: 250px; + } + + &.w-300 { + width: 300px; + max-width: 300px; + } + + &.w-400 { + width: 400px; + max-width: 400px; + } + + &.w-450 { + width: 450px; + max-width: 450px; + } + + &.w-full { + width: 100%; + max-width: 100%; + } +} diff --git a/src/assets/styles/_layout.scss b/src/assets/styles/_layout.scss new file mode 100644 index 0000000..ef030d8 --- /dev/null +++ b/src/assets/styles/_layout.scss @@ -0,0 +1,3 @@ +.mt-0 { + margin-top: 0 !important; +} diff --git a/src/assets/styles/_mixins.scss b/src/assets/styles/_mixins.scss new file mode 100644 index 0000000..e0c5955 --- /dev/null +++ b/src/assets/styles/_mixins.scss @@ -0,0 +1,29 @@ +@import 'variables'; + +@mixin no-scroll-bar { + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE 10+ */ + &::-webkit-scrollbar { + width: 0; + background: transparent; /* Chrome/Safari/Webkit */ + } +} + +@mixin scroll-bar { + scrollbar-color: $quick-filter-border $filter-bg; + scrollbar-width: thin; + + &::-webkit-scrollbar { + width: 11px; + } + + /* Track */ + &::-webkit-scrollbar-track { + background: $filter-bg; + } + + /* Handle */ + &::-webkit-scrollbar-thumb { + background: $quick-filter-border; + } +} diff --git a/src/assets/styles/common.scss b/src/assets/styles/common.scss index 0a34c3b..c241880 100644 --- a/src/assets/styles/common.scss +++ b/src/assets/styles/common.scss @@ -1,3 +1,5 @@ +@import 'inputs'; @import 'buttons'; @import 'texts'; @import 'tables'; +@import 'layout'; diff --git a/src/index.ts b/src/index.ts index 85cb447..b972240 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,4 +33,6 @@ export * from './lib/tables/table-column-name/table-column-name.component'; export * from './lib/tables/table-header/table-header.component'; export * from './lib/misc/status-bar/status-bar.component'; export * from './lib/misc/status-bar/status-bar-config.model'; +export * from './lib/inputs/round-checkbox/round-checkbox.component'; export * from './lib/inputs/editable-input/editable-input.component'; +export * from './lib/inputs/input-with-action/input-with-action.component'; diff --git a/src/lib/common-ui.module.ts b/src/lib/common-ui.module.ts index f74b5c3..9ff2c29 100644 --- a/src/lib/common-ui.module.ts +++ b/src/lib/common-ui.module.ts @@ -21,10 +21,11 @@ import { SyncWidthDirective } from './tables/sync-width.directive'; import { StatusBarComponent } from './misc/status-bar/status-bar.component'; import { EditableInputComponent } from './inputs/editable-input/editable-input.component'; import { PopupFilterComponent } from './filtering/popup-filter/popup-filter.component'; +import { InputWithActionComponent } from './inputs/input-with-action/input-with-action.component'; const buttons = [IconButtonComponent, ChevronButtonComponent, CircleButtonComponent]; -const inputs = [RoundCheckboxComponent, EditableInputComponent]; +const inputs = [RoundCheckboxComponent, EditableInputComponent, InputWithActionComponent]; const matModules = [MatIconModule, MatButtonModule, MatTooltipModule, MatMenuModule, MatCheckboxModule]; @@ -49,7 +50,7 @@ const utils = [SortByPipe, HumanizePipe, SyncWidthDirective]; }) export class CommonUiModule { constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) { - const icons = ['arrow-down', 'check', 'close', 'edit', 'sort-asc', 'sort-desc']; + const icons = ['arrow-down', 'check', 'close', 'edit', 'sort-asc', 'sort-desc', 'search']; icons.forEach(icon => { _iconRegistry.addSvgIconInNamespace('iqser', icon, _sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/${icon}.svg`)); diff --git a/src/lib/inputs/editable-input/editable-input.component.html b/src/lib/inputs/editable-input/editable-input.component.html index 9a2e06e..5d731a5 100644 --- a/src/lib/inputs/editable-input/editable-input.component.html +++ b/src/lib/inputs/editable-input/editable-input.component.html @@ -1,23 +1,24 @@ -
- {{ value }} -
- -
-
- + +
+ {{ value }}
- - + +
- - +
+
+ +
+
+ + +
diff --git a/src/lib/inputs/input-with-action/input-with-action.component.html b/src/lib/inputs/input-with-action/input-with-action.component.html new file mode 100644 index 0000000..8591c2b --- /dev/null +++ b/src/lib/inputs/input-with-action/input-with-action.component.html @@ -0,0 +1,25 @@ +
+ + + {{ hint }} + + + + + + +
diff --git a/src/lib/inputs/input-with-action/input-with-action.component.scss b/src/lib/inputs/input-with-action/input-with-action.component.scss new file mode 100644 index 0000000..f2c22fd --- /dev/null +++ b/src/lib/inputs/input-with-action/input-with-action.component.scss @@ -0,0 +1,14 @@ +:host { + display: block; +} + +mat-icon.disabled { + opacity: 0.7; + cursor: not-allowed; +} + +iqser-circle-button { + position: absolute; + top: 4px; + right: 5px; +} diff --git a/src/lib/inputs/input-with-action/input-with-action.component.ts b/src/lib/inputs/input-with-action/input-with-action.component.ts new file mode 100644 index 0000000..8863b91 --- /dev/null +++ b/src/lib/inputs/input-with-action/input-with-action.component.ts @@ -0,0 +1,43 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'iqser-input-with-action', + templateUrl: './input-with-action.component.html', + styleUrls: ['./input-with-action.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class InputWithActionComponent { + @Input() placeholder = ''; + @Input() hint?: string; + @Input() width: number | 'full' = 250; + @Input() icon?: string; + @Input() autocomplete: 'on' | 'off' = 'on'; + @Input() value = ''; + @Output() readonly action = new EventEmitter(); + @Output() readonly valueChange = new EventEmitter(); + + get hasContent(): boolean { + return !!this.value.length; + } + + get computedWidth(): string { + return this.width === 'full' ? '100%' : `${this.width}px`; + } + + reset(): void { + this.value = ''; + this.valueChange.emit(this.value); + } + + get isSearch(): boolean { + return this.action.observers.length === 0; + } + + executeAction($event: MouseEvent): void { + $event.stopPropagation(); + + if (this.hasContent) { + this.action.emit(this.value); + } + } +}