@brickclay-org/ui 0.0.49 → 0.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1911 -1911
- package/fesm2022/brickclay-org-ui.mjs +227 -2
- package/fesm2022/brickclay-org-ui.mjs.map +1 -1
- package/index.d.ts +69 -2
- package/package.json +1 -1
- package/src/lib/avatar-profile/avatar-profile.css +80 -0
- package/src/styles.css +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Component, EventEmitter, HostListener, ViewChildren, Output, Input, Injectable, NgModule, forwardRef, ViewEncapsulation, Optional, Self, Directive, ViewChild, input, model, output, signal, computed, effect, inject, ElementRef, InjectionToken } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
|
-
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { CommonModule, NgClass } from '@angular/common';
|
|
5
5
|
import * as i1$1 from '@angular/forms';
|
|
6
6
|
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
7
7
|
import moment from 'moment';
|
|
@@ -5543,6 +5543,231 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
5543
5543
|
type: Input
|
|
5544
5544
|
}] } });
|
|
5545
5545
|
|
|
5546
|
+
class AvatarProfile {
|
|
5547
|
+
// ---------- Display inputs ----------
|
|
5548
|
+
src = null;
|
|
5549
|
+
alt = 'Avatar';
|
|
5550
|
+
name = '';
|
|
5551
|
+
size = 'md';
|
|
5552
|
+
fallback = 'auto';
|
|
5553
|
+
// ---------- Functional inputs ----------
|
|
5554
|
+
/** Whether upload / remove actions are enabled */
|
|
5555
|
+
editable = true;
|
|
5556
|
+
/** Accepted file MIME types for the file picker */
|
|
5557
|
+
accept = 'image/jpeg, image/png';
|
|
5558
|
+
/** Max file size in KB (0 = no limit) */
|
|
5559
|
+
maxFileSizeKB = 0;
|
|
5560
|
+
/** Label shown on the upload button */
|
|
5561
|
+
uploadLabel = 'Upload';
|
|
5562
|
+
/** Hint text shown below the upload button */
|
|
5563
|
+
hint = '500x500 JPEG, PNG Image';
|
|
5564
|
+
/** External loading state (e.g. while uploading) */
|
|
5565
|
+
loading = false;
|
|
5566
|
+
/** Whether the remove badge is shown when an image is present */
|
|
5567
|
+
removable = false;
|
|
5568
|
+
// ---------- Form / CVA inputs ----------
|
|
5569
|
+
/** Label displayed above the avatar */
|
|
5570
|
+
label = '';
|
|
5571
|
+
/** Whether the field is required (shows asterisk) */
|
|
5572
|
+
required = false;
|
|
5573
|
+
/** External error state flag */
|
|
5574
|
+
hasError = false;
|
|
5575
|
+
/** Error message to display when hasError is true */
|
|
5576
|
+
errorMessage = '';
|
|
5577
|
+
/** Disabled state (also set via CVA setDisabledState) */
|
|
5578
|
+
disabled = false;
|
|
5579
|
+
// ---------- Outputs ----------
|
|
5580
|
+
/** Emits the selected File after validation passes */
|
|
5581
|
+
fileSelected = new EventEmitter();
|
|
5582
|
+
/** Emits when the user clicks the remove badge */
|
|
5583
|
+
removed = new EventEmitter();
|
|
5584
|
+
/** Emits a human-readable error string on validation failure */
|
|
5585
|
+
fileError = new EventEmitter();
|
|
5586
|
+
initials = '';
|
|
5587
|
+
imageLoadFailed = false;
|
|
5588
|
+
/** Local blob URL for instant preview before server upload completes */
|
|
5589
|
+
previewUrl = null;
|
|
5590
|
+
// ---------- CVA Callbacks ----------
|
|
5591
|
+
onChange = (_value) => { };
|
|
5592
|
+
onTouched = () => { };
|
|
5593
|
+
writeValue(value) {
|
|
5594
|
+
this.src = value || null;
|
|
5595
|
+
// When a new value arrives from the form (e.g. server URL after upload),
|
|
5596
|
+
// discard any stale local blob preview so displaySrc uses the real URL
|
|
5597
|
+
this.revokePreview();
|
|
5598
|
+
this.imageLoadFailed = false;
|
|
5599
|
+
}
|
|
5600
|
+
registerOnChange(fn) {
|
|
5601
|
+
this.onChange = fn;
|
|
5602
|
+
}
|
|
5603
|
+
registerOnTouched(fn) {
|
|
5604
|
+
this.onTouched = fn;
|
|
5605
|
+
}
|
|
5606
|
+
setDisabledState(isDisabled) {
|
|
5607
|
+
this.disabled = isDisabled;
|
|
5608
|
+
this.editable = !isDisabled;
|
|
5609
|
+
}
|
|
5610
|
+
// ---------- Lifecycle ----------
|
|
5611
|
+
ngOnChanges(changes) {
|
|
5612
|
+
if (this.name) {
|
|
5613
|
+
this.initials = this.getInitials(this.name);
|
|
5614
|
+
}
|
|
5615
|
+
this.imageLoadFailed = false;
|
|
5616
|
+
// When the parent updates [src] via @Input (non-CVA usage), discard the local preview
|
|
5617
|
+
if (changes['src'] && this.previewUrl) {
|
|
5618
|
+
this.revokePreview();
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
ngOnDestroy() {
|
|
5622
|
+
this.revokePreview();
|
|
5623
|
+
}
|
|
5624
|
+
onImageError() {
|
|
5625
|
+
this.imageLoadFailed = true;
|
|
5626
|
+
}
|
|
5627
|
+
// ---------- Derived UI ----------
|
|
5628
|
+
/** The URL the template should display — local preview takes priority over the server src */
|
|
5629
|
+
get displaySrc() {
|
|
5630
|
+
return this.previewUrl ?? this.src;
|
|
5631
|
+
}
|
|
5632
|
+
get showInitials() {
|
|
5633
|
+
if (!this.name)
|
|
5634
|
+
return false;
|
|
5635
|
+
if (this.fallback === 'icon')
|
|
5636
|
+
return false;
|
|
5637
|
+
if (this.fallback === 'camera')
|
|
5638
|
+
return false;
|
|
5639
|
+
if (this.fallback === 'initials')
|
|
5640
|
+
return true;
|
|
5641
|
+
return true; // auto
|
|
5642
|
+
}
|
|
5643
|
+
get containerClasses() {
|
|
5644
|
+
return ['avatar-profile', this.size].join(' ');
|
|
5645
|
+
}
|
|
5646
|
+
get sizeClasses() {
|
|
5647
|
+
return [this.size];
|
|
5648
|
+
}
|
|
5649
|
+
get showRemoveButton() {
|
|
5650
|
+
return this.removable && this.editable && !!(this.displaySrc) && !this.imageLoadFailed;
|
|
5651
|
+
}
|
|
5652
|
+
// ---------- File handling ----------
|
|
5653
|
+
onFileSelected(event) {
|
|
5654
|
+
const input = event.target;
|
|
5655
|
+
if (!input.files?.length)
|
|
5656
|
+
return;
|
|
5657
|
+
const file = input.files[0];
|
|
5658
|
+
// Validate file type
|
|
5659
|
+
if (this.accept && !this.isFileTypeValid(file)) {
|
|
5660
|
+
this.fileError.emit(`Invalid file type. Accepted: ${this.accept}`);
|
|
5661
|
+
input.value = '';
|
|
5662
|
+
return;
|
|
5663
|
+
}
|
|
5664
|
+
// Validate file size
|
|
5665
|
+
if (this.maxFileSizeKB > 0 && file.size > this.maxFileSizeKB * 1024) {
|
|
5666
|
+
this.fileError.emit(`File size exceeds ${this.maxFileSizeKB}KB limit.`);
|
|
5667
|
+
input.value = '';
|
|
5668
|
+
return;
|
|
5669
|
+
}
|
|
5670
|
+
// Instant local preview — no server round-trip needed to show the image
|
|
5671
|
+
this.revokePreview();
|
|
5672
|
+
this.previewUrl = URL.createObjectURL(file);
|
|
5673
|
+
this.imageLoadFailed = false;
|
|
5674
|
+
// Push the preview URL as the form value so validation (e.g. required) passes
|
|
5675
|
+
this.onChange(this.previewUrl);
|
|
5676
|
+
this.onTouched();
|
|
5677
|
+
this.fileSelected.emit(event);
|
|
5678
|
+
input.value = ''; // reset so re-selecting the same file still triggers change
|
|
5679
|
+
}
|
|
5680
|
+
onRemove() {
|
|
5681
|
+
this.revokePreview();
|
|
5682
|
+
this.src = null;
|
|
5683
|
+
this.onChange(null);
|
|
5684
|
+
this.onTouched();
|
|
5685
|
+
this.removed.emit();
|
|
5686
|
+
}
|
|
5687
|
+
// ---------- Utils ----------
|
|
5688
|
+
/** Revoke the previous object URL to free memory */
|
|
5689
|
+
revokePreview() {
|
|
5690
|
+
if (this.previewUrl) {
|
|
5691
|
+
URL.revokeObjectURL(this.previewUrl);
|
|
5692
|
+
this.previewUrl = null;
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
5695
|
+
isFileTypeValid(file) {
|
|
5696
|
+
const accepted = this.accept.split(',').map(t => t.trim());
|
|
5697
|
+
return accepted.some(type => {
|
|
5698
|
+
if (type.endsWith('/*')) {
|
|
5699
|
+
return file.type.startsWith(type.replace('/*', '/'));
|
|
5700
|
+
}
|
|
5701
|
+
return file.type === type;
|
|
5702
|
+
});
|
|
5703
|
+
}
|
|
5704
|
+
getInitials(name) {
|
|
5705
|
+
const parts = name.trim().split(/\s+/);
|
|
5706
|
+
if (parts.length === 1) {
|
|
5707
|
+
return parts[0].slice(0, 2).toUpperCase();
|
|
5708
|
+
}
|
|
5709
|
+
return (parts[0][0] + parts.at(-1)[0]).toUpperCase();
|
|
5710
|
+
}
|
|
5711
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AvatarProfile, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5712
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AvatarProfile, isStandalone: true, selector: "bk-avatar-profile", inputs: { src: "src", alt: "alt", name: "name", size: "size", fallback: "fallback", editable: "editable", accept: "accept", maxFileSizeKB: "maxFileSizeKB", uploadLabel: "uploadLabel", hint: "hint", loading: "loading", removable: "removable", label: "label", required: "required", hasError: "hasError", errorMessage: "errorMessage", disabled: "disabled" }, outputs: { fileSelected: "fileSelected", removed: "removed", fileError: "fileError" }, providers: [
|
|
5713
|
+
{
|
|
5714
|
+
provide: NG_VALUE_ACCESSOR,
|
|
5715
|
+
useExisting: forwardRef(() => AvatarProfile),
|
|
5716
|
+
multi: true
|
|
5717
|
+
}
|
|
5718
|
+
], usesOnChanges: true, ngImport: i0, template: "<div class=\"avatar-profile-container\" [ngClass]=\"sizeClasses\">\r\n @if (label) {\r\n <label class=\"avatar-profile-label\">\r\n {{ label }}\r\n @if (required) {\r\n <span class=\"avatar-profile-label-required\">*</span>\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"flex gap-4 justify-end\">\r\n <div\r\n [ngClass]=\"containerClasses\"\r\n [bkTooltip]=\"name\"\r\n [bkTooltipPosition]=\"'top'\"\r\n >\r\n\r\n <!-- Loading overlay -->\r\n @if (loading) {\r\n <div class=\"absolute inset-0 flex items-center justify-center z-10 rounded-full bg-white/60\">\r\n <div class=\"w-5 h-5 border-2 border-[#141414] border-t-transparent rounded-full animate-spin\"></div>\r\n </div>\r\n }\r\n\r\n @if (displaySrc && !imageLoadFailed) {\r\n <img\r\n [src]=\"displaySrc\"\r\n [alt]=\"alt\"\r\n class=\"avatar-img\"\r\n (error)=\"onImageError()\"\r\n />\r\n }\r\n\r\n @else if (fallback === 'camera') {\r\n <svg class=\"placeholder-icon\" width=\"23\" height=\"24\" viewBox=\"0 0 23 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M18.3843 4.66667C17.6726 4.66667 17.0193 4.25833 16.6926 3.62833L15.8526 1.93667C15.316 0.875 13.916 0 12.726 0H10.0543C8.85264 0 7.45264 0.875 6.91598 1.93667L6.07598 3.62833C5.74931 4.25833 5.09598 4.66667 4.38431 4.66667C1.85264 4.66667 -0.154023 6.80167 0.00931005 9.32167L0.615977 18.9583C0.755977 21.3617 2.05098 23.3333 5.27098 23.3333H17.4976C20.7176 23.3333 22.001 21.3617 22.1526 18.9583L22.7593 9.32167C22.9226 6.80167 20.916 4.66667 18.3843 4.66667ZM9.63431 6.125H13.1343C13.6126 6.125 14.0093 6.52167 14.0093 7C14.0093 7.47833 13.6126 7.875 13.1343 7.875H9.63431C9.15598 7.875 8.75931 7.47833 8.75931 7C8.75931 6.52167 9.15598 6.125 9.63431 6.125ZM11.3843 18.8067C9.21431 18.8067 7.44098 17.045 7.44098 14.8633C7.44098 12.6817 9.20264 10.92 11.3843 10.92C13.566 10.92 15.3276 12.6817 15.3276 14.8633C15.3276 17.045 13.5543 18.8067 11.3843 18.8067Z\" fill=\"#6B7080\"/>\r\n </svg>\r\n }\r\n\r\n @else if (showInitials) {\r\n <span class=\"avatar-text\">\r\n {{ initials }}\r\n </span>\r\n }\r\n\r\n @else {\r\n <svg class=\"placeholder-icon\" viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\" xmlns=\"http://www.w3.org/2000/svg\" > <g clip-path=\"url(#clip0)\"> <path d=\"M16 16c3.682 0 6.667-2.985 6.667-6.667S19.682 2.667 16 2.667 9.333 5.651 9.333 9.333 12.318 16 16 16Z\" stroke=\"#363C51\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> <path d=\"M27.453 29.333C27.453 24.173 22.32 20 16 20S4.547 24.173 4.547 29.333\" stroke=\"#363C51\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </g> <defs> <clipPath id=\"clip0\"> <rect width=\"32\" height=\"32\" fill=\"white\" /> </clipPath> </defs> </svg>\r\n }\r\n\r\n <!-- Remove badge -->\r\n @if (showRemoveButton) {\r\n <button\r\n type=\"button\"\r\n class=\"remove-badge\"\r\n (click)=\"onRemove()\"\r\n aria-label=\"Remove avatar\"\r\n >\r\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M1 1L9 9M9 1L1 9\" stroke=\"#6B7080\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\r\n </svg>\r\n </button>\r\n }\r\n\r\n </div>\r\n <div class=\"flex flex-col gap-2 justify-end items-start\">\r\n @if (editable) {\r\n <label class=\"upload-button cursor-pointer\">\r\n {{ uploadLabel }}\r\n <input\r\n type=\"file\"\r\n class=\"hidden\"\r\n [accept]=\"accept\"\r\n [disabled]=\"disabled\"\r\n (change)=\"onFileSelected($event)\"\r\n />\r\n </label>\r\n }\r\n @if (!hasError && hint) {\r\n <p class=\"profile-size\">{{ hint }}</p>\r\n }\r\n @if (hasError && errorMessage) {\r\n <p class=\"avatar-profile-error\">{{ errorMessage }}</p>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".avatar-profile-container{@apply flex flex-col gap-1.5;}.avatar-profile-label{@apply text-sm font-medium text-[#141414];}.avatar-profile-label-required{@apply text-[#E7000B] ml-0.5;}.avatar-profile-error{@apply text-xs text-[#E7000B] font-normal;}.avatar-profile{@apply relative inline-flex items-center justify-center rounded-full flex-shrink-0 select-none shadow-lg transition-all duration-200 border-[3px] border-white bg-[#F9FAFA];}.avatar-profile .avatar-img{@apply w-full h-full object-cover rounded-full;}.upload-button{@apply font-medium text-[#6B7080] border border-[#E3E3E7] rounded shadow-sm;}.sm .upload-button{@apply text-[12px] leading-[18px] px-2 py-3;}.md .upload-button{@apply text-[14px] leading-[20px] px-[10px] py-2.5;}.lg .upload-button{@apply text-[16px] leading-[24px] px-[18px] py-2.5;}.xl .upload-button{@apply text-[16px] leading-[24px] px-5 py-3;}.profile-size{@apply font-medium text-[#6B7080];}.sm .profile-size{@apply text-[10px] leading-[14px];}.md .profile-size{@apply text-[12px] leading-[18px];}.lg .profile-size{@apply text-[14px] leading-[20px];}.xl .profile-size{@apply text-[16px] leading-[24px];}.avatar-profile.sm .avatar-text{@apply font-medium text-[30px] leading-[38px];}.avatar-profile.md .avatar-text{@apply font-medium text-[36px] leading-[44px];}.avatar-profile.lg .avatar-text{@apply font-medium text-[45px] leading-[55px];}.avatar-profile.xl .avatar-text{@apply font-medium text-[60px] leading-[72px];}.avatar-profile{@apply font-medium text-[#6B7080];}.avatar-profile.sm{@apply size-[72px] text-sm;}.avatar-profile.md{@apply size-[96px] text-base;}.avatar-profile.lg{@apply size-[120px] text-[18px] leading-[26px];}.avatar-profile.xl{@apply size-[160px] text-xl;}.avatar-profile.sm .placeholder-icon{@apply size-[28px];}.avatar-profile.md .placeholder-icon{@apply size-[42px];}.avatar-profile.lg .placeholder-icon{@apply size-[52px];}.avatar-profile.xl .placeholder-icon{@apply size-[65px];}.remove-badge{@apply absolute top-0 right-0 w-5 h-5 bg-white rounded-full shadow-md flex items-center justify-center cursor-pointer hover:bg-gray-50 transition z-20 border-0 p-0;}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: BKTooltipDirective, selector: "[bkTooltip]", inputs: ["bkTooltip", "bkTooltipPosition"] }] });
|
|
5719
|
+
}
|
|
5720
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AvatarProfile, decorators: [{
|
|
5721
|
+
type: Component,
|
|
5722
|
+
args: [{ selector: 'bk-avatar-profile', imports: [NgClass, CommonModule, BKTooltipDirective], providers: [
|
|
5723
|
+
{
|
|
5724
|
+
provide: NG_VALUE_ACCESSOR,
|
|
5725
|
+
useExisting: forwardRef(() => AvatarProfile),
|
|
5726
|
+
multi: true
|
|
5727
|
+
}
|
|
5728
|
+
], template: "<div class=\"avatar-profile-container\" [ngClass]=\"sizeClasses\">\r\n @if (label) {\r\n <label class=\"avatar-profile-label\">\r\n {{ label }}\r\n @if (required) {\r\n <span class=\"avatar-profile-label-required\">*</span>\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"flex gap-4 justify-end\">\r\n <div\r\n [ngClass]=\"containerClasses\"\r\n [bkTooltip]=\"name\"\r\n [bkTooltipPosition]=\"'top'\"\r\n >\r\n\r\n <!-- Loading overlay -->\r\n @if (loading) {\r\n <div class=\"absolute inset-0 flex items-center justify-center z-10 rounded-full bg-white/60\">\r\n <div class=\"w-5 h-5 border-2 border-[#141414] border-t-transparent rounded-full animate-spin\"></div>\r\n </div>\r\n }\r\n\r\n @if (displaySrc && !imageLoadFailed) {\r\n <img\r\n [src]=\"displaySrc\"\r\n [alt]=\"alt\"\r\n class=\"avatar-img\"\r\n (error)=\"onImageError()\"\r\n />\r\n }\r\n\r\n @else if (fallback === 'camera') {\r\n <svg class=\"placeholder-icon\" width=\"23\" height=\"24\" viewBox=\"0 0 23 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M18.3843 4.66667C17.6726 4.66667 17.0193 4.25833 16.6926 3.62833L15.8526 1.93667C15.316 0.875 13.916 0 12.726 0H10.0543C8.85264 0 7.45264 0.875 6.91598 1.93667L6.07598 3.62833C5.74931 4.25833 5.09598 4.66667 4.38431 4.66667C1.85264 4.66667 -0.154023 6.80167 0.00931005 9.32167L0.615977 18.9583C0.755977 21.3617 2.05098 23.3333 5.27098 23.3333H17.4976C20.7176 23.3333 22.001 21.3617 22.1526 18.9583L22.7593 9.32167C22.9226 6.80167 20.916 4.66667 18.3843 4.66667ZM9.63431 6.125H13.1343C13.6126 6.125 14.0093 6.52167 14.0093 7C14.0093 7.47833 13.6126 7.875 13.1343 7.875H9.63431C9.15598 7.875 8.75931 7.47833 8.75931 7C8.75931 6.52167 9.15598 6.125 9.63431 6.125ZM11.3843 18.8067C9.21431 18.8067 7.44098 17.045 7.44098 14.8633C7.44098 12.6817 9.20264 10.92 11.3843 10.92C13.566 10.92 15.3276 12.6817 15.3276 14.8633C15.3276 17.045 13.5543 18.8067 11.3843 18.8067Z\" fill=\"#6B7080\"/>\r\n </svg>\r\n }\r\n\r\n @else if (showInitials) {\r\n <span class=\"avatar-text\">\r\n {{ initials }}\r\n </span>\r\n }\r\n\r\n @else {\r\n <svg class=\"placeholder-icon\" viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\" xmlns=\"http://www.w3.org/2000/svg\" > <g clip-path=\"url(#clip0)\"> <path d=\"M16 16c3.682 0 6.667-2.985 6.667-6.667S19.682 2.667 16 2.667 9.333 5.651 9.333 9.333 12.318 16 16 16Z\" stroke=\"#363C51\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> <path d=\"M27.453 29.333C27.453 24.173 22.32 20 16 20S4.547 24.173 4.547 29.333\" stroke=\"#363C51\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /> </g> <defs> <clipPath id=\"clip0\"> <rect width=\"32\" height=\"32\" fill=\"white\" /> </clipPath> </defs> </svg>\r\n }\r\n\r\n <!-- Remove badge -->\r\n @if (showRemoveButton) {\r\n <button\r\n type=\"button\"\r\n class=\"remove-badge\"\r\n (click)=\"onRemove()\"\r\n aria-label=\"Remove avatar\"\r\n >\r\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M1 1L9 9M9 1L1 9\" stroke=\"#6B7080\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\r\n </svg>\r\n </button>\r\n }\r\n\r\n </div>\r\n <div class=\"flex flex-col gap-2 justify-end items-start\">\r\n @if (editable) {\r\n <label class=\"upload-button cursor-pointer\">\r\n {{ uploadLabel }}\r\n <input\r\n type=\"file\"\r\n class=\"hidden\"\r\n [accept]=\"accept\"\r\n [disabled]=\"disabled\"\r\n (change)=\"onFileSelected($event)\"\r\n />\r\n </label>\r\n }\r\n @if (!hasError && hint) {\r\n <p class=\"profile-size\">{{ hint }}</p>\r\n }\r\n @if (hasError && errorMessage) {\r\n <p class=\"avatar-profile-error\">{{ errorMessage }}</p>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".avatar-profile-container{@apply flex flex-col gap-1.5;}.avatar-profile-label{@apply text-sm font-medium text-[#141414];}.avatar-profile-label-required{@apply text-[#E7000B] ml-0.5;}.avatar-profile-error{@apply text-xs text-[#E7000B] font-normal;}.avatar-profile{@apply relative inline-flex items-center justify-center rounded-full flex-shrink-0 select-none shadow-lg transition-all duration-200 border-[3px] border-white bg-[#F9FAFA];}.avatar-profile .avatar-img{@apply w-full h-full object-cover rounded-full;}.upload-button{@apply font-medium text-[#6B7080] border border-[#E3E3E7] rounded shadow-sm;}.sm .upload-button{@apply text-[12px] leading-[18px] px-2 py-3;}.md .upload-button{@apply text-[14px] leading-[20px] px-[10px] py-2.5;}.lg .upload-button{@apply text-[16px] leading-[24px] px-[18px] py-2.5;}.xl .upload-button{@apply text-[16px] leading-[24px] px-5 py-3;}.profile-size{@apply font-medium text-[#6B7080];}.sm .profile-size{@apply text-[10px] leading-[14px];}.md .profile-size{@apply text-[12px] leading-[18px];}.lg .profile-size{@apply text-[14px] leading-[20px];}.xl .profile-size{@apply text-[16px] leading-[24px];}.avatar-profile.sm .avatar-text{@apply font-medium text-[30px] leading-[38px];}.avatar-profile.md .avatar-text{@apply font-medium text-[36px] leading-[44px];}.avatar-profile.lg .avatar-text{@apply font-medium text-[45px] leading-[55px];}.avatar-profile.xl .avatar-text{@apply font-medium text-[60px] leading-[72px];}.avatar-profile{@apply font-medium text-[#6B7080];}.avatar-profile.sm{@apply size-[72px] text-sm;}.avatar-profile.md{@apply size-[96px] text-base;}.avatar-profile.lg{@apply size-[120px] text-[18px] leading-[26px];}.avatar-profile.xl{@apply size-[160px] text-xl;}.avatar-profile.sm .placeholder-icon{@apply size-[28px];}.avatar-profile.md .placeholder-icon{@apply size-[42px];}.avatar-profile.lg .placeholder-icon{@apply size-[52px];}.avatar-profile.xl .placeholder-icon{@apply size-[65px];}.remove-badge{@apply absolute top-0 right-0 w-5 h-5 bg-white rounded-full shadow-md flex items-center justify-center cursor-pointer hover:bg-gray-50 transition z-20 border-0 p-0;}\n"] }]
|
|
5729
|
+
}], propDecorators: { src: [{
|
|
5730
|
+
type: Input
|
|
5731
|
+
}], alt: [{
|
|
5732
|
+
type: Input
|
|
5733
|
+
}], name: [{
|
|
5734
|
+
type: Input
|
|
5735
|
+
}], size: [{
|
|
5736
|
+
type: Input
|
|
5737
|
+
}], fallback: [{
|
|
5738
|
+
type: Input
|
|
5739
|
+
}], editable: [{
|
|
5740
|
+
type: Input
|
|
5741
|
+
}], accept: [{
|
|
5742
|
+
type: Input
|
|
5743
|
+
}], maxFileSizeKB: [{
|
|
5744
|
+
type: Input
|
|
5745
|
+
}], uploadLabel: [{
|
|
5746
|
+
type: Input
|
|
5747
|
+
}], hint: [{
|
|
5748
|
+
type: Input
|
|
5749
|
+
}], loading: [{
|
|
5750
|
+
type: Input
|
|
5751
|
+
}], removable: [{
|
|
5752
|
+
type: Input
|
|
5753
|
+
}], label: [{
|
|
5754
|
+
type: Input
|
|
5755
|
+
}], required: [{
|
|
5756
|
+
type: Input
|
|
5757
|
+
}], hasError: [{
|
|
5758
|
+
type: Input
|
|
5759
|
+
}], errorMessage: [{
|
|
5760
|
+
type: Input
|
|
5761
|
+
}], disabled: [{
|
|
5762
|
+
type: Input
|
|
5763
|
+
}], fileSelected: [{
|
|
5764
|
+
type: Output
|
|
5765
|
+
}], removed: [{
|
|
5766
|
+
type: Output
|
|
5767
|
+
}], fileError: [{
|
|
5768
|
+
type: Output
|
|
5769
|
+
}] } });
|
|
5770
|
+
|
|
5546
5771
|
/*
|
|
5547
5772
|
* Public API Surface of brickclay-lib
|
|
5548
5773
|
*/
|
|
@@ -5552,5 +5777,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
5552
5777
|
* Generated bundle index. Do not edit.
|
|
5553
5778
|
*/
|
|
5554
5779
|
|
|
5555
|
-
export { BKTooltipDirective, BkBadge, BkButton, BkButtonGroup, BkCheckbox, BkCustomCalendar, BkDialogActions, BkDialogClose, BkDialogContent, BkDialogModule, BkDialogTitle, BkGrid, BkIconButton, BkInput, BkInputChips, BkPill, BkRadioButton, BkScheduledDatePicker, BkSelect, BkSpinner, BkTabs, BkTextarea, BkTimePicker, BkToggle, BkValidator, BrickclayIcons, BrickclayLib, CalendarManagerService, CalendarModule, DEFAULT_DIALOG_CONFIG, DIALOG_DATA, DIALOG_GLOBAL_CONFIG, DialogRef, DialogService, getDialogBackdropAnimation, getDialogPanelAnimation };
|
|
5780
|
+
export { AvatarProfile, BKTooltipDirective, BkBadge, BkButton, BkButtonGroup, BkCheckbox, BkCustomCalendar, BkDialogActions, BkDialogClose, BkDialogContent, BkDialogModule, BkDialogTitle, BkGrid, BkIconButton, BkInput, BkInputChips, BkPill, BkRadioButton, BkScheduledDatePicker, BkSelect, BkSpinner, BkTabs, BkTextarea, BkTimePicker, BkToggle, BkValidator, BrickclayIcons, BrickclayLib, CalendarManagerService, CalendarModule, DEFAULT_DIALOG_CONFIG, DIALOG_DATA, DIALOG_GLOBAL_CONFIG, DialogRef, DialogService, getDialogBackdropAnimation, getDialogPanelAnimation };
|
|
5556
5781
|
//# sourceMappingURL=brickclay-org-ui.mjs.map
|