@frame-kit/ui-ng 0.2.23 → 0.2.25
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/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs +7 -0
- package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs.map +1 -1
- package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs +9 -1
- package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs.map +1 -1
- package/package.json +2 -2
- package/ui/avatar/README.md +1 -1
|
@@ -70,6 +70,13 @@ class FkContentSplitComponent {
|
|
|
70
70
|
}
|
|
71
71
|
const hostWidth = entry.contentRect.width;
|
|
72
72
|
const bp = this.breakpoint();
|
|
73
|
+
// A width of 0 means the split is hidden (an inactive tab, a
|
|
74
|
+
// collapsed section), not that the screen is narrow. Keep the
|
|
75
|
+
// last real layout, so showing it again does not fold the left
|
|
76
|
+
// panel away and animate it back.
|
|
77
|
+
if (hostWidth === 0) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
73
80
|
if (hostWidth < bp && this.layout() !== 'narrow') {
|
|
74
81
|
this.ngZone.run(() => {
|
|
75
82
|
this.layout.set('narrow');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-kit-ui-ng-layouts-content-split.mjs","sources":["../../../../packages/ui-ng/layouts/content-split/content-split.component.ts","../../../../packages/ui-ng/layouts/content-split/content-split.component.html","../../../../packages/ui-ng/layouts/content-split/frame-kit-ui-ng-layouts-content-split.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n ElementRef,\n HostBinding,\n inject,\n input,\n model,\n NgZone,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\n\nimport type { ContentSplitLayout } from './content-split.types';\n\n@Component({\n selector: 'fk-content-split',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './content-split.component.html',\n styleUrl: './content-split.component.scss',\n})\nexport class FkContentSplitComponent {\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly ngZone = inject(NgZone);\n private readonly destroyRef = inject(DestroyRef);\n private readonly platformId = inject(PLATFORM_ID);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n\n // ===== INPUTS =====\n /** Width of the left panel when the layout is in wide mode. */\n readonly leftWidth = input<string>('280px');\n /** Width of the left panel when collapsed to a narrow icon rail. */\n readonly railWidth = input<string>('64px');\n /** Host element pixel width below which the layout switches to narrow mode. */\n readonly breakpoint = input<number>(700);\n /**\n * Optional CSS max-width for the layout. When set, the split is capped at\n * this width and centered in its container (via auto inline margins);\n * unset (default), it fills the full available width as before.\n */\n readonly maxWidth = input<string | null>(null);\n /** Two-way binding for the collapsed state of the left panel. */\n readonly collapsed = model<boolean>(false);\n\n // ===== INTERNAL STATE =====\n readonly layout = signal<ContentSplitLayout>('wide');\n readonly animate = signal(false);\n\n private observer: ResizeObserver | null = null;\n\n // ===== COMPUTED =====\n readonly isNarrow = computed(() => this.layout() === 'narrow');\n readonly isWide = computed(() => this.layout() === 'wide');\n readonly showOverlay = computed(() => this.isNarrow() && !this.collapsed());\n\n readonly classes = computed(() =>\n [\n 'fk-content-split',\n this.isNarrow() ? 'fk-content-split--narrow' : 'fk-content-split--wide',\n this.isNarrow() && !this.collapsed() ? 'fk-content-split--open' : '',\n this.isNarrow() && this.collapsed() ? 'fk-content-split--closed' : '',\n this.animate() ? 'fk-content-split--animate' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' '),\n );\n\n // ===== HOST BINDINGS =====\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n @HostBinding('style.--fk-content-split-left-width')\n get hostLeftWidth() {\n return this.leftWidth();\n }\n\n @HostBinding('style.--fk-content-split-rail-width')\n get hostRailWidth() {\n return this.railWidth();\n }\n\n @HostBinding('style.--fk-content-split-max-width')\n get hostMaxWidth() {\n return this.maxWidth();\n }\n\n // ===== CONSTRUCTOR =====\n\n constructor() {\n // ResizeObserver is browser-only — the SSR pass has no live\n // layout to observe, so the initial layout signal stays at its\n // default (\"wide\") and the browser pass wires the observer once\n // hydration mounts the element.\n if (isPlatformBrowser(this.platformId)) {\n this.ngZone.runOutsideAngular(() => {\n this.observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n\n if (!entry) {\n return;\n }\n\n const hostWidth = entry.contentRect.width;\n const bp = this.breakpoint();\n\n if (hostWidth < bp && this.layout() !== 'narrow') {\n this.ngZone.run(() => {\n this.layout.set('narrow');\n this.animate.set(false);\n this.collapsed.set(true);\n });\n } else if (hostWidth >= bp && this.layout() !== 'wide') {\n this.ngZone.run(() => {\n this.layout.set('wide');\n this.animate.set(false);\n this.collapsed.set(true);\n });\n }\n });\n\n this.observer.observe(this.el.nativeElement);\n });\n }\n\n this.destroyRef.onDestroy(() => {\n this.observer?.disconnect();\n });\n }\n\n // ===== PUBLIC METHODS =====\n\n /** Expand the left panel. */\n open(): void {\n this.animate.set(true);\n this.collapsed.set(false);\n }\n\n /** Collapse the left panel. */\n close(): void {\n this.animate.set(true);\n this.collapsed.set(true);\n }\n\n /** Toggle the left panel between expanded and collapsed. */\n toggle(): void {\n if (this.collapsed()) {\n this.open();\n } else {\n this.close();\n }\n }\n\n /** Dismiss the left panel overlay on narrow viewports (alias for `close`). */\n dismiss(): void {\n this.close();\n }\n}\n","@if (showOverlay()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-content-split__overlay\" (click)=\"dismiss()\"></div>\n}\n\n<div\n class=\"fk-content-split__left\"\n [class.fk-content-split__left--open]=\"!collapsed()\"\n>\n <div class=\"fk-content-split__left-content\">\n <ng-content select=\"[contentSplitLeft]\" />\n </div>\n</div>\n\n<div class=\"fk-content-split__rail\">\n <button\n class=\"fk-content-split__rail-toggle\"\n type=\"button\"\n [attr.aria-label]=\"collapsed() ? 'Open panel' : 'Close panel'\"\n (click)=\"toggle()\"\n >\n <span\n class=\"fk-content-split__rail-toggle-icon\"\n [class.fk-content-split__rail-toggle-icon--open]=\"!collapsed()\"\n aria-hidden=\"true\"\n >\n <span class=\"fk-content-split__rail-toggle-projected\">\n <ng-content select=\"[contentSplitToggleIcon]\" />\n </span>\n <svg\n class=\"fk-content-split__rail-toggle-fallback\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M15.25 9H2.75\" />\n <path d=\"M11 4.75L15.25 9L11 13.25\" />\n </svg>\n </span>\n </button>\n</div>\n\n<div class=\"fk-content-split__right\">\n <ng-content select=\"[contentSplitRight]\" />\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"frame-kit-ui-ng-layouts-content-split.mjs","sources":["../../../../packages/ui-ng/layouts/content-split/content-split.component.ts","../../../../packages/ui-ng/layouts/content-split/content-split.component.html","../../../../packages/ui-ng/layouts/content-split/frame-kit-ui-ng-layouts-content-split.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n ElementRef,\n HostBinding,\n inject,\n input,\n model,\n NgZone,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\n\nimport type { ContentSplitLayout } from './content-split.types';\n\n@Component({\n selector: 'fk-content-split',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './content-split.component.html',\n styleUrl: './content-split.component.scss',\n})\nexport class FkContentSplitComponent {\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly ngZone = inject(NgZone);\n private readonly destroyRef = inject(DestroyRef);\n private readonly platformId = inject(PLATFORM_ID);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n\n // ===== INPUTS =====\n /** Width of the left panel when the layout is in wide mode. */\n readonly leftWidth = input<string>('280px');\n /** Width of the left panel when collapsed to a narrow icon rail. */\n readonly railWidth = input<string>('64px');\n /** Host element pixel width below which the layout switches to narrow mode. */\n readonly breakpoint = input<number>(700);\n /**\n * Optional CSS max-width for the layout. When set, the split is capped at\n * this width and centered in its container (via auto inline margins);\n * unset (default), it fills the full available width as before.\n */\n readonly maxWidth = input<string | null>(null);\n /** Two-way binding for the collapsed state of the left panel. */\n readonly collapsed = model<boolean>(false);\n\n // ===== INTERNAL STATE =====\n readonly layout = signal<ContentSplitLayout>('wide');\n readonly animate = signal(false);\n\n private observer: ResizeObserver | null = null;\n\n // ===== COMPUTED =====\n readonly isNarrow = computed(() => this.layout() === 'narrow');\n readonly isWide = computed(() => this.layout() === 'wide');\n readonly showOverlay = computed(() => this.isNarrow() && !this.collapsed());\n\n readonly classes = computed(() =>\n [\n 'fk-content-split',\n this.isNarrow() ? 'fk-content-split--narrow' : 'fk-content-split--wide',\n this.isNarrow() && !this.collapsed() ? 'fk-content-split--open' : '',\n this.isNarrow() && this.collapsed() ? 'fk-content-split--closed' : '',\n this.animate() ? 'fk-content-split--animate' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' '),\n );\n\n // ===== HOST BINDINGS =====\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n @HostBinding('style.--fk-content-split-left-width')\n get hostLeftWidth() {\n return this.leftWidth();\n }\n\n @HostBinding('style.--fk-content-split-rail-width')\n get hostRailWidth() {\n return this.railWidth();\n }\n\n @HostBinding('style.--fk-content-split-max-width')\n get hostMaxWidth() {\n return this.maxWidth();\n }\n\n // ===== CONSTRUCTOR =====\n\n constructor() {\n // ResizeObserver is browser-only — the SSR pass has no live\n // layout to observe, so the initial layout signal stays at its\n // default (\"wide\") and the browser pass wires the observer once\n // hydration mounts the element.\n if (isPlatformBrowser(this.platformId)) {\n this.ngZone.runOutsideAngular(() => {\n this.observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n\n if (!entry) {\n return;\n }\n\n const hostWidth = entry.contentRect.width;\n const bp = this.breakpoint();\n\n // A width of 0 means the split is hidden (an inactive tab, a\n // collapsed section), not that the screen is narrow. Keep the\n // last real layout, so showing it again does not fold the left\n // panel away and animate it back.\n if (hostWidth === 0) {\n return;\n }\n\n if (hostWidth < bp && this.layout() !== 'narrow') {\n this.ngZone.run(() => {\n this.layout.set('narrow');\n this.animate.set(false);\n this.collapsed.set(true);\n });\n } else if (hostWidth >= bp && this.layout() !== 'wide') {\n this.ngZone.run(() => {\n this.layout.set('wide');\n this.animate.set(false);\n this.collapsed.set(true);\n });\n }\n });\n\n this.observer.observe(this.el.nativeElement);\n });\n }\n\n this.destroyRef.onDestroy(() => {\n this.observer?.disconnect();\n });\n }\n\n // ===== PUBLIC METHODS =====\n\n /** Expand the left panel. */\n open(): void {\n this.animate.set(true);\n this.collapsed.set(false);\n }\n\n /** Collapse the left panel. */\n close(): void {\n this.animate.set(true);\n this.collapsed.set(true);\n }\n\n /** Toggle the left panel between expanded and collapsed. */\n toggle(): void {\n if (this.collapsed()) {\n this.open();\n } else {\n this.close();\n }\n }\n\n /** Dismiss the left panel overlay on narrow viewports (alias for `close`). */\n dismiss(): void {\n this.close();\n }\n}\n","@if (showOverlay()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-content-split__overlay\" (click)=\"dismiss()\"></div>\n}\n\n<div\n class=\"fk-content-split__left\"\n [class.fk-content-split__left--open]=\"!collapsed()\"\n>\n <div class=\"fk-content-split__left-content\">\n <ng-content select=\"[contentSplitLeft]\" />\n </div>\n</div>\n\n<div class=\"fk-content-split__rail\">\n <button\n class=\"fk-content-split__rail-toggle\"\n type=\"button\"\n [attr.aria-label]=\"collapsed() ? 'Open panel' : 'Close panel'\"\n (click)=\"toggle()\"\n >\n <span\n class=\"fk-content-split__rail-toggle-icon\"\n [class.fk-content-split__rail-toggle-icon--open]=\"!collapsed()\"\n aria-hidden=\"true\"\n >\n <span class=\"fk-content-split__rail-toggle-projected\">\n <ng-content select=\"[contentSplitToggleIcon]\" />\n </span>\n <svg\n class=\"fk-content-split__rail-toggle-fallback\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M15.25 9H2.75\" />\n <path d=\"M11 4.75L15.25 9L11 13.25\" />\n </svg>\n </span>\n </button>\n</div>\n\n<div class=\"fk-content-split__right\">\n <ng-content select=\"[contentSplitRight]\" />\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -31,7 +31,15 @@ class AvatarComponent {
|
|
|
31
31
|
if (!name) {
|
|
32
32
|
return '?';
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
// Only words that start with a letter count, so a suffix such as
|
|
35
|
+
// "(she/her)" or "(dev)" never lends a bracket to the initials.
|
|
36
|
+
const parts = name
|
|
37
|
+
.trim()
|
|
38
|
+
.split(/\s+/)
|
|
39
|
+
.filter((part) => /^\p{L}/u.test(part));
|
|
40
|
+
if (parts.length === 0) {
|
|
41
|
+
return '?';
|
|
42
|
+
}
|
|
35
43
|
if (parts.length >= 2) {
|
|
36
44
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
37
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-kit-ui-ng-ui-avatar.mjs","sources":["../../../../packages/ui-ng/ui/avatar/avatar.component.ts","../../../../packages/ui-ng/ui/avatar/avatar.component.html","../../../../packages/ui-ng/ui/avatar/frame-kit-ui-ng-ui-avatar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\n\nimport type { AvatarSize, AvatarStatus } from './avatar.types';\n\n@Component({\n selector: 'fk-avatar',\n standalone: true,\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './avatar.component.html',\n styleUrl: './avatar.component.scss',\n})\nexport class AvatarComponent {\n // ===== IDENTITY =====\n /** Full display name used to derive initials when no explicit initials or image are provided. */\n readonly name = input.required<string>();\n /** URL of the avatar image; falls back to initials when the image fails to load. */\n readonly avatarUrl = input<string | null>(null);\n /** Explicit two-character initials that override the name-derived fallback. */\n readonly initials = input<string | null>(null);\n\n // ===== STATUS =====\n /** Online/offline/away status to reflect on the indicator dot. */\n readonly status = input<AvatarStatus | null>(null);\n /** When true, renders the status indicator dot over the avatar. */\n readonly showStatus = input<boolean>(false);\n\n // ===== DISPLAY =====\n /** Controls the avatar size. */\n readonly size = input<AvatarSize>('md');\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n\n readonly derivedInitials = computed(() => {\n const explicit = this.initials();\n\n if (explicit) {\n return explicit;\n }\n\n const name = this.name();\n\n if (!name) {\n return '?';\n }\n\n const parts = name.trim().split(/\\s+/);\n\n if (parts.length >= 2) {\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n }\n\n return parts[0].substring(0, 2).toUpperCase();\n });\n\n readonly showIndicator = computed(\n () => this.showStatus() && this.status() !== null,\n );\n\n readonly classes = computed(() => {\n return [\n 'fk-avatar',\n `fk-avatar--${this.size()}`,\n this.showIndicator() ? `fk-avatar--status-${this.status()}` : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n // Tracks whether the image failed to load\n readonly imgError = { value: false };\n\n onImgError(): void {\n this.imgError.value = true;\n }\n}\n","<!-- Avatar circle -->\n<span\n class=\"fk-avatar__circle\"\n [attr.aria-label]=\"ariaLabel() ?? 'Avatar for ' + name()\"\n role=\"img\"\n>\n @if (avatarUrl() && !imgError.value) {\n <img\n class=\"fk-avatar__image\"\n [src]=\"avatarUrl()\"\n [alt]=\"name()\"\n (error)=\"onImgError()\"\n />\n } @else {\n <span class=\"fk-avatar__initials\" aria-hidden=\"true\">\n {{ derivedInitials() }}\n </span>\n }\n</span>\n\n<!-- Status indicator -->\n@if (showIndicator()) {\n <span\n class=\"fk-avatar__status\"\n [attr.aria-label]=\"status() + ' status'\"\n role=\"status\"\n ></span>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;MAkBa,eAAe,CAAA;;;AAGjB,IAAA,IAAI,GAAG,KAAK,CAAC,QAAQ,0EAAU;;AAE/B,IAAA,SAAS,GAAG,KAAK,CAAgB,IAAI,gFAAC;;AAEtC,IAAA,QAAQ,GAAG,KAAK,CAAgB,IAAI,+EAAC;;;AAIrC,IAAA,MAAM,GAAG,KAAK,CAAsB,IAAI,6EAAC;;AAEzC,IAAA,UAAU,GAAG,KAAK,CAAU,KAAK,iFAAC;;;AAIlC,IAAA,IAAI,GAAG,KAAK,CAAa,IAAI,2EAAC;;AAG9B,IAAA,SAAS,GAAG,KAAK,CAAS,EAAE,gFAAC;AAC7B,IAAA,EAAE,GAAG,KAAK,CAAgB,IAAI,yEAAC;AAC/B,IAAA,SAAS,GAAG,KAAK,CAAgB,IAAI,gFAAC;;AAItC,IAAA,eAAe,GAAG,QAAQ,CAAC,MAAK;AACvC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;QAEhC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,QAAQ;QACjB;AAEA,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;QAExB,IAAI,CAAC,IAAI,EAAE;AACT,YAAA,OAAO,GAAG;QACZ
|
|
1
|
+
{"version":3,"file":"frame-kit-ui-ng-ui-avatar.mjs","sources":["../../../../packages/ui-ng/ui/avatar/avatar.component.ts","../../../../packages/ui-ng/ui/avatar/avatar.component.html","../../../../packages/ui-ng/ui/avatar/frame-kit-ui-ng-ui-avatar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\n\nimport type { AvatarSize, AvatarStatus } from './avatar.types';\n\n@Component({\n selector: 'fk-avatar',\n standalone: true,\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './avatar.component.html',\n styleUrl: './avatar.component.scss',\n})\nexport class AvatarComponent {\n // ===== IDENTITY =====\n /** Full display name used to derive initials when no explicit initials or image are provided. */\n readonly name = input.required<string>();\n /** URL of the avatar image; falls back to initials when the image fails to load. */\n readonly avatarUrl = input<string | null>(null);\n /** Explicit two-character initials that override the name-derived fallback. */\n readonly initials = input<string | null>(null);\n\n // ===== STATUS =====\n /** Online/offline/away status to reflect on the indicator dot. */\n readonly status = input<AvatarStatus | null>(null);\n /** When true, renders the status indicator dot over the avatar. */\n readonly showStatus = input<boolean>(false);\n\n // ===== DISPLAY =====\n /** Controls the avatar size. */\n readonly size = input<AvatarSize>('md');\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n\n readonly derivedInitials = computed(() => {\n const explicit = this.initials();\n\n if (explicit) {\n return explicit;\n }\n\n const name = this.name();\n\n if (!name) {\n return '?';\n }\n\n // Only words that start with a letter count, so a suffix such as\n // \"(she/her)\" or \"(dev)\" never lends a bracket to the initials.\n const parts = name\n .trim()\n .split(/\\s+/)\n .filter((part) => /^\\p{L}/u.test(part));\n\n if (parts.length === 0) {\n return '?';\n }\n\n if (parts.length >= 2) {\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n }\n\n return parts[0].substring(0, 2).toUpperCase();\n });\n\n readonly showIndicator = computed(\n () => this.showStatus() && this.status() !== null,\n );\n\n readonly classes = computed(() => {\n return [\n 'fk-avatar',\n `fk-avatar--${this.size()}`,\n this.showIndicator() ? `fk-avatar--status-${this.status()}` : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n // Tracks whether the image failed to load\n readonly imgError = { value: false };\n\n onImgError(): void {\n this.imgError.value = true;\n }\n}\n","<!-- Avatar circle -->\n<span\n class=\"fk-avatar__circle\"\n [attr.aria-label]=\"ariaLabel() ?? 'Avatar for ' + name()\"\n role=\"img\"\n>\n @if (avatarUrl() && !imgError.value) {\n <img\n class=\"fk-avatar__image\"\n [src]=\"avatarUrl()\"\n [alt]=\"name()\"\n (error)=\"onImgError()\"\n />\n } @else {\n <span class=\"fk-avatar__initials\" aria-hidden=\"true\">\n {{ derivedInitials() }}\n </span>\n }\n</span>\n\n<!-- Status indicator -->\n@if (showIndicator()) {\n <span\n class=\"fk-avatar__status\"\n [attr.aria-label]=\"status() + ' status'\"\n role=\"status\"\n ></span>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;MAkBa,eAAe,CAAA;;;AAGjB,IAAA,IAAI,GAAG,KAAK,CAAC,QAAQ,0EAAU;;AAE/B,IAAA,SAAS,GAAG,KAAK,CAAgB,IAAI,gFAAC;;AAEtC,IAAA,QAAQ,GAAG,KAAK,CAAgB,IAAI,+EAAC;;;AAIrC,IAAA,MAAM,GAAG,KAAK,CAAsB,IAAI,6EAAC;;AAEzC,IAAA,UAAU,GAAG,KAAK,CAAU,KAAK,iFAAC;;;AAIlC,IAAA,IAAI,GAAG,KAAK,CAAa,IAAI,2EAAC;;AAG9B,IAAA,SAAS,GAAG,KAAK,CAAS,EAAE,gFAAC;AAC7B,IAAA,EAAE,GAAG,KAAK,CAAgB,IAAI,yEAAC;AAC/B,IAAA,SAAS,GAAG,KAAK,CAAgB,IAAI,gFAAC;;AAItC,IAAA,eAAe,GAAG,QAAQ,CAAC,MAAK;AACvC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;QAEhC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,QAAQ;QACjB;AAEA,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;QAExB,IAAI,CAAC,IAAI,EAAE;AACT,YAAA,OAAO,GAAG;QACZ;;;QAIA,MAAM,KAAK,GAAG;AACX,aAAA,IAAI;aACJ,KAAK,CAAC,KAAK;AACX,aAAA,MAAM,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEzC,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AACtB,YAAA,OAAO,GAAG;QACZ;AAEA,QAAA,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE;YACrB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE;QACjE;AAEA,QAAA,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE;AAC/C,IAAA,CAAC,sFAAC;AAEO,IAAA,aAAa,GAAG,QAAQ,CAC/B,MAAM,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,oFAClD;AAEQ,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;QAC/B,OAAO;YACL,WAAW;AACX,YAAA,CAAA,WAAA,EAAc,IAAI,CAAC,IAAI,EAAE,CAAA,CAAE;AAC3B,YAAA,IAAI,CAAC,aAAa,EAAE,GAAG,CAAA,kBAAA,EAAqB,IAAI,CAAC,MAAM,EAAE,CAAA,CAAE,GAAG,EAAE;YAChE,IAAI,CAAC,SAAS,EAAE;AACjB;aACE,MAAM,CAAC,OAAO;aACd,IAAI,CAAC,GAAG,CAAC;AACd,IAAA,CAAC,8EAAC;AAEF,IAAA,IACI,SAAS,GAAA;AACX,QAAA,OAAO,IAAI,CAAC,OAAO,EAAE;IACvB;AAEA,IAAA,IACI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,EAAE,EAAE;IAClB;;AAGS,IAAA,QAAQ,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE;IAEpC,UAAU,GAAA;AACR,QAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,IAAI;IAC5B;uGAvFW,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAf,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAe,uwCClB5B,+nBA4BA,EAAA,MAAA,EAAA,CAAA,mpEAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FDVa,eAAe,EAAA,UAAA,EAAA,CAAA;kBAR3B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,WAAW,cACT,IAAI,EAAA,OAAA,EACP,EAAE,EAAA,eAAA,EACM,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,+nBAAA,EAAA,MAAA,EAAA,CAAA,mpEAAA,CAAA,EAAA;;sBA4E9C,WAAW;uBAAC,OAAO;;sBAKnB,WAAW;uBAAC,SAAS;;;AE/FxB;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frame-kit/ui-ng",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.25",
|
|
4
4
|
"description": "Style-agnostic, token-driven Angular UI component library for FrameKit.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@angular/forms": "^21.2.0",
|
|
18
18
|
"@angular/platform-browser": "^21.2.0",
|
|
19
19
|
"@angular/router": "^21.2.0",
|
|
20
|
-
"@frame-kit/utils": "0.2.
|
|
20
|
+
"@frame-kit/utils": "0.2.25",
|
|
21
21
|
"rxjs": "^7.0.0"
|
|
22
22
|
},
|
|
23
23
|
"sideEffects": false,
|
package/ui/avatar/README.md
CHANGED
|
@@ -25,7 +25,7 @@ A token-driven avatar atom that displays a user image with an initials fallback
|
|
|
25
25
|
## Features
|
|
26
26
|
|
|
27
27
|
- Displays an image when `avatarUrl` is provided, gracefully falling back to initials on load error
|
|
28
|
-
- Auto-derives initials from `name` (first + last initial for multi-word names, first two characters for single names)
|
|
28
|
+
- Auto-derives initials from `name` (first + last initial for multi-word names, first two characters for single names); words that do not start with a letter, such as a "(she/her)" suffix, are ignored
|
|
29
29
|
- Status indicator dot positioned at the bottom-right corner, color-coded by status
|
|
30
30
|
- Three size presets (`sm`, `md`, `lg`) controlled via tokens
|
|
31
31
|
- Fully token-driven — all colors, sizes, typography, and radii are overridable
|