diff --git a/package-lock.json b/package-lock.json index bc0779aa..4da0de55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@andes/plex", - "version": "9.0.0-beta.3", + "version": "9.0.0-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@andes/plex", - "version": "9.0.0-beta.3", + "version": "9.0.0-beta.4", "license": "GPL-3.0", "dependencies": { "tslib": "^2.0.0" diff --git a/package.json b/package.json index 3843fffe..3d3d9b90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@andes/plex", - "version": "9.0.0-beta.3", + "version": "9.0.0-beta.4", "repository": { "type": "git", "url": "git+https://github.com/andes/plex.git" @@ -14,7 +14,7 @@ "build:lib:watch": "ng-packagr -p ng-package.json -w", "build": "npm run build:lib && npm run postbuild:lib", "pack": "npm run build && cd dist && npm pack", - "start:demo": "node scripts/check-dist.js && ng serve plex-demo", + "start:demo": "node scripts/check-dist.js && ng serve plex-demo --port 4444", "dev:demo": "npm run build:lib && concurrently -k -r \"npm:build:lib:watch\" \"npm:start:demo\"", "build:demo": "npm run build && ng build plex-demo", "build:demo:prod": "npm run build:lib && ng build plex-demo --configuration production --base-href \"/plex/demo/\"", diff --git a/projects/plex-demo/src/app/home/home.component.ts b/projects/plex-demo/src/app/home/home.component.ts index f3f0dbef..e3bea94d 100644 --- a/projects/plex-demo/src/app/home/home.component.ts +++ b/projects/plex-demo/src/app/home/home.component.ts @@ -1,19 +1,26 @@ import { Component, ChangeDetectorRef, OnInit } from '@angular/core'; import { PlexVisualizadorService } from '@andes/plex'; import { Plex } from '@andes/plex'; +import * as moment from 'moment'; @Component({ templateUrl: 'home.html' }) export class HomeDemoComponent implements OnInit { - public field = ''; - public tooltip = 'Este es un tooltip de una sola linea pero con gran tantidad de texto'; + field = ''; + tooltip = 'Este es un tooltip de una sola linea pero con gran tantidad de texto'; hint = 'Este es un hint ubicado en el sidebar. Debería poder contener varias líneas de texto.'; - public data = []; + hintRequerido = 'Este campo es requerido. Por favor completarlo para poder guardar el formulario.'; + data = []; documento = '45979360'; fecha; - - public modelSelector; + dataSelect = [ + { id: 1, nombre: 'Opción 1' }, + { id: 2, nombre: 'Opción 2' } + ]; + modelSelector; + inverted = true; + pepe = false; constructor( public plex: Plex, @@ -50,6 +57,10 @@ export class HomeDemoComponent implements OnInit { } } + modificarFecha() { + this.fecha = moment().format('DD/MM/YYYY HH:mm'); + } + helpClick() { } diff --git a/projects/plex-demo/src/app/home/home.html b/projects/plex-demo/src/app/home/home.html index ab4f0b98..2e3d2e75 100644 --- a/projects/plex-demo/src/app/home/home.html +++ b/projects/plex-demo/src/app/home/home.html @@ -25,12 +25,31 @@ -
-
- Documento: - {{ documento | number }} -
+
Documento: + + {{ documento | number }} +
+ + + + + + + + + + + + + + + Badge info + + Badge warning + +
@@ -61,8 +80,9 @@ - + +
  • Cras justo odio
  • @@ -112,7 +132,9 @@
+ icon="{{ plex.navbarVisible ? 'close' : 'eye' }}" (click)="plex.toggleHideNavBar()" + tooltip="{{ plex.navbarVisible ? 'Ocultar barra de navegación' : 'Mostrar barra de navegación' }}" + tooltipPosition="left">
@@ -121,13 +143,6 @@
Texto principal o titulo
Texto secundario intrisecamente relacionado al titulo
- - - Validado -
Texto principal o titulo
-
Texto secundario - intrisecamente relacionado al titulo
-
@@ -228,17 +243,21 @@ + required [hint]="hintRequerido"> - + - - +
+ + +
+ ariaLabel="Ingrese un código"> +
diff --git a/projects/plex-demo/src/app/templates/componentes/plex-modal-template/plex-modal-template.html b/projects/plex-demo/src/app/templates/componentes/plex-modal-template/plex-modal-template.html index 2bc068b1..948a0728 100644 --- a/projects/plex-demo/src/app/templates/componentes/plex-modal-template/plex-modal-template.html +++ b/projects/plex-demo/src/app/templates/componentes/plex-modal-template/plex-modal-template.html @@ -140,7 +140,8 @@ contener formularios y otros componentes
- Este modal se puede cerrar con el botón "Aceptar", con la tecla ESC, con la cruz en la esquina superior derecha, o + Este modal se puede cerrar con el botón "Aceptar", con la tecla ESC, con la cruz en la + esquina superior derecha, o haciendo click fuera del mismo.
@@ -169,13 +170,16 @@ - + + Modal dentro de un plex-sidebar invert

- Un componente que abarca toda la pantalla o el foco de la misma, como un modal o un alert, nunca deben heredar estilos del contendor. + Un componente que abarca toda la pantalla o el foco de la misma, como un modal o un alert, nunca + deben heredar estilos del contendor.

diff --git a/src/lib/copy/copy.component.ts b/src/lib/copy/copy.component.ts index a5229ea3..588737f3 100644 --- a/src/lib/copy/copy.component.ts +++ b/src/lib/copy/copy.component.ts @@ -6,10 +6,9 @@ import { Component, OnInit, Input } from '@angular/core'; template: ` - ` diff --git a/src/lib/core/navbar-item-owner.component.ts b/src/lib/core/navbar-item-owner.component.ts new file mode 100644 index 00000000..2e061ec2 --- /dev/null +++ b/src/lib/core/navbar-item-owner.component.ts @@ -0,0 +1,20 @@ +import { Component, OnDestroy } from '@angular/core'; +import { Plex } from './service'; + +@Component({ + selector: 'plex-navbar-item-owner', + template: '' +}) +export class PlexNavbarItemOwnerComponent implements OnDestroy { + /** + * Token del item que este owner "posee". + * Se setea desde el servicio al crearlo. + */ + token!: number; + + constructor(private plex: Plex) { } + + ngOnDestroy() { + this.plex.clearNavbarItemIfToken(this.token); + } +} diff --git a/src/lib/core/service.ts b/src/lib/core/service.ts index 806377a6..e08c94ab 100644 --- a/src/lib/core/service.ts +++ b/src/lib/core/service.ts @@ -9,17 +9,20 @@ import { DropdownItem } from '../dropdown/dropdown-item.interface'; import { NotificationsService } from './../toast/simple-notifications/services/notifications.service'; import { PlexTitle } from './plex-title.interface'; import { WizardConfig } from './wizard-config.interface'; +import { PlexNavbarItemOwnerComponent } from './navbar-item-owner.component'; const introJs: any = _introJs; @Injectable() export class Plex { - public menu: DropdownItem[]; + public menu!: DropdownItem[]; public loaderCount = 0; public appStatus: Subject = new Subject(); public userInfo: any; public navbarVisible = true; private navbarHost?: ViewContainerRef; private navbarCmpRef?: ComponentRef; + private navbarOwnerRef?: ComponentRef; + private navbarItemToken = 0; private pending?: { component: Type; inputs?: any }; /** @@ -29,7 +32,7 @@ export class Plex { /** * Contiene el título y breadcrumb que se muestran en el navbar */ - public title: PlexTitle[]; + public title!: PlexTitle[]; constructor( private titleService: Title, @@ -378,8 +381,8 @@ export class Plex { * Navbar dinamico */ - private viewContainerRef: ViewContainerRef; - setViewContainerRef(viewContainerRef) { + private viewContainerRef!: ViewContainerRef; + setViewContainerRef(viewContainerRef: ViewContainerRef) { this.viewContainerRef = viewContainerRef; } @@ -398,26 +401,48 @@ export class Plex { * @param componentRef * @param inputs */ - setNavbarItem(component: Type, inputs?: Partial) { - + setNavbarItem(component: Type, inputs?: Partial) { if (!this.navbarHost) { this.pending = { component, inputs }; - return; // evita reintentos infinitos + return; } - this.navbarHost.clear(); + const token = ++this.navbarItemToken; + + this.clearNavbarItem(); + const cmpRef = this.navbarHost.createComponent(component); - if (inputs) { Object.assign(cmpRef.instance, inputs); } + + if (inputs) { + Object.assign(cmpRef.instance, inputs); + } + cmpRef.changeDetectorRef.detectChanges(); this.navbarCmpRef = cmpRef; + + if (this.viewContainerRef) { + const ownerRef = this.viewContainerRef.createComponent(PlexNavbarItemOwnerComponent); + ownerRef.instance.token = token; + this.navbarOwnerRef = ownerRef; + } } clearNavbarItem() { this.navbarCmpRef?.destroy(); this.navbarCmpRef = undefined; + + this.navbarOwnerRef?.destroy(); + this.navbarOwnerRef = undefined; + this.navbarHost?.clear(); } + clearNavbarItemIfToken(token: number) { + if (this.navbarItemToken === token) { + this.clearNavbarItem(); + } + } + /** * Borra el item dinamico agregado. */ diff --git a/src/lib/css/plex-bool.scss b/src/lib/css/plex-bool.scss index c5606b41..9365c23d 100644 --- a/src/lib/css/plex-bool.scss +++ b/src/lib/css/plex-bool.scss @@ -100,7 +100,7 @@ mat-slide-toggle.mat-disabled { plex-layout-sidebar[type="invert"] { mat-slide-toggle { label { - color: #fff; + color: #fff !important; } .mat-mdc-slide-toggle { diff --git a/src/lib/css/plex-box.scss b/src/lib/css/plex-box.scss index bebb6323..3e5e07b1 100644 --- a/src/lib/css/plex-box.scss +++ b/src/lib/css/plex-box.scss @@ -1,7 +1,7 @@ @use 'sass:color'; .plex-box, -plex-box > DIV { +plex-box>DIV { box-sizing: border-box; height: 100%; background: white; @@ -10,12 +10,12 @@ plex-box > DIV { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); padding: calc($grid-gutter-width-base / 3); - & > header { + &>header { margin-bottom: 15px; border-bottom: solid #aaa 1px; } - & > .plex-box-content { + &>.plex-box-content { clear: both; overflow-x: hidden; // expandir a todo el alto del contenedor flex: 1; @@ -29,6 +29,7 @@ plex-box > DIV { // Ver src/lib/layout/{main,sidebar}.component.ts &.scrollbar { padding-right: 10px; + plex-help .card { right: 30px; } @@ -40,9 +41,10 @@ plex-box > DIV { // Firefox hacks @-moz-document url-prefix() { + // https://codepen.io/sulfurious/pen/eWPBjY .plex-box, - plex-box > DIV { + plex-box>DIV { position: absolute; width: calc(100% - 30px); } @@ -50,7 +52,7 @@ plex-box > DIV { .plex-box-invert { background: $dark-blue; - color: white; + color: white !important; --heading-background-color: #{$dark-blue}; ::-webkit-scrollbar-thumb { @@ -75,6 +77,7 @@ plex-box > DIV { plex-modal { color: initial !important; + plex-list { section.item.selectable:hover { color: #292b2c; @@ -82,6 +85,12 @@ plex-box > DIV { } } + mat-slide-toggle { + label { + color: #fff !important; + } + } + plex-list { section.item { background-color: transparentize($color: $blue, $amount: 0.9); @@ -94,8 +103,8 @@ plex-box > DIV { } } - .striped > :nth-child(odd) > section.item { + .striped> :nth-child(odd)>section.item { background-color: transparentize($color: $blue, $amount: 0.85) !important; } } -} +} \ No newline at end of file diff --git a/src/lib/css/plex-select.scss b/src/lib/css/plex-select.scss index aec61f4d..cb6c8c44 100644 --- a/src/lib/css/plex-select.scss +++ b/src/lib/css/plex-select.scss @@ -61,319 +61,321 @@ $selectize-caret-margin-rtl: 0 4px 0 -2px !default; // mixins @mixin selectize-border-radius ($radii) { - -webkit-border-radius: $radii; - -moz-border-radius: $radii; - border-radius: $radii; + -webkit-border-radius: $radii; + -moz-border-radius: $radii; + border-radius: $radii; } @mixin selectize-unselectable () { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } @mixin selectize-box-shadow ($shadow) { - -webkit-box-shadow: $shadow; - box-shadow: $shadow; + -webkit-box-shadow: $shadow; + box-shadow: $shadow; } @mixin selectize-box-sizing ($type: border-box) { - -webkit-box-sizing: $type; - -moz-box-sizing: $type; - box-sizing: $type; + -webkit-box-sizing: $type; + -moz-box-sizing: $type; + box-sizing: $type; } @mixin selectize-vertical-gradient ($color-top, $color-bottom) { - background-color: mix($color-top, $color-bottom, 60%); - background-image: -moz-linear-gradient(top, $color-top, $color-bottom); // FF 3.6+ - background-image: -webkit-gradient(linear, 0 0, 0 100%, from($color-top), to($color-bottom)); // Safari 4+, Chrome 2+ - background-image: -webkit-linear-gradient(top, $color-top, $color-bottom); // Safari 5.1+, Chrome 10+ - background-image: -o-linear-gradient(top, $color-top, $color-bottom); // Opera 11.10 - background-image: linear-gradient(to bottom, $color-top, $color-bottom); // Standard, IE10 - background-repeat: repeat-x; - filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#{argb($color-top)}', endColorstr='#{argb($color-bottom)}', GradientType=0)"; // IE9 and down + background-color: mix($color-top, $color-bottom, 60%); + background-image: -moz-linear-gradient(top, $color-top, $color-bottom); // FF 3.6+ + background-image: -webkit-gradient(linear, 0 0, 0 100%, from($color-top), to($color-bottom)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(top, $color-top, $color-bottom); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(top, $color-top, $color-bottom); // Opera 11.10 + background-image: linear-gradient(to bottom, $color-top, $color-bottom); // Standard, IE10 + background-repeat: repeat-x; + filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#{argb($color-top)}', endColorstr='#{argb($color-bottom)}', GradientType=0)"; // IE9 and down } .selectize-control { - position: relative; + position: relative; } .selectize-dropdown, .selectize-input, .selectize-input input { - color: $selectize-color-text; - font-family: $selectize-font-family; - font-size: $selectize-font-size; - line-height: $selectize-line-height; - -webkit-font-smoothing: $selectize-font-smoothing; + color: $selectize-color-text; + font-family: $selectize-font-family; + font-size: $selectize-font-size; + line-height: $selectize-line-height; + -webkit-font-smoothing: $selectize-font-smoothing; } .selectize-control.single .selectize-input.input-active, .selectize-input { - background: $selectize-color-input; - cursor: text; - display: inline-block; + background: $selectize-color-input; + cursor: text; + display: inline-block; } .selectize-input { - border: $selectize-border; - padding: $selectize-padding-y $selectize-padding-x; - display: inline-block; - width: 100%; - position: relative; - z-index: 1; - @include selectize-box-sizing (border-box); - @include selectize-box-shadow ($selectize-shadow-input); - @include selectize-border-radius ($selectize-border-radius); - - .selectize-control.multi &.has-items { - $padding-x: $selectize-padding-x; - $padding-top: $selectize-padding-y - $selectize-padding-item-y - $selectize-width-item-border; - $padding-bottom: $selectize-padding-y - $selectize-padding-item-y - $selectize-margin-item-y - $selectize-width-item-border; - padding: $padding-top $padding-x $padding-bottom; - } - - &.full { - background-color: $selectize-color-input-full; - } - - &.disabled, - &.disabled * { - cursor: default !important; - } - - &.focus { - @include selectize-box-shadow ($selectize-shadow-input-focus); - } - - &.dropdown-active { - @include selectize-border-radius ($selectize-border-radius $selectize-border-radius 0 0); - } - - >* { - vertical-align: baseline; - display: -moz-inline-stack; + border: $selectize-border; + padding: $selectize-padding-y $selectize-padding-x; display: inline-block; - zoom: 1; - display: inline; - } - - .selectize-control.multi &>div { - cursor: pointer; - margin: 0 $selectize-margin-item-x $selectize-margin-item-y 0; - padding: $selectize-padding-item-y $selectize-padding-item-x; - background: $selectize-color-item; - color: $selectize-color-item-text; - border: $selectize-width-item-border solid $selectize-color-item-border; - - &.active { - background: $selectize-color-item-active; - color: $selectize-color-item-active-text; - border: $selectize-width-item-border solid $selectize-color-item-active-border; - } - } - - &.locked { - background-color: $selectize-color-disabled; - } + width: 100%; + min-width: 150px; + position: relative; + z-index: 1; + @include selectize-box-sizing (border-box); + @include selectize-box-shadow ($selectize-shadow-input); + @include selectize-border-radius ($selectize-border-radius); - .selectize-control.multi &.disabled>div { + .selectize-control.multi &.has-items { + $padding-x: $selectize-padding-x; + $padding-top: $selectize-padding-y - $selectize-padding-item-y - $selectize-width-item-border; + $padding-bottom: $selectize-padding-y - $selectize-padding-item-y - $selectize-margin-item-y - $selectize-width-item-border; + padding: $padding-top $padding-x $padding-bottom; + } - &, - &.active { - color: lighten(desaturate($selectize-color-item-text, 100%), $selectize-lighten-disabled-item-text); - background: lighten(desaturate($selectize-color-item, 100%), $selectize-lighten-disabled-item); - border: $selectize-width-item-border solid lighten(desaturate($selectize-color-item-border, 100%), $selectize-lighten-disabled-item-border); - } - } - - >input { - &::-ms-clear { - display: none; - } - - display: inline-block !important; - padding: 0 !important; - min-height: 0 !important; - max-height: none !important; - max-width: 100% !important; - margin: $selectize-caret-margin !important; - text-indent: 0 !important; - border: 0 none !important; - background: none !important; - line-height: inherit !important; - -webkit-user-select: auto !important; - @include selectize-box-shadow (none !important); + &.full { + background-color: $selectize-color-input-full; + } - &:focus { - outline: none !important; + &.disabled, + &.disabled * { + cursor: default !important; } - &::placeholder { - color: $input-color-placeholder; - opacity: 1; + &.focus { + @include selectize-box-shadow ($selectize-shadow-input-focus); + } + + &.dropdown-active { + @include selectize-border-radius ($selectize-border-radius $selectize-border-radius 0 0); + } + + >* { + vertical-align: baseline; + display: -moz-inline-stack; + display: inline-block; + zoom: 1; + display: inline; + } + + .selectize-control.multi &>div { + cursor: pointer; + margin: 0 $selectize-margin-item-x $selectize-margin-item-y 0; + padding: $selectize-padding-item-y $selectize-padding-item-x; + background: $selectize-color-item; + color: $selectize-color-item-text; + border: $selectize-width-item-border solid $selectize-color-item-border; + + &.active { + background: $selectize-color-item-active; + color: $selectize-color-item-active-text; + border: $selectize-width-item-border solid $selectize-color-item-active-border; + } + } + + &.locked { + background-color: $selectize-color-disabled; + } + + .selectize-control.multi &.disabled>div { + + &, + &.active { + color: lighten(desaturate($selectize-color-item-text, 100%), $selectize-lighten-disabled-item-text); + background: lighten(desaturate($selectize-color-item, 100%), $selectize-lighten-disabled-item); + border: $selectize-width-item-border solid lighten(desaturate($selectize-color-item-border, 100%), $selectize-lighten-disabled-item-border); + } + } + + >input { + &::-ms-clear { + display: none; + } + + display: inline-block !important; + padding: 0 !important; + min-height: 0 !important; + max-height: none !important; + max-width: 100% !important; + margin: $selectize-caret-margin !important; + text-indent: 0 !important; + border: 0 none !important; + background: none !important; + line-height: inherit !important; + -webkit-user-select: auto !important; + user-select: auto !important; + @include selectize-box-shadow (none !important); + + &:focus { + outline: none !important; + } + + &::placeholder { + color: $input-color-placeholder; + opacity: 1; + } } - } } .selectize-input::after { - content: ' '; - display: block; - clear: left; + content: ' '; + display: block; + clear: left; } .selectize-input.dropdown-active::before { - content: ' '; - display: block; - position: absolute; - background: $selectize-color-dropdown-border-top; - height: 1px; - bottom: 0; - left: 0; - right: 0; + content: ' '; + display: block; + position: absolute; + background: $selectize-color-dropdown-border-top; + height: 1px; + bottom: 0; + left: 0; + right: 0; } .selectize-dropdown { - position: absolute; - z-index: 10; - border: $selectize-dropdown-border; - background: $selectize-color-dropdown; - margin: -1px 0 0; - border-top: 0 none; - @include selectize-box-sizing (border-box); - @include selectize-box-shadow (0 1px 3px rgba(black, 0.1)); - @include selectize-border-radius (0 0 $selectize-border-radius $selectize-border-radius); - - [data-selectable] { - cursor: pointer; - overflow: hidden; - - .highlight { - background: $selectize-color-highlight; - @include selectize-border-radius (1px); - } - } - - .optgroup-header, - [data-selectable] { - padding: $selectize-padding-dropdown-item-y $selectize-padding-dropdown-item-x; - } - - .optgroup:first-child .optgroup-header { + position: absolute; + z-index: 10; + border: $selectize-dropdown-border; + background: $selectize-color-dropdown; + margin: -1px 0 0; border-top: 0 none; - } + @include selectize-box-sizing (border-box); + @include selectize-box-shadow (0 1px 3px rgba(black, 0.1)); + @include selectize-border-radius (0 0 $selectize-border-radius $selectize-border-radius); + + [data-selectable] { + cursor: pointer; + overflow: hidden; + + .highlight { + background: $selectize-color-highlight; + @include selectize-border-radius (1px); + } + } + + .optgroup-header, + [data-selectable] { + padding: $selectize-padding-dropdown-item-y $selectize-padding-dropdown-item-x; + } - .optgroup-header { - color: $selectize-color-optgroup-text; - background: $selectize-color-optgroup; - cursor: default; - } + .optgroup:first-child .optgroup-header { + border-top: 0 none; + } - .active { - background-color: $selectize-color-dropdown-item-active; - color: $selectize-color-dropdown-item-active-text; + .optgroup-header { + color: $selectize-color-optgroup-text; + background: $selectize-color-optgroup; + cursor: default; + } - &.create { - color: $selectize-color-dropdown-item-create-active-text; + .active { + background-color: $selectize-color-dropdown-item-active; + color: $selectize-color-dropdown-item-active-text; + + &.create { + color: $selectize-color-dropdown-item-create-active-text; + } } - } - .create { - color: $selectize-color-dropdown-item-create-text; - } + .create { + color: $selectize-color-dropdown-item-create-text; + } } .selectize-dropdown-content { - overflow-y: auto; - overflow-x: hidden; - max-height: $selectize-max-height-dropdown; + overflow-y: auto; + overflow-x: hidden; + max-height: $selectize-max-height-dropdown; } .selectize-control.single .selectize-input { - &, - input { - cursor: pointer; - } + &, + input { + cursor: pointer; + } - &.input-active, - &.input-active input { - cursor: text; - } + &.input-active, + &.input-active input { + cursor: text; + } - &:after { - content: ' '; - display: block; - position: absolute; - top: 50%; - right: $selectize-arrow-offset; - margin-top: round((calc(-1 * $selectize-arrow-size / 2))); - width: 0; - height: 0; - border-style: solid; - border-width: $selectize-arrow-size $selectize-arrow-size 0 $selectize-arrow-size; - border-color: $selectize-arrow-color transparent transparent transparent; - } - - &.dropdown-active:after { - margin-top: $selectize-arrow-size * -0.8; - border-width: 0 $selectize-arrow-size $selectize-arrow-size $selectize-arrow-size; - border-color: transparent transparent $selectize-arrow-color transparent; - } + &:after { + content: ' '; + display: block; + position: absolute; + top: 50%; + right: $selectize-arrow-offset; + margin-top: round((calc(-1 * $selectize-arrow-size / 2))); + width: 0; + height: 0; + border-style: solid; + border-width: $selectize-arrow-size $selectize-arrow-size 0 $selectize-arrow-size; + border-color: $selectize-arrow-color transparent transparent transparent; + } + + &.dropdown-active:after { + margin-top: $selectize-arrow-size * -0.8; + border-width: 0 $selectize-arrow-size $selectize-arrow-size $selectize-arrow-size; + border-color: transparent transparent $selectize-arrow-color transparent; + } } .selectize-control.rtl { - &.single .selectize-input:after { - left: $selectize-arrow-offset; - right: auto; - } - - .selectize-input>input { - margin: $selectize-caret-margin-rtl !important; - } + &.single .selectize-input:after { + left: $selectize-arrow-offset; + right: auto; + } + + .selectize-input>input { + margin: $selectize-caret-margin-rtl !important; + } } .selectize-control .selectize-input.disabled { - opacity: $selectize-opacity-disabled; - background-color: $selectize-color-disabled; + opacity: $selectize-opacity-disabled; + background-color: $selectize-color-disabled; } // PLUGINS/REMOVE_BUTTON.SCSS .selectize-control { - .remove-button { - opacity: .5; - font-size: .9em; - color: $dark-grey; - text-decoration: none; - margin-left: .5em; - - &:hover { - color: black; - } - } - - &.single { .remove-button { - position: absolute; - right: 32px; - top: 8px; + opacity: .5; + font-size: .9em; + color: $dark-grey; + text-decoration: none; + margin-left: .5em; + + &:hover { + color: black; + } } - &:hover { - .remove-button { - opacity: 1; - } + &.single { + .remove-button { + position: absolute; + right: 32px; + top: 8px; + } + + &:hover { + .remove-button { + opacity: 1; + } + } } - } - &.multi { - .item:hover { - .remove-button { - opacity: 1; - } + &.multi { + .item:hover { + .remove-button { + opacity: 1; + } + } } - } } // SELECTIZE.BOOTSTRAP3.SCSS @@ -442,157 +444,161 @@ $selectize-arrow-offset: $selectize-padding-x + 5px !default; .selectize-dropdown, .selectize-dropdown.form-control { - height: auto; - padding: 0; - margin: 2px 0 0; - z-index: $zindex-dropdown; - background: $selectize-color-dropdown; - // // border: 1px solid $dropdown-fallback-border; - // // border: 1px solid $dropdown-border; - // @include selectize-border-radius ($border-radius-base); - @include selectize-box-shadow (0 6px 12px rgba(black, .175)); + height: auto; + padding: 0; + margin: 2px 0 0; + z-index: $zindex-dropdown; + background: $selectize-color-dropdown; + // // border: 1px solid $dropdown-fallback-border; + // // border: 1px solid $dropdown-border; + // @include selectize-border-radius ($border-radius-base); + @include selectize-box-shadow (0 6px 12px rgba(black, .175)); } .selectize-dropdown { - .optgroup-header { - // font-size: $font-size-small; - line-height: $line-height-base; - } + .optgroup-header { + // font-size: $font-size-small; + line-height: $line-height-base; + } - .optgroup:first-child:before { - display: none; - } + .optgroup:first-child:before { + display: none; + } - .optgroup:before { - content: ' '; - display: block; - @include nav-divider; - margin-left: $selectize-padding-dropdown-item-x * -1; - margin-right: $selectize-padding-dropdown-item-x * -1; - } + .optgroup:before { + content: ' '; + display: block; + @include nav-divider; + margin-left: $selectize-padding-dropdown-item-x * -1; + margin-right: $selectize-padding-dropdown-item-x * -1; + } } .selectize-dropdown-content { - padding: 5px 0; + padding: 5px 0; + + .option { + min-width: 132px; + } } .selectize-dropdown-header { - padding: $selectize-padding-dropdown-item-y * 2 $selectize-padding-dropdown-item-x; + padding: $selectize-padding-dropdown-item-y * 2 $selectize-padding-dropdown-item-x; } .selectize-input { - min-height: $input-height; + min-height: $input-height; - &.dropdown-active { - @include selectize-border-radius ($selectize-border-radius); - } - - &.dropdown-active::before { - display: none; - } - - &.focus { - $color: $input-border-focus; - $color-rgba: rgba(red($color), green($color), blue($color), .6); - border-color: $color; - outline: 0; - @include selectize-box-shadow ("inset 0 1px 1px rgba(black, .075), 0 0 8px #{$color-rgba}"); - } + &.dropdown-active { + @include selectize-border-radius ($selectize-border-radius); + } + + &.dropdown-active::before { + display: none; + } + + &.focus { + $color: $input-border-focus; + $color-rgba: rgba(red($color), green($color), blue($color), .6); + border-color: $color; + outline: 0; + @include selectize-box-shadow ("inset 0 1px 1px rgba(black, .075), 0 0 8px #{$color-rgba}"); + } } .has-danger .selectize-input { - border-color: $selectize-color-input-error; - @include selectize-box-shadow ($selectize-shadow-input-error); + border-color: $selectize-color-input-error; + @include selectize-box-shadow ($selectize-shadow-input-error); - &:focus { - border-color: $selectize-color-input-error-focus; - @include selectize-box-shadow ($selectize-shadow-input-error-focus); - } + &:focus { + border-color: $selectize-color-input-error-focus; + @include selectize-box-shadow ($selectize-shadow-input-error-focus); + } } .selectize-control { - &.multi { - .selectize-input.has-items { - padding-left: $selectize-padding-x - $selectize-padding-item-x; - padding-right: $selectize-padding-x - $selectize-padding-item-x; + &.multi { + .selectize-input.has-items { + padding-left: $selectize-padding-x - $selectize-padding-item-x; + padding-right: $selectize-padding-x - $selectize-padding-item-x; + } + + .selectize-input>div { + @include selectize-border-radius ($selectize-border-radius - 1px); + } } - - .selectize-input>div { - @include selectize-border-radius ($selectize-border-radius - 1px); - } - } } .form-control.selectize-control { - padding: 0; - height: auto; - border: none; - background: none; - line-height: 0; - @include selectize-box-shadow (none); - @include selectize-border-radius (0); + padding: 0; + height: auto; + border: none; + background: none; + line-height: 0; + @include selectize-box-shadow (none); + @include selectize-border-radius (0); } // Loading // @jgabriel .selectize-control.loading:before { - color: $dark-grey; - position: absolute; - right: 29px; - top: 3px; - z-index: 10; - - font-family: "Material Design Icons"; - content: "\F0772"; - font-size: 1.4em; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-transform: translate(0, 0); - transform: translate(0, 0); - -webkit-animation: fa-spin 2s infinite linear; - animation: selectize-spin 2s infinite linear; + color: $dark-grey; + position: absolute; + right: 29px; + top: 3px; + z-index: 10; + + font-family: "Material Design Icons"; + content: "\F0772"; + font-size: 1.4em; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-transform: translate(0, 0); + transform: translate(0, 0); + -webkit-animation: fa-spin 2s infinite linear; + animation: selectize-spin 2s infinite linear; } @-webkit-keyframes selectize-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } } @keyframes selectize-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } } plex-select { - .search-icon-container { - position: relative; - - .search-icon { - position: absolute; - right: 0.5rem; - bottom: 0.8rem; - color: $dark-grey; - z-index: 100; - font-size: 0.9em; - - &.disabled { - color: #ccc; - } + .search-icon-container { + position: relative; + + .search-icon { + position: absolute; + right: 0.5rem; + bottom: 0.8rem; + color: $dark-grey; + z-index: 100; + font-size: 0.9em; + + &.disabled { + color: #ccc; + } + } } - } } \ No newline at end of file diff --git a/src/lib/css/plex-text.scss b/src/lib/css/plex-text.scss index 464660f3..50f9efa0 100644 --- a/src/lib/css/plex-text.scss +++ b/src/lib/css/plex-text.scss @@ -67,6 +67,7 @@ plex-text { } .st-toolbar-item { + &:focus, &:active, &:focus-visible { @@ -81,16 +82,11 @@ plex-text { } } -plex-layout-sidebar[type="invert"] { - .text-editor { - color: black; - } -} - // Fix para cambiar texto del botón "confirmar" de ingreso de texto con link .st-input-button { font-size: 0; } + .st-input-button::after { content: 'Confirmar'; font-size: 14px; diff --git a/src/lib/css/tooltip.scss b/src/lib/css/tooltip.scss index ac3c96a9..9248e3ce 100644 --- a/src/lib/css/tooltip.scss +++ b/src/lib/css/tooltip.scss @@ -25,6 +25,7 @@ $tooltip-arrow-color: #505050; } } +tooltip-content .tooltip, mat-tooltip-component .mdc-tooltip { - font-size: 12px !important; + font-size: 12px; } \ No newline at end of file diff --git a/src/lib/datetime/datetime.component.ts b/src/lib/datetime/datetime.component.ts index 122a2acf..7030f5be 100644 --- a/src/lib/datetime/datetime.component.ts +++ b/src/lib/datetime/datetime.component.ts @@ -108,7 +108,7 @@ export type Moment = _moment.Moment; [(ngModel)]="dateTimeString" [min]="minTimeString" [max]="maxTimeString" [placeholder]="placeholder" [disabled]="disabled" [readonly]="readonly"> - @@ -292,7 +292,7 @@ export class PlexDateTimeComponent implements AfterViewInit, OnChanges, OnDestro writeValue(value: any) { this._value = value ? moment(value).toDate() : null; this.timeString = this._value ? moment(this._value).format('HH:mm') : ''; - this.dateTimeString = this._value ? moment(this._value).format('DD/MM/YYYY HH:mm') : ''; + this.dateTimeString = this._value ? moment(this._value).format(this._format) : ''; } customText() { return `${this.hintPrefix} ${this.hintSuffix}`; } @@ -380,7 +380,7 @@ export class PlexDateTimeComponent implements AfterViewInit, OnChanges, OnDestro } const m = moment(this.dateTimeString, this._format); this._value = m.isValid() ? m.toDate() : null; - this.dateTimeString = this._value ? moment(this._value).format('DD/MM/YYYY HH:mm') : ''; + this.dateTimeString = this._value ? moment(this._value).format(this._format) : ''; this.emitChange(true); } @@ -389,14 +389,14 @@ export class PlexDateTimeComponent implements AfterViewInit, OnChanges, OnDestro const input = ev?.targetElement?.value || ev; if (pickerType === 'date') { - this.dateTimeString = this._value ? moment(this._value).format('DD/MM/YYYY HH:mm') : ''; + this.dateTimeString = this._value ? moment(this._value).format(this._format) : ''; this.timePicker?.open(); } else { // pickerType === 'time' const [hh, mm] = input.split(':').map(Number); const m = moment(this._value).set({ hour: hh, minute: mm, second: 0, millisecond: 0 }); const mFormated = moment(m, this._format); this._value = mFormated.isValid() ? mFormated.toDate() : null; - this.dateTimeString = this._value ? moment(this._value).format('DD/MM/YYYY HH:mm') : ''; + this.dateTimeString = this._value ? moment(this._value).format(this._format) : ''; } this.emitChange(true); } diff --git a/src/lib/hint/hint.component.ts b/src/lib/hint/hint.component.ts index fca2cc50..ea09f120 100644 --- a/src/lib/hint/hint.component.ts +++ b/src/lib/hint/hint.component.ts @@ -1,5 +1,5 @@ import { MatTooltip, TooltipPosition } from '@angular/material/tooltip'; -import { Component, OnInit, Input, HostListener, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; +import { Component, OnInit, Input, HostListener, ViewChild, AfterViewInit } from '@angular/core'; import { PlexType } from '../core/plex-type.type'; @Component({ @@ -31,6 +31,12 @@ export class HintComponent implements OnInit, AfterViewInit { @Input() detach: '' | 'both' | 'right' | 'top'; + @Input() + hintOffsetX = 0; + + @Input() + hintOffsetY = 0; + @ViewChild('matTooltip', { static: false }) matTooltip: MatTooltip; constructor() { } @@ -45,23 +51,42 @@ export class HintComponent implements OnInit, AfterViewInit { adjustIfLabel() { setTimeout(() => { - const labelElement = this.hostElement.querySelector('label'); + if (!this.hostElement) { + return; + } - if (labelElement !== null) { - labelElement.style.display = 'inline'; + const hintElement = this.hostElement.nextElementSibling as HTMLElement | null; + const container = this.hostElement.parentElement as HTMLElement | null; + const labelElement = this.hostElement.querySelector('label') as HTMLElement | null; - const label = labelElement.getBoundingClientRect(); + if (!hintElement || !container) { + return; + } - const hintElement = this.hostElement.nextElementSibling as HTMLElement; - const hint = hintElement.getBoundingClientRect(); + if (getComputedStyle(container).position === 'static') { + container.style.position = 'relative'; + } - hintElement.style.position = 'relative'; - hintElement.style.top = -label.height - 2 * hint.height + 'px'; + const hostRect = this.hostElement.getBoundingClientRect(); + const containerRect = container.getBoundingClientRect(); + + let top = 0; + let left = hostRect.left - containerRect.left; - const adjustX = this.hostElement.getAttribute('required') === 'true' ? 10 : 0; + if (labelElement) { + labelElement.style.display = 'inline'; + hintElement.style.position = 'absolute'; - hintElement.style.left = label.width + hint.width + adjustX + 'px'; + const labelRect = labelElement.getBoundingClientRect(); + top += hostRect.top - containerRect.top - 5; + left = labelRect.right - containerRect.left + 15; + } else { + hintElement.style.position = 'relative'; + left = hostRect.right - hostRect.left; } + + hintElement.style.top = (top + this.hintOffsetY) + 'px'; + hintElement.style.left = (left + this.hintOffsetX) + 'px'; }, 100); } diff --git a/src/lib/hint/hint.directive.ts b/src/lib/hint/hint.directive.ts index 85e9cd9a..67ff181d 100644 --- a/src/lib/hint/hint.directive.ts +++ b/src/lib/hint/hint.directive.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, Input, OnInit, ViewContainerRef } from '@angular/core'; +import { ChangeDetectorRef, ComponentRef, Directive, inject, Input, OnInit, ViewContainerRef, EnvironmentInjector } from '@angular/core'; import { PlexType } from '../core/plex-type.type'; import { HintComponent } from './hint.component'; @@ -9,6 +9,8 @@ import { HintComponent } from './hint.component'; export class HintDirective implements OnInit { private tooltip: ComponentRef; + private envInjector = inject(EnvironmentInjector); + @Input('hint') content: string | HintComponent; // default = Background gris @@ -28,7 +30,6 @@ export class HintDirective implements OnInit { constructor( private viewContainerRef: ViewContainerRef, - private resolver: ComponentFactoryResolver, private cdr: ChangeDetectorRef ) { } @@ -36,14 +37,11 @@ export class HintDirective implements OnInit { ngOnInit(): void { this.cdr.detectChanges(); - const factory = this.resolver.resolveComponentFactory(HintComponent); - this.tooltip = this.viewContainerRef.createComponent(factory); + this.tooltip = this.viewContainerRef.createComponent(HintComponent, { environmentInjector: this.envInjector }); this.tooltip.instance.hostElement = this.viewContainerRef.element.nativeElement; this.tooltip.instance.content = this.content as string; this.tooltip.instance.position = this.position as any; this.tooltip.instance.hintType = this.hintType; this.tooltip.instance.hintIcon = this.hintIcon; - } - } diff --git a/src/lib/text/text.component.ts b/src/lib/text/text.component.ts index b30e2178..bbeb6df2 100644 --- a/src/lib/text/text.component.ts +++ b/src/lib/text/text.component.ts @@ -125,7 +125,7 @@ export class PlexTextComponent implements OnInit, AfterViewInit, ControlValueAcc this.focusout.emit(); } - public onChange = (_: Event) => { }; + public onChange = (eventOrValue: Event | string | null) => { }; public disabledEvent(event: Event) { event.stopImmediatePropagation(); @@ -135,12 +135,15 @@ export class PlexTextComponent implements OnInit, AfterViewInit, ControlValueAcc public validateFn = (c: UntypedFormControl) => { }; public validate(c: UntypedFormControl) { - if (c.value === null || c.value === undefined || c.value.toString().trim() === '') { + const value = this.html + ? this.normalizeValue(c.value) + : c.value; + + if (value === null || value === undefined || value.toString().trim() === '') { return null; } - const isValid = this.customValidation(c.value); - + const isValid = this.customValidation(value); return isValid ? null : { customValidation: true }; } @@ -157,12 +160,6 @@ export class PlexTextComponent implements OnInit, AfterViewInit, ControlValueAcc this.password = false; } - registerOnTouched(fn: any): void { - } - - setDisabledState?(isDisabled: boolean): void { - } - ngOnInit() { if (this.type === 'password') { this.mensaje = 'Ingrese caracteres alfanuméricos, sin espacios.'; @@ -208,49 +205,105 @@ export class PlexTextComponent implements OnInit, AfterViewInit, ControlValueAcc } } - writeValue(value: any) { - const element = this.multiline ? this.textarea.nativeElement : this.input.nativeElement; - this.renderer.setProperty(element, 'value', typeof value === 'undefined' ? '' : value); - if (this.multiline) { - this.adjustTextArea(); + registerOnTouched(fn: any): void { + } + + setDisabledState?(isDisabled: boolean): void { + } + + + private normalizeValue(value: string | null | undefined): string { + if (!value) { + return ''; } + if (!this.html) { + return value; + } + + const normalized = value + .replace(/ /gi, ' ') + .replace(//gi, '') + .replace(/<\/?(div|p|span)[^>]*>/gi, '') + .replace(/\s+/g, ' ') + .trim(); + + return normalized === '' ? '' : value; + } + + private isHtmlVisuallyEmpty(value: string | null | undefined): boolean { + if (!value) { + return true; + } + + const text = value + .replace(/ /gi, ' ') + .replace(//gi, '') + .replace(/<\/?(div|p|span)[^>]*>/gi, '') + .replace(/<[^>]+>/g, '') + .replace(/\s+/g, ' ') + .trim(); + + return text === ''; + } + + writeValue(value: any) { if (this.html) { - this.richText = value || ''; + this.richText = this.normalizeValue(value || ''); + } else { + const element = this.multiline ? this.textarea.nativeElement : this.input.nativeElement; + this.renderer.setProperty(element, 'value', typeof value === 'undefined' ? '' : value); + + if (this.multiline) { + this.adjustTextArea(); + } } - this.isEmpty = !(value && value.toString().trim()); + this.isEmpty = this.html + ? this.isHtmlVisuallyEmpty(this.richText) + : !(value && value.toString().trim()); } hasDanger() { return (this.control as any).name && (this.control.dirty || this.control.touched) && !this.control.valid; } - registerOnChange(fn) { - this.onChange = (event: Event) => { - const value = (event.target as HTMLInputElement).value || ''; + registerOnChange(fn: any) { + this.onChange = (eventOrValue: Event | string | null) => { + let value = ''; + + if (this.html) { + value = typeof eventOrValue === 'string' ? eventOrValue : ''; + value = this.normalizeValue(value); + } else { + value = (eventOrValue as Event)?.target + ? ((eventOrValue as Event).target as HTMLInputElement).value || '' + : ''; + } - if (this.customValidation) { - if (this.control && this.control.control) { - this.control.control.setValidators(this.validate.bind(this)); - this.control.control.updateValueAndValidity({ emitEvent: false }); - } + if (this.customValidation && this.control?.control) { + this.control.control.setValidators(this.validate.bind(this)); + this.control.control.updateValueAndValidity({ emitEvent: false }); } + fn(value); - this.isEmpty = !(value && value.toString().trim()); + this.isEmpty = this.html + ? this.isHtmlVisuallyEmpty(value) + : !(value && value.toString().trim()); if (this.multiline) { this.adjustTextArea(); } + if (this.changeTimeout) { clearTimeout(this.changeTimeout); } + this.changeTimeout = setTimeout(() => { - this.change.emit({ - value - }); + this.change.emit({ value }); }, this.debounce); + this.typing.emit(); }; } @@ -258,8 +311,11 @@ export class PlexTextComponent implements OnInit, AfterViewInit, ControlValueAcc clearInput() { if (!this.disabled && !this.isEmpty) { this.writeValue(null); - this.onChange(null); - this.input.nativeElement.focus(); + this.onChange(this.html ? '' : null); + + if (!this.html) { + this.input.nativeElement.focus(); + } } } diff --git a/src/lib/tooltip/tooltip-content.component.ts b/src/lib/tooltip/tooltip-content.component.ts index fd5613ff..7307b115 100644 --- a/src/lib/tooltip/tooltip-content.component.ts +++ b/src/lib/tooltip/tooltip-content.component.ts @@ -1,19 +1,23 @@ -import { Component, Input, AfterViewInit, ElementRef, ChangeDetectorRef } from '@angular/core'; +import { Component, Input, AfterViewInit, ElementRef, ChangeDetectorRef, OnDestroy } from '@angular/core'; @Component({ // tslint:disable-next-line:component-selector selector: 'tooltip-content', - template: `