mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-07 09:38:06 +00:00
feat: show font name & preview font in branding (#6026)
* feat: add fontname package and draft * feat: change back delete visibility * feat: replace fontname lib with opentype.js * feat: dynamic font preview also for font name --------- Co-authored-by: Max Peintner <max@caos.ch>
This commit is contained in:
parent
244f16ac48
commit
37cf9f5fb2
33
console/package-lock.json
generated
33
console/package-lock.json
generated
@ -37,6 +37,7 @@
|
|||||||
"libphonenumber-js": "^1.10.30",
|
"libphonenumber-js": "^1.10.30",
|
||||||
"material-design-icons-iconfont": "^6.1.1",
|
"material-design-icons-iconfont": "^6.1.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"opentype.js": "^1.3.4",
|
||||||
"ngx-color": "^9.0.0",
|
"ngx-color": "^9.0.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tinycolor2": "^1.6.0",
|
"tinycolor2": "^1.6.0",
|
||||||
@ -61,6 +62,7 @@
|
|||||||
"@types/jasminewd2": "~2.0.10",
|
"@types/jasminewd2": "~2.0.10",
|
||||||
"@types/jsonwebtoken": "^9.0.1",
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
"@types/node": "^18.15.11",
|
"@types/node": "^18.15.11",
|
||||||
|
"@types/opentype.js": "^1.3.4",
|
||||||
"@types/qrcode": "^1.5.0",
|
"@types/qrcode": "^1.5.0",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
||||||
@ -4458,6 +4460,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.9.tgz",
|
||||||
"integrity": "sha512-IeB32oIV4oGArLrd7znD2rkHQ6EDCM+2Sr76dJnrHwv9OHBTTM6nuDLK9bmikXzPa0ZlWMWtRGo/Uw4mrzQedA=="
|
"integrity": "sha512-IeB32oIV4oGArLrd7znD2rkHQ6EDCM+2Sr76dJnrHwv9OHBTTM6nuDLK9bmikXzPa0ZlWMWtRGo/Uw4mrzQedA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/opentype.js": {
|
||||||
|
"version": "1.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/opentype.js/-/opentype.js-1.3.4.tgz",
|
||||||
|
"integrity": "sha512-6fbXi67I07ugNM+FExwJnfuui2hD7hraD6nqjr3UnqsbBpxSkrtmO6tBubPdNAjqRT9TVkquVkNS9IkgTtq6/w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/q": {
|
"node_modules/@types/q": {
|
||||||
"version": "0.0.32",
|
"version": "0.0.32",
|
||||||
"resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
|
"resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
|
||||||
@ -12578,6 +12586,21 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/opentype.js": {
|
||||||
|
"version": "1.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz",
|
||||||
|
"integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==",
|
||||||
|
"dependencies": {
|
||||||
|
"string.prototype.codepointat": "^0.2.1",
|
||||||
|
"tiny-inflate": "^1.0.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"ot": "bin/ot"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.1",
|
"version": "0.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||||
@ -15205,6 +15228,11 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string.prototype.codepointat": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
|
||||||
|
},
|
||||||
"node_modules/strip-ansi": {
|
"node_modules/strip-ansi": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
@ -15546,6 +15574,11 @@
|
|||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
|
||||||
|
},
|
||||||
"node_modules/tinycolor2": {
|
"node_modules/tinycolor2": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"libphonenumber-js": "^1.10.30",
|
"libphonenumber-js": "^1.10.30",
|
||||||
"material-design-icons-iconfont": "^6.1.1",
|
"material-design-icons-iconfont": "^6.1.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"opentype.js": "^1.3.4",
|
||||||
"ngx-color": "^9.0.0",
|
"ngx-color": "^9.0.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tinycolor2": "^1.6.0",
|
"tinycolor2": "^1.6.0",
|
||||||
@ -65,6 +66,7 @@
|
|||||||
"@types/jasminewd2": "~2.0.10",
|
"@types/jasminewd2": "~2.0.10",
|
||||||
"@types/jsonwebtoken": "^9.0.1",
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
"@types/node": "^18.15.11",
|
"@types/node": "^18.15.11",
|
||||||
|
"@types/opentype.js": "^1.3.4",
|
||||||
"@types/qrcode": "^1.5.0",
|
"@types/qrcode": "^1.5.0",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
||||||
|
@ -492,7 +492,9 @@
|
|||||||
</cnsl-info-section>
|
</cnsl-info-section>
|
||||||
<div class="font-preview" *ngIf="previewData.fontUrl; else addFontButton">
|
<div class="font-preview" *ngIf="previewData.fontUrl; else addFontButton">
|
||||||
<mat-icon class="icon">text_fields</mat-icon>
|
<mat-icon class="icon">text_fields</mat-icon>
|
||||||
|
<span class="font-name" [ngStyle]="fontName ? { 'font-family': 'brandingFont' } : { '': '' }">{{
|
||||||
|
fontName
|
||||||
|
}}</span>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -634,6 +636,7 @@
|
|||||||
class="preview"
|
class="preview"
|
||||||
[ngClass]="{ darkmode: theme === Theme.DARK, lightmode: theme === Theme.LIGHT }"
|
[ngClass]="{ darkmode: theme === Theme.DARK, lightmode: theme === Theme.LIGHT }"
|
||||||
[policy]="view === View.PREVIEW ? previewData : data"
|
[policy]="view === View.PREVIEW ? previewData : data"
|
||||||
|
[ngStyle]="fontName ? { 'font-family': 'brandingFont' } : { '': '' }"
|
||||||
>
|
>
|
||||||
</cnsl-preview>
|
</cnsl-preview>
|
||||||
</div>
|
</div>
|
||||||
|
@ -243,8 +243,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.font-preview {
|
.font-preview {
|
||||||
height: 70px;
|
|
||||||
width: 70px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -252,22 +250,17 @@
|
|||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
border: 1px solid map-get($foreground, divider);
|
border: 1px solid map-get($foreground, divider);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
padding: 1rem 0.5rem 1rem 0.75rem;
|
||||||
|
|
||||||
.icon {
|
.font-name {
|
||||||
position: absolute;
|
margin-left: 0.5rem;
|
||||||
top: 50%;
|
font-size: 16px;
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%) translateY(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dl-btn {
|
.dl-btn {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transform: translateX(50%) translateY(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -31,6 +31,7 @@ import {
|
|||||||
} from 'src/app/services/theme.service';
|
} from 'src/app/services/theme.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import * as opentype from 'opentype.js';
|
||||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||||
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
||||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||||
@ -88,6 +89,8 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
|||||||
public ColorType: any = ColorType;
|
public ColorType: any = ColorType;
|
||||||
public AssetType: any = AssetType;
|
public AssetType: any = AssetType;
|
||||||
|
|
||||||
|
public fontName = '';
|
||||||
|
|
||||||
public refreshPreview: EventEmitter<void> = new EventEmitter();
|
public refreshPreview: EventEmitter<void> = new EventEmitter();
|
||||||
public org!: Org.AsObject;
|
public org!: Org.AsObject;
|
||||||
public InfoSectionType: any = InfoSectionType;
|
public InfoSectionType: any = InfoSectionType;
|
||||||
@ -180,6 +183,10 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
|||||||
if (file) {
|
if (file) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
|
this.getFontName(file);
|
||||||
|
this.previewNewFont(file);
|
||||||
|
|
||||||
switch (this.serviceType) {
|
switch (this.serviceType) {
|
||||||
case PolicyComponentServiceType.MGMT:
|
case PolicyComponentServiceType.MGMT:
|
||||||
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.MGMTFONT, formData, this.org.id));
|
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.MGMTFONT, formData, this.org.id));
|
||||||
@ -199,6 +206,7 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
|||||||
this.getPreviewData().then((data) => {
|
this.getPreviewData().then((data) => {
|
||||||
if (data.policy) {
|
if (data.policy) {
|
||||||
this.previewData = data.policy;
|
this.previewData = data.policy;
|
||||||
|
this.fontName = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@ -374,6 +382,11 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
|||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.policy) {
|
if (data.policy) {
|
||||||
this.previewData = data.policy;
|
this.previewData = data.policy;
|
||||||
|
if (this.previewData?.fontUrl) {
|
||||||
|
this.fetchFontMetadataAndPreview(this.previewData.fontUrl);
|
||||||
|
} else {
|
||||||
|
this.fontName = 'Could not parse font name';
|
||||||
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -385,6 +398,11 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
|||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.policy) {
|
if (data.policy) {
|
||||||
this.data = data.policy;
|
this.data = data.policy;
|
||||||
|
if (this.data?.fontUrl) {
|
||||||
|
this.fetchFontMetadataAndPreview(this.data?.fontUrl);
|
||||||
|
} else {
|
||||||
|
this.fontName = 'Could not parse font name';
|
||||||
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -678,6 +696,45 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fetchFontMetadataAndPreview(url: string): void {
|
||||||
|
fetch(url)
|
||||||
|
.then((res) => res.blob())
|
||||||
|
.then((blob) => {
|
||||||
|
this.getFontName(blob);
|
||||||
|
this.previewNewFont(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFontName(blob: Blob): void {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
if (e.target && e.target.result) {
|
||||||
|
try {
|
||||||
|
const font = opentype.parse(e.target.result);
|
||||||
|
this.fontName = font.names.fullName['en'];
|
||||||
|
} catch (e) {
|
||||||
|
this.fontName = 'Could not parse font name';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
private previewNewFont(blob: Blob): void {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = (e) => {
|
||||||
|
if (e.target) {
|
||||||
|
let customFont = new FontFace('brandingFont', `url(${e.target.result})`);
|
||||||
|
// typescript complains that add is not found but
|
||||||
|
// indeed it is https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/add
|
||||||
|
// @ts-ignore
|
||||||
|
document.fonts.add(customFont);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * defaults to false because urls are distinct anyway
|
// * defaults to false because urls are distinct anyway
|
||||||
// */
|
// */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user