mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-07 22:07:40 +00:00
fix(console): angular 13, grpc web 1.3.0, dependencies (#2688)
* cli core, migrations * material * input directive * fresh lock * update oidc lib * use angular-eslint next for ng 13 * clean lock * change qrcode lib, env * eslint * deps * grpc libs update, grpc web 1.0.3, rev env * remove grpc-web from build/zitadel/Dockerfile Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
parent
77a3f848e5
commit
33ae935bf6
@ -18,7 +18,7 @@ ENV PROTOC_ARCH x86_64
|
|||||||
FROM ${BUILDARCH}-base AS base
|
FROM ${BUILDARCH}-base AS base
|
||||||
ARG PROTOC_VERSION=3.13.0
|
ARG PROTOC_VERSION=3.13.0
|
||||||
ARG PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip
|
ARG PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip
|
||||||
ARG GRPC_WEB_VERSION=1.2.1
|
ARG GRPC_WEB_VERSION=1.3.0
|
||||||
# no arm specific version available and x86 works fine at the moment:
|
# no arm specific version available and x86 works fine at the moment:
|
||||||
ARG GRPC_WEB=protoc-gen-grpc-web-${GRPC_WEB_VERSION}-linux-x86_64
|
ARG GRPC_WEB=protoc-gen-grpc-web-${GRPC_WEB_VERSION}-linux-x86_64
|
||||||
|
|
||||||
|
@ -12,17 +12,13 @@ ENV PROTOC_ARCH x86_64
|
|||||||
#######################
|
#######################
|
||||||
## This step sets up the folder structure,
|
## This step sets up the folder structure,
|
||||||
## initalices go mods,
|
## initalices go mods,
|
||||||
## downloads the protofiles,
|
## downloads the protofiles and protoc for later use
|
||||||
## protoc and protoc-gen-grpc-web for later use
|
|
||||||
#######################
|
#######################
|
||||||
FROM ${BUILDARCH}-base AS base
|
FROM ${BUILDARCH}-base AS base
|
||||||
ARG PROTOC_VERSION=3.18.0
|
ARG PROTOC_VERSION=3.18.0
|
||||||
ARG PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip
|
ARG PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip
|
||||||
ARG GATEWAY_VERSION=2.6.0
|
ARG GATEWAY_VERSION=2.6.0
|
||||||
ARG VALIDATOR_VERSION=0.6.2
|
ARG VALIDATOR_VERSION=0.6.2
|
||||||
ARG GRPC_WEB_VERSION=1.2.1
|
|
||||||
# no arm specific version available and x86 works fine at the moment:
|
|
||||||
ARG GRPC_WEB=protoc-gen-grpc-web-${GRPC_WEB_VERSION}-linux-x86_64
|
|
||||||
|
|
||||||
RUN apk add tar curl
|
RUN apk add tar curl
|
||||||
WORKDIR /proto
|
WORKDIR /proto
|
||||||
@ -33,9 +29,6 @@ RUN apk add tar curl \
|
|||||||
&& unzip -o $PROTOC_ZIP -d /usr/local bin/protoc \
|
&& unzip -o $PROTOC_ZIP -d /usr/local bin/protoc \
|
||||||
&& unzip -o $PROTOC_ZIP -d /proto include/* \
|
&& unzip -o $PROTOC_ZIP -d /proto include/* \
|
||||||
&& rm -f $PROTOC_ZIP \
|
&& rm -f $PROTOC_ZIP \
|
||||||
&& curl -OL https://github.com/grpc/grpc-web/releases/download/${GRPC_WEB_VERSION}/${GRPC_WEB} \
|
|
||||||
&& mv ${GRPC_WEB} /usr/local/bin/protoc-gen-grpc-web \
|
|
||||||
&& chmod +x /usr/local/bin/protoc-gen-grpc-web \
|
|
||||||
&& curl https://raw.githubusercontent.com/envoyproxy/protoc-gen-validate/v${VALIDATOR_VERSION}/validate/validate.proto --create-dirs -o include/validate/validate.proto \
|
&& curl https://raw.githubusercontent.com/envoyproxy/protoc-gen-validate/v${VALIDATOR_VERSION}/validate/validate.proto --create-dirs -o include/validate/validate.proto \
|
||||||
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v${GATEWAY_VERSION}/protoc-gen-openapiv2/options/annotations.proto --create-dirs -o include/protoc-gen-openapiv2/options/annotations.proto \
|
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v${GATEWAY_VERSION}/protoc-gen-openapiv2/options/annotations.proto --create-dirs -o include/protoc-gen-openapiv2/options/annotations.proto \
|
||||||
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v${GATEWAY_VERSION}/protoc-gen-openapiv2/options/openapiv2.proto --create-dirs -o include/protoc-gen-openapiv2/options/openapiv2.proto \
|
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v${GATEWAY_VERSION}/protoc-gen-openapiv2/options/openapiv2.proto --create-dirs -o include/protoc-gen-openapiv2/options/openapiv2.proto \
|
||||||
|
1
console/.gitignore
vendored
1
console/.gitignore
vendored
@ -30,6 +30,7 @@ speed-measure-plugin*.json
|
|||||||
.history/*
|
.history/*
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
|
/.angular/cache
|
||||||
/.sass-cache
|
/.sass-cache
|
||||||
/connect.lock
|
/connect.lock
|
||||||
/coverage
|
/coverage
|
||||||
|
@ -22,22 +22,15 @@
|
|||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"assets": [
|
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
|
||||||
"src/favicon.ico",
|
"styles": ["src/styles.scss"],
|
||||||
"src/assets",
|
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"],
|
||||||
"src/manifest.webmanifest"
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"src/styles.scss"
|
|
||||||
],
|
|
||||||
"scripts": [
|
|
||||||
"./node_modules/tinycolor2/dist/tinycolor-min.js"
|
|
||||||
],
|
|
||||||
"allowedCommonJsDependencies": [
|
"allowedCommonJsDependencies": [
|
||||||
"@angular/common/locales/de",
|
"@angular/common/locales/de",
|
||||||
"src/app/proto/generated/zitadel/admin_pb",
|
"src/app/proto/generated/zitadel/admin_pb",
|
||||||
"src/app/proto/generated/zitadel/management_pb",
|
"src/app/proto/generated/zitadel/management_pb",
|
||||||
"src/app/proto/generated/**",
|
"src/app/proto/generated/**",
|
||||||
|
"google-protobuf/google/protobuf/empty_pb",
|
||||||
"file-saver",
|
"file-saver",
|
||||||
"qrcode"
|
"qrcode"
|
||||||
],
|
],
|
||||||
@ -126,27 +119,15 @@
|
|||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"tsConfig": "tsconfig.spec.json",
|
"tsConfig": "tsconfig.spec.json",
|
||||||
"karmaConfig": "karma.conf.js",
|
"karmaConfig": "karma.conf.js",
|
||||||
"assets": [
|
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
|
||||||
"src/favicon.ico",
|
"styles": ["./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css", "src/styles.scss"],
|
||||||
"src/assets",
|
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"]
|
||||||
"src/manifest.webmanifest"
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
|
|
||||||
"src/styles.scss"
|
|
||||||
],
|
|
||||||
"scripts": [
|
|
||||||
"./node_modules/tinycolor2/dist/tinycolor-min.js"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"builder": "@angular-eslint/builder:lint",
|
"builder": "@angular-eslint/builder:lint",
|
||||||
"options": {
|
"options": {
|
||||||
"lintFilePatterns": [
|
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
||||||
"src/**/*.ts",
|
|
||||||
"src/**/*.html"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"e2e": {
|
"e2e": {
|
||||||
|
17957
console/package-lock.json
generated
17957
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,60 +10,59 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "~12.2.12",
|
"@angular/animations": "~13.0.2",
|
||||||
"@angular/cdk": "~12.2.12",
|
"@angular/cdk": "~13.0.2",
|
||||||
"@angular/common": "~12.2.12",
|
"@angular/common": "~13.0.2",
|
||||||
"@angular/compiler": "~12.2.12",
|
"@angular/compiler": "~13.0.2",
|
||||||
"@angular/core": "~12.2.12",
|
"@angular/core": "~13.0.2",
|
||||||
"@angular/forms": "~12.2.12",
|
"@angular/forms": "~13.0.2",
|
||||||
"@angular/material": "^12.2.12",
|
"@angular/material": "^13.0.2",
|
||||||
"@angular/material-moment-adapter": "^12.2.12",
|
"@angular/material-moment-adapter": "^13.0.2",
|
||||||
"@angular/platform-browser": "~12.2.12",
|
"@angular/platform-browser": "~13.0.2",
|
||||||
"@angular/platform-browser-dynamic": "~12.2.12",
|
"@angular/platform-browser-dynamic": "~13.0.2",
|
||||||
"@angular/router": "~12.2.12",
|
"@angular/router": "~13.0.2",
|
||||||
"@angular/service-worker": "~12.2.12",
|
"@angular/service-worker": "~13.0.2",
|
||||||
"@grpc/grpc-js": "^1.3.2",
|
"@grpc/grpc-js": "^1.3.2",
|
||||||
"@ngx-translate/core": "^13.0.0",
|
"@ngx-translate/core": "^13.0.0",
|
||||||
"@ngx-translate/http-loader": "^6.0.0",
|
"@ngx-translate/http-loader": "^6.0.0",
|
||||||
"@types/file-saver": "^2.0.2",
|
"@types/file-saver": "^2.0.2",
|
||||||
"@types/google-protobuf": "^3.15.3",
|
"@types/google-protobuf": "^3.15.3",
|
||||||
"@types/uuid": "^8.3.0",
|
"@types/uuid": "^8.3.0",
|
||||||
"angular-oauth2-oidc": "^12.1.0",
|
"angular-oauth2-oidc": "^13.0.1",
|
||||||
"angularx-qrcode": "^11.0.0",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"google-proto-files": "^2.4.0",
|
"google-proto-files": "^2.5.0",
|
||||||
"google-protobuf": "^3.19.1",
|
"google-protobuf": "^3.19.1",
|
||||||
"grpc-web": "^1.2.1",
|
"grpc-web": "^1.3.0",
|
||||||
"libphonenumber-js": "^1.9.34",
|
"libphonenumber-js": "^1.9.34",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
|
"ng-qrcode": "^6.0.0",
|
||||||
"ngx-color": "^7.2.0",
|
"ngx-color": "^7.2.0",
|
||||||
"ngx-image-cropper": "^3.3.5",
|
"ngx-image-cropper": "^3.3.5",
|
||||||
"ngx-quicklink": "^0.2.6",
|
"ngx-quicklink": "^0.2.6",
|
||||||
"rxjs": "~7.3.0",
|
"rxjs": "~7.4.0",
|
||||||
"tinycolor2": "^1.4.2",
|
"tinycolor2": "^1.4.2",
|
||||||
"ts-protoc-gen": "^0.14.0",
|
|
||||||
"tslib": "^2.2.0",
|
"tslib": "^2.2.0",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-eslint/builder": "12.6.1",
|
"@angular-devkit/build-angular": "~13.0.2",
|
||||||
"@angular-eslint/eslint-plugin": "12.5.0",
|
"@angular-eslint/builder": "^13.0.1",
|
||||||
"@angular-eslint/eslint-plugin-template": "12.5.0",
|
"@angular-eslint/eslint-plugin": "^13.0.1",
|
||||||
"@angular-eslint/schematics": "12.6.1",
|
"@angular-eslint/eslint-plugin-template": "^13.0.1",
|
||||||
"@angular-eslint/template-parser": "12.5.0",
|
"@angular-eslint/schematics": "^13.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "4.28.2",
|
"@angular-eslint/template-parser": "^13.0.1",
|
||||||
"@typescript-eslint/parser": "4.28.2",
|
"@angular/cli": "~13.0.2",
|
||||||
"eslint": "^7.26.0",
|
"@angular/compiler-cli": "~13.0.2",
|
||||||
"@angular-devkit/build-angular": "~12.2.12",
|
"@angular/language-service": "~13.0.2",
|
||||||
"@angular/cli": "~12.2.12",
|
|
||||||
"@angular/compiler-cli": "~12.2.12",
|
|
||||||
"@angular/language-service": "~12.2.12",
|
|
||||||
"@types/jasmine": "~3.9.1",
|
"@types/jasmine": "~3.9.1",
|
||||||
"@types/jasminewd2": "~2.0.10",
|
"@types/jasminewd2": "~2.0.10",
|
||||||
"@types/node": "^16.10.2",
|
"@types/node": "^16.10.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "5.3.0",
|
||||||
|
"@typescript-eslint/parser": "5.3.0",
|
||||||
"codelyzer": "^6.0.0",
|
"codelyzer": "^6.0.0",
|
||||||
|
"eslint": "^8.2.0",
|
||||||
"jasmine-core": "~3.9.0",
|
"jasmine-core": "~3.9.0",
|
||||||
"jasmine-spec-reporter": "~7.0.0",
|
"jasmine-spec-reporter": "~7.0.0",
|
||||||
"karma": "~6.3.6",
|
"karma": "~6.3.6",
|
||||||
@ -77,6 +76,6 @@
|
|||||||
"stylelint-config-standard": "^22.0.0",
|
"stylelint-config-standard": "^22.0.0",
|
||||||
"stylelint-scss": "^3.21.0",
|
"stylelint-scss": "^3.21.0",
|
||||||
"ts-node": "~10.2.1",
|
"ts-node": "~10.2.1",
|
||||||
"typescript": "^4.2.4"
|
"typescript": "^4.4.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
.root-header {
|
.root-header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -40,6 +40,7 @@ import { WarnDialogModule } from './modules/warn-dialog/warn-dialog.module';
|
|||||||
import { SignedoutComponent } from './pages/signedout/signedout.component';
|
import { SignedoutComponent } from './pages/signedout/signedout.component';
|
||||||
import { HasFeaturePipeModule } from './pipes/has-feature-pipe/has-feature-pipe.module';
|
import { HasFeaturePipeModule } from './pipes/has-feature-pipe/has-feature-pipe.module';
|
||||||
import { HasRolePipeModule } from './pipes/has-role-pipe/has-role-pipe.module';
|
import { HasRolePipeModule } from './pipes/has-role-pipe/has-role-pipe.module';
|
||||||
|
import { AdminService } from './services/admin.service';
|
||||||
import { AuthenticationService } from './services/authentication.service';
|
import { AuthenticationService } from './services/authentication.service';
|
||||||
import { GrpcAuthService } from './services/grpc-auth.service';
|
import { GrpcAuthService } from './services/grpc-auth.service';
|
||||||
import { GrpcService } from './services/grpc.service';
|
import { GrpcService } from './services/grpc.service';
|
||||||
@ -47,6 +48,7 @@ import { AuthInterceptor } from './services/interceptors/auth.interceptor';
|
|||||||
import { GRPC_INTERCEPTORS } from './services/interceptors/grpc-interceptor';
|
import { GRPC_INTERCEPTORS } from './services/interceptors/grpc-interceptor';
|
||||||
import { I18nInterceptor } from './services/interceptors/i18n.interceptor';
|
import { I18nInterceptor } from './services/interceptors/i18n.interceptor';
|
||||||
import { OrgInterceptor } from './services/interceptors/org.interceptor';
|
import { OrgInterceptor } from './services/interceptors/org.interceptor';
|
||||||
|
import { ManagementService } from './services/mgmt.service';
|
||||||
import { RefreshService } from './services/refresh.service';
|
import { RefreshService } from './services/refresh.service';
|
||||||
import { SeoService } from './services/seo.service';
|
import { SeoService } from './services/seo.service';
|
||||||
import { StatehandlerProcessorService, StatehandlerProcessorServiceImpl } from './services/statehandler-processor.service';
|
import { StatehandlerProcessorService, StatehandlerProcessorServiceImpl } from './services/statehandler-processor.service';
|
||||||
@ -183,6 +185,8 @@ const authConfig: AuthConfig = {
|
|||||||
GrpcService,
|
GrpcService,
|
||||||
AuthenticationService,
|
AuthenticationService,
|
||||||
GrpcAuthService,
|
GrpcAuthService,
|
||||||
|
ManagementService,
|
||||||
|
AdminService,
|
||||||
SubscriptionService,
|
SubscriptionService,
|
||||||
AssetService,
|
AssetService,
|
||||||
{ provide: 'windowObject', useValue: window },
|
{ provide: 'windowObject', useValue: window },
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin app-card-theme($theme) {
|
@mixin app-card-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
.radio-button-wrapper {
|
.radio-button-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
.radio-button-wrapper {
|
.radio-button-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin avatar-theme($theme) {
|
@mixin avatar-theme($theme) {
|
||||||
$primary: map-get($theme, primary);
|
$primary: map-get($theme, primary);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin card-theme($theme) {
|
@mixin card-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
.change-header {
|
.change-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin detail-layout-theme($theme) {
|
@mixin detail-layout-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin cnsl-form-field-theme($theme) {
|
@mixin cnsl-form-field-theme($theme) {
|
||||||
$primary: map-get($theme, primary);
|
$primary: map-get($theme, primary);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
.idp-radio-button-wrapper {
|
.idp-radio-button-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin info-row-theme($theme) {
|
@mixin info-row-theme($theme) {
|
||||||
$foreground: map-get($theme, foreground);
|
$foreground: map-get($theme, foreground);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin info-section-theme($theme) {
|
@mixin info-section-theme($theme) {
|
||||||
$primary: map-get($theme, primary);
|
$primary: map-get($theme, primary);
|
||||||
|
@ -2,452 +2,462 @@ import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
|
|||||||
import { getSupportedInputTypes, Platform } from '@angular/cdk/platform';
|
import { getSupportedInputTypes, Platform } from '@angular/cdk/platform';
|
||||||
import { AutofillMonitor } from '@angular/cdk/text-field';
|
import { AutofillMonitor } from '@angular/cdk/text-field';
|
||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
Directive,
|
Directive,
|
||||||
DoCheck,
|
DoCheck,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
HostListener,
|
HostListener,
|
||||||
Inject,
|
Inject,
|
||||||
Input,
|
Input,
|
||||||
NgZone,
|
NgZone,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
Optional,
|
Optional,
|
||||||
Self,
|
Self,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormGroupDirective, NgControl, NgForm } from '@angular/forms';
|
import { FormGroupDirective, NgControl, NgForm } from '@angular/forms';
|
||||||
import { CanUpdateErrorState, CanUpdateErrorStateCtor, ErrorStateMatcher, mixinErrorState } from '@angular/material/core';
|
import { CanUpdateErrorState, ErrorStateMatcher, mixinErrorState } from '@angular/material/core';
|
||||||
import { MAT_FORM_FIELD, MatFormField, MatFormFieldControl } from '@angular/material/form-field';
|
import { MAT_FORM_FIELD, MatFormField, MatFormFieldControl } from '@angular/material/form-field';
|
||||||
import { getMatInputUnsupportedTypeError, MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input';
|
import { getMatInputUnsupportedTypeError, MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
|
||||||
// Invalid input type. Using one of these will throw an MatInputUnsupportedTypeError.
|
// Invalid input type. Using one of these will throw an MatInputUnsupportedTypeError.
|
||||||
const MAT_INPUT_INVALID_TYPES = [
|
const MAT_INPUT_INVALID_TYPES = ['button', 'checkbox', 'file', 'hidden', 'image', 'radio', 'range', 'reset', 'submit'];
|
||||||
'button',
|
|
||||||
'checkbox',
|
|
||||||
'file',
|
|
||||||
'hidden',
|
|
||||||
'image',
|
|
||||||
'radio',
|
|
||||||
'range',
|
|
||||||
'reset',
|
|
||||||
'submit',
|
|
||||||
];
|
|
||||||
|
|
||||||
let nextUniqueId = 0;
|
let nextUniqueId = 0;
|
||||||
|
|
||||||
// Boilerplate for applying mixins to MatInput.
|
const _MatInputBase = mixinErrorState(
|
||||||
/** @docs-private */
|
class {
|
||||||
class MatInputBase {
|
constructor(
|
||||||
constructor(public _defaultErrorStateMatcher: ErrorStateMatcher,
|
public _defaultErrorStateMatcher: ErrorStateMatcher,
|
||||||
public _parentForm: NgForm,
|
public _parentForm: NgForm,
|
||||||
public _parentFormGroup: FormGroupDirective,
|
public _parentFormGroup: FormGroupDirective,
|
||||||
/** @docs-private */
|
/** @docs-private */
|
||||||
public ngControl: NgControl) { }
|
public ngControl: NgControl,
|
||||||
}
|
) {}
|
||||||
const _MatInputMixinBase: CanUpdateErrorStateCtor & typeof MatInputBase =
|
},
|
||||||
mixinErrorState(MatInputBase);
|
);
|
||||||
|
|
||||||
/** Directive that allows a native input to work inside a `MatFormField`. */
|
/** Directive that allows a native input to work inside a `MatFormField`. */
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: `input[cnslInput], textarea[cnslInput], select[cnslNativeControl]`,
|
selector: `input[cnslInput], textarea[cnslInput], select[cnslNativeControl]`,
|
||||||
exportAs: 'cnslInput',
|
exportAs: 'cnslInput',
|
||||||
host: {
|
host: {
|
||||||
/**
|
/**
|
||||||
* @breaking-change 8.0.0 remove .mat-form-field-autofill-control in favor of AutofillMonitor.
|
* @breaking-change 8.0.0 remove .mat-form-field-autofill-control in favor of AutofillMonitor.
|
||||||
*/
|
*/
|
||||||
// 'class': 'cnsl-input-element cnsl-form-field-autofill-control',
|
// 'class': 'cnsl-input-element cnsl-form-field-autofill-control',
|
||||||
// '[class.mat-input-server]': '_isServer',
|
// '[class.mat-input-server]': '_isServer',
|
||||||
// Native input properties that are overwritten by Angular inputs need to be synced with
|
// Native input properties that are overwritten by Angular inputs need to be synced with
|
||||||
// the native input element. Otherwise property bindings for those don't work.
|
// the native input element. Otherwise property bindings for those don't work.
|
||||||
'[attr.id]': 'id',
|
'[attr.id]': 'id',
|
||||||
// At the time of writing, we have a lot of customer tests that look up the input based on its
|
// At the time of writing, we have a lot of customer tests that look up the input based on its
|
||||||
// placeholder. Since we sometimes omit the placeholder attribute from the DOM to prevent screen
|
// placeholder. Since we sometimes omit the placeholder attribute from the DOM to prevent screen
|
||||||
// readers from reading it twice, we have to keep it somewhere in the DOM for the lookup.
|
// readers from reading it twice, we have to keep it somewhere in the DOM for the lookup.
|
||||||
'[attr.data-placeholder]': 'placeholder',
|
'[attr.data-placeholder]': 'placeholder',
|
||||||
'[disabled]': 'disabled',
|
'[disabled]': 'disabled',
|
||||||
'[required]': 'required',
|
'[required]': 'required',
|
||||||
'[attr.readonly]': 'readonly && !_isNativeSelect || null',
|
'[attr.readonly]': 'readonly && !_isNativeSelect || null',
|
||||||
'[attr.aria-invalid]': 'errorState',
|
'[attr.aria-invalid]': 'errorState',
|
||||||
'[attr.aria-required]': 'required.toString()',
|
'[attr.aria-required]': 'required.toString()',
|
||||||
},
|
},
|
||||||
providers: [{ provide: MatFormFieldControl, useExisting: InputDirective }],
|
providers: [{ provide: MatFormFieldControl, useExisting: InputDirective }],
|
||||||
})
|
})
|
||||||
export class InputDirective extends _MatInputMixinBase implements MatFormFieldControl<any>, OnChanges,
|
export class InputDirective
|
||||||
OnDestroy, AfterViewInit, DoCheck, CanUpdateErrorState {
|
extends _MatInputBase
|
||||||
protected _uid: string = `cnsl-input-${nextUniqueId++}`;
|
implements
|
||||||
protected _previousNativeValue: any;
|
MatFormFieldControl<any>,
|
||||||
private _inputValueAccessor: { value: any; };
|
OnChanges,
|
||||||
private _previousPlaceholder!: string | null;
|
CanUpdateErrorState,
|
||||||
|
OnDestroy,
|
||||||
|
AfterViewInit,
|
||||||
|
DoCheck,
|
||||||
|
CanUpdateErrorState
|
||||||
|
{
|
||||||
|
protected _uid: string = `cnsl-input-${nextUniqueId++}`;
|
||||||
|
protected _previousNativeValue: any;
|
||||||
|
private _inputValueAccessor: { value: any };
|
||||||
|
private _previousPlaceholder!: string | null;
|
||||||
|
|
||||||
/** Whether the component is being rendered on the server. */
|
/** Whether the component is being rendered on the server. */
|
||||||
readonly _isServer: boolean;
|
readonly _isServer: boolean;
|
||||||
|
|
||||||
/** Whether the component is a native html select. */
|
/** Whether the component is a native html select. */
|
||||||
readonly _isNativeSelect: boolean;
|
readonly _isNativeSelect: boolean;
|
||||||
|
|
||||||
/** Whether the component is a textarea. */
|
/** Whether the component is a textarea. */
|
||||||
readonly _isTextarea: boolean;
|
readonly _isTextarea: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
focused: boolean = false;
|
focused: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
readonly stateChanges: Subject<void> = new Subject<void>();
|
readonly stateChanges: Subject<void> = new Subject<void>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
controlType: string = 'mat-input';
|
controlType: string = 'mat-input';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
autofilled: boolean = false;
|
autofilled: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
get disabled(): boolean {
|
get disabled(): boolean {
|
||||||
if (this.ngControl && this.ngControl.disabled !== null) {
|
if (this.ngControl && this.ngControl.disabled !== null) {
|
||||||
return this.ngControl.disabled;
|
return this.ngControl.disabled;
|
||||||
}
|
|
||||||
return this._disabled;
|
|
||||||
}
|
}
|
||||||
set disabled(value: boolean) {
|
return this._disabled;
|
||||||
this._disabled = coerceBooleanProperty(value);
|
}
|
||||||
|
set disabled(value: boolean) {
|
||||||
|
this._disabled = coerceBooleanProperty(value);
|
||||||
|
|
||||||
// Browsers may not fire the blur event if the input is disabled too quickly.
|
// Browsers may not fire the blur event if the input is disabled too quickly.
|
||||||
// Reset from here to ensure that the element doesn't become stuck.
|
// Reset from here to ensure that the element doesn't become stuck.
|
||||||
if (this.focused) {
|
if (this.focused) {
|
||||||
this.focused = false;
|
this.focused = false;
|
||||||
this.stateChanges.next();
|
this.stateChanges.next();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
protected _disabled: boolean = false;
|
}
|
||||||
|
protected _disabled: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
get id(): string { return this._id; }
|
get id(): string {
|
||||||
set id(value: string) { this._id = value || this._uid; }
|
return this._id;
|
||||||
protected _id!: string;
|
}
|
||||||
|
set id(value: string) {
|
||||||
|
this._id = value || this._uid;
|
||||||
|
}
|
||||||
|
protected _id!: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
@Input() placeholder!: string;
|
@Input() placeholder!: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
get required(): boolean { return this._required; }
|
get required(): boolean {
|
||||||
set required(value: boolean) { this._required = coerceBooleanProperty(value); }
|
return this._required;
|
||||||
protected _required: boolean = false;
|
}
|
||||||
|
set required(value: boolean) {
|
||||||
|
this._required = coerceBooleanProperty(value);
|
||||||
|
}
|
||||||
|
protected _required: boolean = false;
|
||||||
|
|
||||||
/** Input type of the element. */
|
/** Input type of the element. */
|
||||||
@Input()
|
@Input()
|
||||||
get type(): string { return this._type; }
|
get type(): string {
|
||||||
set type(value: string) {
|
return this._type;
|
||||||
this._type = value || 'text';
|
}
|
||||||
this._validateType();
|
set type(value: string) {
|
||||||
|
this._type = value || 'text';
|
||||||
|
this._validateType();
|
||||||
|
|
||||||
// When using Angular inputs, developers are no longer able to set the properties on the native
|
// When using Angular inputs, developers are no longer able to set the properties on the native
|
||||||
// input element. To ensure that bindings for `type` work, we need to sync the setter
|
// input element. To ensure that bindings for `type` work, we need to sync the setter
|
||||||
// with the native property. Textarea elements don't support the type property or attribute.
|
// with the native property. Textarea elements don't support the type property or attribute.
|
||||||
if (!this._isTextarea && getSupportedInputTypes().has(this._type)) {
|
if (!this._isTextarea && getSupportedInputTypes().has(this._type)) {
|
||||||
(this._elementRef.nativeElement as HTMLInputElement).type = this._type;
|
(this._elementRef.nativeElement as HTMLInputElement).type = this._type;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
protected _type: string = 'text';
|
}
|
||||||
|
protected _type: string = 'text';
|
||||||
|
|
||||||
/** An object used to control when error messages are shown. */
|
/** An object used to control when error messages are shown. */
|
||||||
@Input() errorStateMatcher!: ErrorStateMatcher;
|
@Input() errorStateMatcher!: ErrorStateMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @angular-eslint/no-input-rename
|
// eslint-disable-next-line @angular-eslint/no-input-rename
|
||||||
@Input('aria-describedby') userAriaDescribedBy!: string;
|
@Input('aria-describedby') userAriaDescribedBy!: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented as part of MatFormFieldControl.
|
* Implemented as part of MatFormFieldControl.
|
||||||
* @docs-private
|
* @docs-private
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
get value(): string { return this._inputValueAccessor.value; }
|
get value(): string {
|
||||||
set value(value: string) {
|
return this._inputValueAccessor.value;
|
||||||
if (value !== this.value) {
|
}
|
||||||
this._inputValueAccessor.value = value;
|
set value(value: string) {
|
||||||
this.stateChanges.next();
|
if (value !== this.value) {
|
||||||
}
|
this._inputValueAccessor.value = value;
|
||||||
|
this.stateChanges.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether the element is readonly. */
|
||||||
|
@Input()
|
||||||
|
get readonly(): boolean {
|
||||||
|
return this._readonly;
|
||||||
|
}
|
||||||
|
set readonly(value: boolean) {
|
||||||
|
this._readonly = coerceBooleanProperty(value);
|
||||||
|
}
|
||||||
|
private _readonly: boolean = false;
|
||||||
|
|
||||||
|
protected _neverEmptyInputTypes: string[] = ['date', 'datetime', 'datetime-local', 'month', 'time', 'week'].filter((t) =>
|
||||||
|
getSupportedInputTypes().has(t),
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected _elementRef: ElementRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||||
|
protected _platform: Platform,
|
||||||
|
/** @docs-private */
|
||||||
|
@Optional() @Self() public ngControl: NgControl,
|
||||||
|
@Optional() _parentForm: NgForm,
|
||||||
|
@Optional() _parentFormGroup: FormGroupDirective,
|
||||||
|
_defaultErrorStateMatcher: ErrorStateMatcher,
|
||||||
|
@Optional() @Self() @Inject(MAT_INPUT_VALUE_ACCESSOR) inputValueAccessor: any,
|
||||||
|
private _autofillMonitor: AutofillMonitor,
|
||||||
|
ngZone: NgZone,
|
||||||
|
@Optional() @Inject(MAT_FORM_FIELD) private _formField?: MatFormField,
|
||||||
|
) {
|
||||||
|
super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
|
||||||
|
|
||||||
|
const element = this._elementRef.nativeElement;
|
||||||
|
const nodeName = element.nodeName.toLowerCase();
|
||||||
|
|
||||||
|
// If no input value accessor was explicitly specified, use the element as the input value
|
||||||
|
// accessor.
|
||||||
|
this._inputValueAccessor = inputValueAccessor || element;
|
||||||
|
|
||||||
|
this._previousNativeValue = this.value;
|
||||||
|
|
||||||
|
// Force setter to be called in case id was not specified.
|
||||||
|
this.id = this.id;
|
||||||
|
|
||||||
|
// On some versions of iOS the caret gets stuck in the wrong place when holding down the delete
|
||||||
|
// key. In order to get around this we need to "jiggle" the caret loose. Since this bug only
|
||||||
|
// exists on iOS, we only bother to install the listener on iOS.
|
||||||
|
if (_platform.IOS) {
|
||||||
|
ngZone.runOutsideAngular(() => {
|
||||||
|
_elementRef.nativeElement.addEventListener('keyup', (event: Event) => {
|
||||||
|
const el: HTMLInputElement = event.target as HTMLInputElement;
|
||||||
|
if (!el.value && !el.selectionStart && !el.selectionEnd) {
|
||||||
|
// Note: Just setting `0, 0` doesn't fix the issue. Setting
|
||||||
|
// `1, 1` fixes it for the first time that you type text and
|
||||||
|
// then hold delete. Toggling to `1, 1` and then back to
|
||||||
|
// `0, 0` seems to completely fix it.
|
||||||
|
el.setSelectionRange(1, 1);
|
||||||
|
el.setSelectionRange(0, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the element is readonly. */
|
this._isServer = !this._platform.isBrowser;
|
||||||
@Input()
|
this._isNativeSelect = nodeName === 'select';
|
||||||
get readonly(): boolean { return this._readonly; }
|
this._isTextarea = nodeName === 'textarea';
|
||||||
set readonly(value: boolean) { this._readonly = coerceBooleanProperty(value); }
|
|
||||||
private _readonly: boolean = false;
|
|
||||||
|
|
||||||
protected _neverEmptyInputTypes: string[] = [
|
if (this._isNativeSelect) {
|
||||||
'date',
|
this.controlType = (element as HTMLSelectElement).multiple ? 'mat-native-select-multiple' : 'mat-native-select';
|
||||||
'datetime',
|
|
||||||
'datetime-local',
|
|
||||||
'month',
|
|
||||||
'time',
|
|
||||||
'week',
|
|
||||||
].filter(t => getSupportedInputTypes().has(t));
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
protected _elementRef: ElementRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
|
||||||
protected _platform: Platform,
|
|
||||||
/** @docs-private */
|
|
||||||
@Optional() @Self() public ngControl: NgControl,
|
|
||||||
@Optional() _parentForm: NgForm,
|
|
||||||
@Optional() _parentFormGroup: FormGroupDirective,
|
|
||||||
_defaultErrorStateMatcher: ErrorStateMatcher,
|
|
||||||
@Optional() @Self() @Inject(MAT_INPUT_VALUE_ACCESSOR) inputValueAccessor: any,
|
|
||||||
private _autofillMonitor: AutofillMonitor,
|
|
||||||
ngZone: NgZone,
|
|
||||||
@Optional() @Inject(MAT_FORM_FIELD) private _formField?: MatFormField) {
|
|
||||||
|
|
||||||
super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
|
|
||||||
|
|
||||||
const element = this._elementRef.nativeElement;
|
|
||||||
const nodeName = element.nodeName.toLowerCase();
|
|
||||||
|
|
||||||
// If no input value accessor was explicitly specified, use the element as the input value
|
|
||||||
// accessor.
|
|
||||||
this._inputValueAccessor = inputValueAccessor || element;
|
|
||||||
|
|
||||||
this._previousNativeValue = this.value;
|
|
||||||
|
|
||||||
// Force setter to be called in case id was not specified.
|
|
||||||
this.id = this.id;
|
|
||||||
|
|
||||||
// On some versions of iOS the caret gets stuck in the wrong place when holding down the delete
|
|
||||||
// key. In order to get around this we need to "jiggle" the caret loose. Since this bug only
|
|
||||||
// exists on iOS, we only bother to install the listener on iOS.
|
|
||||||
if (_platform.IOS) {
|
|
||||||
ngZone.runOutsideAngular(() => {
|
|
||||||
_elementRef.nativeElement.addEventListener('keyup', (event: Event) => {
|
|
||||||
const el: HTMLInputElement = event.target as HTMLInputElement;
|
|
||||||
if (!el.value && !el.selectionStart && !el.selectionEnd) {
|
|
||||||
// Note: Just setting `0, 0` doesn't fix the issue. Setting
|
|
||||||
// `1, 1` fixes it for the first time that you type text and
|
|
||||||
// then hold delete. Toggling to `1, 1` and then back to
|
|
||||||
// `0, 0` seems to completely fix it.
|
|
||||||
el.setSelectionRange(1, 1);
|
|
||||||
el.setSelectionRange(0, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isServer = !this._platform.isBrowser;
|
|
||||||
this._isNativeSelect = nodeName === 'select';
|
|
||||||
this._isTextarea = nodeName === 'textarea';
|
|
||||||
|
|
||||||
if (this._isNativeSelect) {
|
|
||||||
this.controlType = (element as HTMLSelectElement).multiple ? 'mat-native-select-multiple' :
|
|
||||||
'mat-native-select';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
if (this._platform.isBrowser) {
|
if (this._platform.isBrowser) {
|
||||||
this._autofillMonitor.monitor(this._elementRef.nativeElement).subscribe(event => {
|
this._autofillMonitor.monitor(this._elementRef.nativeElement).subscribe((event) => {
|
||||||
this.autofilled = event.isAutofilled;
|
this.autofilled = event.isAutofilled;
|
||||||
this.stateChanges.next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges(): void {
|
|
||||||
this.stateChanges.next();
|
this.stateChanges.next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(): void {
|
||||||
|
this.stateChanges.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.stateChanges.complete();
|
||||||
|
|
||||||
|
if (this._platform.isBrowser) {
|
||||||
|
this._autofillMonitor.stopMonitoring(this._elementRef.nativeElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngDoCheck(): void {
|
||||||
|
if (this.ngControl) {
|
||||||
|
// We need to re-evaluate this on every change detection cycle, because there are some
|
||||||
|
// error triggers that we can't subscribe to (e.g. parent form submissions). This means
|
||||||
|
// that whatever logic is in here has to be super lean or we risk destroying the performance.
|
||||||
|
this.updateErrorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
// We need to dirty-check the native element's value, because there are some cases where
|
||||||
this.stateChanges.complete();
|
// we won't be notified when it changes (e.g. the consumer isn't using forms or they're
|
||||||
|
// updating the value using `emitEvent: false`).
|
||||||
|
this._dirtyCheckNativeValue();
|
||||||
|
|
||||||
if (this._platform.isBrowser) {
|
// We need to dirty-check and set the placeholder attribute ourselves, because whether it's
|
||||||
this._autofillMonitor.stopMonitoring(this._elementRef.nativeElement);
|
// present or not depends on a query which is prone to "changed after checked" errors.
|
||||||
}
|
this._dirtyCheckPlaceholder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Focuses the input. */
|
||||||
|
focus(options?: FocusOptions): void {
|
||||||
|
this._elementRef.nativeElement.focus(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
|
||||||
|
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
|
||||||
|
// ViewEngine they're overwritten.
|
||||||
|
/** Callback for the cases where the focused state of the input changes. */
|
||||||
|
/* eslint-disable */
|
||||||
|
@HostListener('focus', ['true'])
|
||||||
|
@HostListener('blur', ['false'])
|
||||||
|
/* eslint-enable */
|
||||||
|
_focusChanged(isFocused: boolean): void {
|
||||||
|
if (isFocused !== this.focused && (!this.readonly || !isFocused)) {
|
||||||
|
this.focused = isFocused;
|
||||||
|
this.stateChanges.next();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngDoCheck(): void {
|
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
|
||||||
if (this.ngControl) {
|
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
|
||||||
// We need to re-evaluate this on every change detection cycle, because there are some
|
// ViewEngine they're overwritten.
|
||||||
// error triggers that we can't subscribe to (e.g. parent form submissions). This means
|
// eslint-disable-next-line
|
||||||
// that whatever logic is in here has to be super lean or we risk destroying the performance.
|
@HostListener('input')
|
||||||
this.updateErrorState();
|
_onInput(): void {
|
||||||
}
|
// This is a noop function and is used to let Angular know whenever the value changes.
|
||||||
|
// Angular will run a new change detection each time the `input` event has been dispatched.
|
||||||
|
// It's necessary that Angular recognizes the value change, because when floatingLabel
|
||||||
|
// is set to false and Angular forms aren't used, the placeholder won't recognize the
|
||||||
|
// value changes and will not disappear.
|
||||||
|
// Listening to the input event wouldn't be necessary when the input is using the
|
||||||
|
// FormsModule or ReactiveFormsModule, because Angular forms also listens to input events.
|
||||||
|
}
|
||||||
|
|
||||||
// We need to dirty-check the native element's value, because there are some cases where
|
/** Does some manual dirty checking on the native input `placeholder` attribute. */
|
||||||
// we won't be notified when it changes (e.g. the consumer isn't using forms or they're
|
private _dirtyCheckPlaceholder(): void {
|
||||||
// updating the value using `emitEvent: false`).
|
// If we're hiding the native placeholder, it should also be cleared from the DOM, otherwise
|
||||||
this._dirtyCheckNativeValue();
|
// screen readers will read it out twice: once from the label and once from the attribute.
|
||||||
|
const placeholder = this._formField?._hideControlPlaceholder?.() ? null : this.placeholder;
|
||||||
// We need to dirty-check and set the placeholder attribute ourselves, because whether it's
|
if (placeholder !== this._previousPlaceholder) {
|
||||||
// present or not depends on a query which is prone to "changed after checked" errors.
|
const element = this._elementRef.nativeElement;
|
||||||
this._dirtyCheckPlaceholder();
|
this._previousPlaceholder = placeholder;
|
||||||
|
placeholder ? element.setAttribute('placeholder', placeholder) : element.removeAttribute('placeholder');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Focuses the input. */
|
/** Does some manual dirty checking on the native input `value` property. */
|
||||||
focus(options?: FocusOptions): void {
|
protected _dirtyCheckNativeValue(): void {
|
||||||
this._elementRef.nativeElement.focus(options);
|
const newValue = this._elementRef.nativeElement.value;
|
||||||
|
|
||||||
|
if (this._previousNativeValue !== newValue) {
|
||||||
|
this._previousNativeValue = newValue;
|
||||||
|
this.stateChanges.next();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
|
/** Make sure the input is a supported type. */
|
||||||
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
|
protected _validateType(): void {
|
||||||
// ViewEngine they're overwritten.
|
if (MAT_INPUT_INVALID_TYPES.indexOf(this._type) > -1) {
|
||||||
/** Callback for the cases where the focused state of the input changes. */
|
throw getMatInputUnsupportedTypeError(this._type);
|
||||||
/* eslint-disable */
|
|
||||||
@HostListener('focus', ['true'])
|
|
||||||
@HostListener('blur', ['false'])
|
|
||||||
/* eslint-enable */
|
|
||||||
_focusChanged(isFocused: boolean): void {
|
|
||||||
if (isFocused !== this.focused && (!this.readonly || !isFocused)) {
|
|
||||||
this.focused = isFocused;
|
|
||||||
this.stateChanges.next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
|
/** Checks whether the input type is one of the types that are never empty. */
|
||||||
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
|
protected _isNeverEmpty(): boolean {
|
||||||
// ViewEngine they're overwritten.
|
return this._neverEmptyInputTypes.indexOf(this._type) > -1;
|
||||||
// eslint-disable-next-line
|
}
|
||||||
@HostListener('input')
|
|
||||||
_onInput(): void {
|
/** Checks whether the input is invalid based on the native validation. */
|
||||||
// This is a noop function and is used to let Angular know whenever the value changes.
|
protected _isBadInput(): boolean {
|
||||||
// Angular will run a new change detection each time the `input` event has been dispatched.
|
// The `validity` property won't be present on platform-server.
|
||||||
// It's necessary that Angular recognizes the value change, because when floatingLabel
|
const validity = (this._elementRef.nativeElement as HTMLInputElement).validity;
|
||||||
// is set to false and Angular forms aren't used, the placeholder won't recognize the
|
return validity && validity.badInput;
|
||||||
// value changes and will not disappear.
|
}
|
||||||
// Listening to the input event wouldn't be necessary when the input is using the
|
|
||||||
// FormsModule or ReactiveFormsModule, because Angular forms also listens to input events.
|
/**
|
||||||
|
* Implemented as part of MatFormFieldControl.
|
||||||
|
* @docs-private
|
||||||
|
*/
|
||||||
|
get empty(): boolean {
|
||||||
|
return !this._isNeverEmpty() && !this._elementRef.nativeElement.value && !this._isBadInput() && !this.autofilled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implemented as part of MatFormFieldControl.
|
||||||
|
* @docs-private
|
||||||
|
*/
|
||||||
|
get shouldLabelFloat(): boolean {
|
||||||
|
if (this._isNativeSelect) {
|
||||||
|
// For a single-selection `<select>`, the label should float when the selected option has
|
||||||
|
// a non-empty display value. For a `<select multiple>`, the label *always* floats to avoid
|
||||||
|
// overlapping the label with the options.
|
||||||
|
const selectElement = this._elementRef.nativeElement as HTMLSelectElement;
|
||||||
|
const firstOption: HTMLOptionElement | undefined = selectElement.options[0];
|
||||||
|
|
||||||
|
// On most browsers the `selectedIndex` will always be 0, however on IE and Edge it'll be
|
||||||
|
// -1 if the `value` is set to something, that isn't in the list of options, at a later point.
|
||||||
|
return (
|
||||||
|
this.focused ||
|
||||||
|
selectElement.multiple ||
|
||||||
|
!this.empty ||
|
||||||
|
!!(selectElement.selectedIndex > -1 && firstOption && firstOption.label)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this.focused || !this.empty;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Does some manual dirty checking on the native input `placeholder` attribute. */
|
/**
|
||||||
private _dirtyCheckPlaceholder(): void {
|
* Implemented as part of MatFormFieldControl.
|
||||||
// If we're hiding the native placeholder, it should also be cleared from the DOM, otherwise
|
* @docs-private
|
||||||
// screen readers will read it out twice: once from the label and once from the attribute.
|
*/
|
||||||
const placeholder = this._formField?._hideControlPlaceholder?.() ? null : this.placeholder;
|
setDescribedByIds(ids: string[]): void {
|
||||||
if (placeholder !== this._previousPlaceholder) {
|
if (ids.length) {
|
||||||
const element = this._elementRef.nativeElement;
|
this._elementRef.nativeElement.setAttribute('aria-describedby', ids.join(' '));
|
||||||
this._previousPlaceholder = placeholder;
|
} else {
|
||||||
placeholder ?
|
this._elementRef.nativeElement.removeAttribute('aria-describedby');
|
||||||
element.setAttribute('placeholder', placeholder) : element.removeAttribute('placeholder');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Does some manual dirty checking on the native input `value` property. */
|
/**
|
||||||
protected _dirtyCheckNativeValue(): void {
|
* Implemented as part of MatFormFieldControl.
|
||||||
const newValue = this._elementRef.nativeElement.value;
|
* @docs-private
|
||||||
|
*/
|
||||||
if (this._previousNativeValue !== newValue) {
|
onContainerClick(): void {
|
||||||
this._previousNativeValue = newValue;
|
// Do not re-focus the input element if the element is already focused. Otherwise it can happen
|
||||||
this.stateChanges.next();
|
// that someone clicks on a time input and the cursor resets to the "hours" field while the
|
||||||
}
|
// "minutes" field was actually clicked. See: https://github.com/angular/components/issues/12849
|
||||||
|
if (!this.focused) {
|
||||||
|
this.focus();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/* eslint-disable */
|
||||||
|
static ngAcceptInputType_disabled: BooleanInput;
|
||||||
|
static ngAcceptInputType_readonly: BooleanInput;
|
||||||
|
static ngAcceptInputType_required: BooleanInput;
|
||||||
|
|
||||||
/** Make sure the input is a supported type. */
|
// Accept `any` to avoid conflicts with other directives on `<input>` that may
|
||||||
protected _validateType(): void {
|
// accept different types.
|
||||||
if (MAT_INPUT_INVALID_TYPES.indexOf(this._type) > -1) {
|
static ngAcceptInputType_value: any;
|
||||||
throw getMatInputUnsupportedTypeError(this._type);
|
/* eslint-enable */
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Checks whether the input type is one of the types that are never empty. */
|
|
||||||
protected _isNeverEmpty(): boolean {
|
|
||||||
return this._neverEmptyInputTypes.indexOf(this._type) > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Checks whether the input is invalid based on the native validation. */
|
|
||||||
protected _isBadInput(): boolean {
|
|
||||||
// The `validity` property won't be present on platform-server.
|
|
||||||
const validity = (this._elementRef.nativeElement as HTMLInputElement).validity;
|
|
||||||
return validity && validity.badInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implemented as part of MatFormFieldControl.
|
|
||||||
* @docs-private
|
|
||||||
*/
|
|
||||||
get empty(): boolean {
|
|
||||||
return !this._isNeverEmpty() && !this._elementRef.nativeElement.value && !this._isBadInput() &&
|
|
||||||
!this.autofilled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implemented as part of MatFormFieldControl.
|
|
||||||
* @docs-private
|
|
||||||
*/
|
|
||||||
get shouldLabelFloat(): boolean {
|
|
||||||
if (this._isNativeSelect) {
|
|
||||||
// For a single-selection `<select>`, the label should float when the selected option has
|
|
||||||
// a non-empty display value. For a `<select multiple>`, the label *always* floats to avoid
|
|
||||||
// overlapping the label with the options.
|
|
||||||
const selectElement = this._elementRef.nativeElement as HTMLSelectElement;
|
|
||||||
const firstOption: HTMLOptionElement | undefined = selectElement.options[0];
|
|
||||||
|
|
||||||
// On most browsers the `selectedIndex` will always be 0, however on IE and Edge it'll be
|
|
||||||
// -1 if the `value` is set to something, that isn't in the list of options, at a later point.
|
|
||||||
return this.focused || selectElement.multiple || !this.empty ||
|
|
||||||
!!(selectElement.selectedIndex > -1 && firstOption && firstOption.label);
|
|
||||||
} else {
|
|
||||||
return this.focused || !this.empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implemented as part of MatFormFieldControl.
|
|
||||||
* @docs-private
|
|
||||||
*/
|
|
||||||
setDescribedByIds(ids: string[]): void {
|
|
||||||
if (ids.length) {
|
|
||||||
this._elementRef.nativeElement.setAttribute('aria-describedby', ids.join(' '));
|
|
||||||
} else {
|
|
||||||
this._elementRef.nativeElement.removeAttribute('aria-describedby');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implemented as part of MatFormFieldControl.
|
|
||||||
* @docs-private
|
|
||||||
*/
|
|
||||||
onContainerClick(): void {
|
|
||||||
// Do not re-focus the input element if the element is already focused. Otherwise it can happen
|
|
||||||
// that someone clicks on a time input and the cursor resets to the "hours" field while the
|
|
||||||
// "minutes" field was actually clicked. See: https://github.com/angular/components/issues/12849
|
|
||||||
if (!this.focused) {
|
|
||||||
this.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* eslint-disable */
|
|
||||||
static ngAcceptInputType_disabled: BooleanInput;
|
|
||||||
static ngAcceptInputType_readonly: BooleanInput;
|
|
||||||
static ngAcceptInputType_required: BooleanInput;
|
|
||||||
|
|
||||||
// Accept `any` to avoid conflicts with other directives on `<input>` that may
|
|
||||||
// accept different types.
|
|
||||||
static ngAcceptInputType_value: any;
|
|
||||||
/* eslint-enable */
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin cnsl-label-theme($theme) {
|
@mixin cnsl-label-theme($theme) {
|
||||||
$primary: map-get($theme, primary);
|
$primary: map-get($theme, primary);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin onboarding-theme($theme) {
|
@mixin onboarding-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@import './preview/preview.component.scss';
|
@import './preview/preview.component.scss';
|
||||||
|
|
||||||
|
@ -1,63 +1,63 @@
|
|||||||
<h1 mat-dialog-title>
|
<h1 mat-dialog-title>
|
||||||
<span class="title">{{'USER.MFA.DIALOG.ADD_MFA_TITLE' | translate}} {{data?.number}}</span>
|
<span class="title">{{'USER.MFA.DIALOG.ADD_MFA_TITLE' | translate}} {{data?.number}}</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<ng-container *ngIf="selectedType === undefined">
|
<ng-container *ngIf="selectedType === undefined">
|
||||||
<p class="desc">{{'USER.MFA.DIALOG.ADD_MFA_DESCRIPTION' | translate}}</p>
|
<p class="desc">{{'USER.MFA.DIALOG.ADD_MFA_DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
<div class="type-selection">
|
<div class="type-selection">
|
||||||
<button mat-raised-button color="primary" [disabled]="data.otpDisabled" (click)="selectType(AuthFactorType.OTP)">
|
<button mat-raised-button color="primary" [disabled]="data.otpDisabled" (click)="selectType(AuthFactorType.OTP)">
|
||||||
<div class="otp-btn">
|
<div class="otp-btn">
|
||||||
<mat-icon class="icon" svgIcon="mdi_radar"></mat-icon>
|
<mat-icon class="icon" svgIcon="mdi_radar"></mat-icon>
|
||||||
<span>{{'USER.MFA.OTP' | translate}}</span>
|
<span>{{'USER.MFA.OTP' | translate}}</span>
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button mat-raised-button color="primary" (click)="selectType(AuthFactorType.U2F)">
|
|
||||||
<div class="u2f-btn">
|
|
||||||
<div class="icon-row">
|
|
||||||
<i matTooltip="Fingerprint" class="las la-fingerprint"></i>
|
|
||||||
<i matTooltip="Security Key" class="lab la-usb"></i>
|
|
||||||
<mat-icon matTooltip="NFC">nfc</mat-icon>
|
|
||||||
</div>
|
|
||||||
<span>{{'USER.MFA.U2F' | translate}}</span>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</button>
|
||||||
|
<button mat-raised-button color="primary" (click)="selectType(AuthFactorType.U2F)">
|
||||||
<div class="otp" *ngIf="selectedType === AuthFactorType.OTP">
|
<div class="u2f-btn">
|
||||||
<p class="desc">{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}</p>
|
<div class="icon-row">
|
||||||
|
<i matTooltip="Fingerprint" class="las la-fingerprint"></i>
|
||||||
<div class="qrcode-wrapper">
|
<i matTooltip="Security Key" class="lab la-usb"></i>
|
||||||
<qrcode *ngIf="otpurl" class="qrcode" [qrdata]="otpurl" [width]="150" [errorCorrectionLevel]="'M'"></qrcode>
|
<mat-icon matTooltip="NFC">nfc</mat-icon>
|
||||||
|
</div>
|
||||||
|
<span>{{'USER.MFA.U2F' | translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<cnsl-form-field class="formfield" label="Access Code" required="true">
|
<div class="otp" *ngIf="selectedType === AuthFactorType.OTP">
|
||||||
<cnsl-label>Code</cnsl-label>
|
<p class="desc">{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}</p>
|
||||||
<input cnslInput [(ngModel)]="otpcode" />
|
|
||||||
</cnsl-form-field>
|
<div class="qrcode-wrapper">
|
||||||
|
<qr-code *ngIf="otpurl" class="qrcode" [value]="otpurl" [size]="150" [errorCorrectionLevel]="'M'"></qr-code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="u2f" *ngIf="selectedType === AuthFactorType.U2F">
|
<cnsl-form-field class="formfield" label="Access Code" required="true">
|
||||||
<p>{{'USER.MFA.U2F_DIALOG_DESCRIPTION' | translate}}</p>
|
<cnsl-label>Code</cnsl-label>
|
||||||
|
<input cnslInput [(ngModel)]="otpcode" />
|
||||||
|
</cnsl-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
<cnsl-form-field class="form-field" label="Name" required="true">
|
<div class="u2f" *ngIf="selectedType === AuthFactorType.U2F">
|
||||||
<cnsl-label>{{'USER.MFA.U2F_NAME' | translate}}</cnsl-label>
|
<p>{{'USER.MFA.U2F_DIALOG_DESCRIPTION' | translate}}</p>
|
||||||
<input cnslInput [(ngModel)]="u2fname" required (keydown.enter)="u2fname ? submitU2F() : null" />
|
|
||||||
</cnsl-form-field>
|
|
||||||
|
|
||||||
<mat-spinner diameter="30" *ngIf="u2fLoading"></mat-spinner>
|
<cnsl-form-field class="form-field" label="Name" required="true">
|
||||||
|
<cnsl-label>{{'USER.MFA.U2F_NAME' | translate}}</cnsl-label>
|
||||||
|
<input cnslInput [(ngModel)]="u2fname" required (keydown.enter)="u2fname ? submitU2F() : null" />
|
||||||
|
</cnsl-form-field>
|
||||||
|
|
||||||
<p class="error">{{u2fError}}</p>
|
<mat-spinner diameter="30" *ngIf="u2fLoading"></mat-spinner>
|
||||||
</div>
|
|
||||||
|
<p class="error">{{u2fError}}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions class="action">
|
<div mat-dialog-actions class="action">
|
||||||
<button cdkFocusInitial color="primary" mat-button class="ok-button" (click)="closeDialog()">
|
<button cdkFocusInitial color="primary" mat-button class="ok-button" (click)="closeDialog()">
|
||||||
{{'ACTIONS.CLOSE' | translate}}
|
{{'ACTIONS.CLOSE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button *ngIf="selectedType !== undefined" cdkFocusInitial color="primary" mat-raised-button class="ok-button"
|
<button *ngIf="selectedType !== undefined" cdkFocusInitial color="primary" mat-raised-button class="ok-button"
|
||||||
(click)="submitAuth()">
|
(click)="submitAuth()">
|
||||||
{{'ACTIONS.CREATE' | translate}}
|
{{'ACTIONS.CREATE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
@ -9,12 +9,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<cnsl-form-field class="form-field" label="Name" required="true">
|
<cnsl-form-field class="form-field" label="Name" required="true">
|
||||||
<cnsl-label>{{'USER.MFA.U2F_NAME' | translate}}</cnsl-label>
|
<cnsl-label>{{'USER.MFA.U2F_NAME' | translate}}</cnsl-label>
|
||||||
<input cnslInput [(ngModel)]="name" required (keydown.enter)="name ? closeDialogWithCode() : null" />
|
<input cnslInput [(ngModel)]="name" required (keydown.enter)="name ? closeDialogWithCode() : null" />
|
||||||
</cnsl-form-field>
|
</cnsl-form-field>
|
||||||
|
|
||||||
<button [disabled]="!name" mat-raised-button class="ok-button" color="primary"
|
<button [disabled]="!name" mat-raised-button class="ok-button" color="primary"
|
||||||
(click)="closeDialogWithCode()">{{'USER.PASSWORDLESS.DIALOG.NEW' | translate}}
|
(click)="closeDialogWithCode()">{{'USER.PASSWORDLESS.DIALOG.NEW' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p class="error">{{error}}</p>
|
<p class="error">{{error}}</p>
|
||||||
@ -26,7 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button mat-raised-button class="send-button" color="primary"
|
<button mat-raised-button class="send-button" color="primary"
|
||||||
(click)="sendMyPasswordlessLink()">{{'USER.PASSWORDLESS.DIALOG.SEND' | translate}}
|
(click)="sendMyPasswordlessLink()">{{'USER.PASSWORDLESS.DIALOG.SEND' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
@ -34,9 +34,9 @@
|
|||||||
<i class="icon las la-qrcode"></i>
|
<i class="icon las la-qrcode"></i>
|
||||||
<p>{{'USER.PASSWORDLESS.DIALOG.QRCODE_DESCRIPTION' | translate}}</p>
|
<p>{{'USER.PASSWORDLESS.DIALOG.QRCODE_DESCRIPTION' | translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button mat-raised-button class="qr-button" color="primary"
|
<button mat-raised-button class="qr-button" color="primary"
|
||||||
(click)="addMyPasswordlessLink()">{{'USER.PASSWORDLESS.DIALOG.QRCODE' | translate}}
|
(click)="addMyPasswordlessLink()">{{'USER.PASSWORDLESS.DIALOG.QRCODE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -55,12 +55,13 @@
|
|||||||
<p>{{'USER.PASSWORDLESS.DIALOG.QRCODE_SCAN' | translate}}</p>
|
<p>{{'USER.PASSWORDLESS.DIALOG.QRCODE_SCAN' | translate}}</p>
|
||||||
|
|
||||||
<div class="qrcode-wrapper">
|
<div class="qrcode-wrapper">
|
||||||
<qrcode *ngIf="qrcodeLink" class="qrcode" [qrdata]="qrcodeLink" [width]="150" [errorCorrectionLevel]="'M'"></qrcode>
|
<qr-code *ngIf="qrcodeLink" class="qrcode" [value]="qrcodeLink" [size]="150" [errorCorrectionLevel]="'M'">
|
||||||
</div>
|
</qr-code>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-spinner diameter="30" *ngIf="loading"></mat-spinner>
|
<mat-spinner diameter="30" *ngIf="loading"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions class="action">
|
<div mat-dialog-actions class="action">
|
||||||
<button mat-button (click)="closeDialog()">{{'ACTIONS.CLOSE' | translate}}</button>
|
<button mat-button (click)="closeDialog()">{{'ACTIONS.CLOSE' | translate}}</button>
|
||||||
</div>
|
</div>
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin theme-card($theme) {
|
@mixin theme-card($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin membership-theme($theme) {
|
@mixin membership-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -12,7 +12,7 @@ import { MatTableModule } from '@angular/material/table';
|
|||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { QRCodeModule } from 'angularx-qrcode';
|
import { QrCodeModule } from 'ng-qrcode';
|
||||||
import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.module';
|
import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.module';
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
||||||
@ -93,7 +93,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
|||||||
DetailFormMachineModule,
|
DetailFormMachineModule,
|
||||||
WarnDialogModule,
|
WarnDialogModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
QRCodeModule,
|
QrCodeModule,
|
||||||
MetaLayoutModule,
|
MetaLayoutModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
HasRolePipeModule,
|
HasRolePipeModule,
|
||||||
@ -124,4 +124,4 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
|||||||
InfoSectionModule,
|
InfoSectionModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class UserDetailModule { }
|
export class UserDetailModule {}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"authServiceUrl": "https://api.zitadel.dev",
|
"authServiceUrl": "https://api.zitadel.dev",
|
||||||
"mgmtServiceUrl": "https://api.zitadel.dev",
|
"mgmtServiceUrl": "https://api.zitadel.dev",
|
||||||
"adminServiceUrl":"https://api.zitadel.dev",
|
"adminServiceUrl": "https://api.zitadel.dev",
|
||||||
"subscriptionServiceUrl":"https://sub.zitadel.dev",
|
"subscriptionServiceUrl": "https://sub.zitadel.dev",
|
||||||
"assetServiceUrl":"https://api.zitadel.dev",
|
"assetServiceUrl": "https://api.zitadel.dev",
|
||||||
"issuer": "https://issuer.zitadel.dev",
|
"issuer": "https://issuer.zitadel.dev",
|
||||||
"clientid": "70669160379706195@zitadel"
|
"clientid": "70669160379706195@zitadel"
|
||||||
}
|
}
|
||||||
|
@ -18,16 +18,6 @@
|
|||||||
* BROWSER POLYFILLS
|
* BROWSER POLYFILLS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
|
||||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Web Animations `@angular/platform-browser/animations`
|
|
||||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
|
||||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
|
||||||
*/
|
|
||||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@import './component-themes';
|
@import './component-themes';
|
||||||
@import '~@angular/material/theming';
|
@import '@angular/material/theming';
|
||||||
|
|
||||||
// Plus imports for other components in your app.
|
// Plus imports for other components in your app.
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin cnsl-error-theme($theme) {
|
@mixin cnsl-error-theme($theme) {
|
||||||
$warn: map-get($theme, warn);
|
$warn: map-get($theme, warn);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin input-theme($theme) {
|
@mixin input-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin link-theme($theme) {
|
@mixin link-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin sidenav-list-theme($theme) {
|
@mixin sidenav-list-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use '~@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
@mixin table-theme($theme) {
|
@mixin table-theme($theme) {
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
@ -7,7 +7,9 @@ import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@ang
|
|||||||
declare const require: any;
|
declare const require: any;
|
||||||
|
|
||||||
// First, initialize the Angular testing environment.
|
// First, initialize the Angular testing environment.
|
||||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
|
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
|
||||||
|
teardown: { destroyAfterEach: false }
|
||||||
|
});
|
||||||
// Then we find all the tests.
|
// Then we find all the tests.
|
||||||
const context = require.context('./', true, /\.spec\.ts$/);
|
const context = require.context('./', true, /\.spec\.ts$/);
|
||||||
// And load the modules.
|
// And load the modules.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user