@frame-kit/ui-ng 0.0.1
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/COMPONENTS.md +683 -0
- package/DEVELOPMENT_GUIDE.md +1102 -0
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/THEMING.md +130 -0
- package/core/headline/README.md +121 -0
- package/core/icon/README.md +173 -0
- package/core/image/README.md +210 -0
- package/core/link/README.md +297 -0
- package/core/separator/README.md +145 -0
- package/core/text/README.md +240 -0
- package/directives/infinite-scroll/README.md +102 -0
- package/directives/spotlight/README.md +154 -0
- package/directives/tooltip/README.md +147 -0
- package/docs/endpoint-link/README.md +142 -0
- package/docs/method-badge/README.md +154 -0
- package/fesm2022/frame-kit-ui-ng-core-headline.mjs +122 -0
- package/fesm2022/frame-kit-ui-ng-core-headline.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-icon.mjs +189 -0
- package/fesm2022/frame-kit-ui-ng-core-icon.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-image.mjs +123 -0
- package/fesm2022/frame-kit-ui-ng-core-image.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-link.mjs +369 -0
- package/fesm2022/frame-kit-ui-ng-core-link.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-separator.mjs +59 -0
- package/fesm2022/frame-kit-ui-ng-core-separator.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-core-text.mjs +204 -0
- package/fesm2022/frame-kit-ui-ng-core-text.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-directives-infinite-scroll.mjs +74 -0
- package/fesm2022/frame-kit-ui-ng-directives-infinite-scroll.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-directives-spotlight.mjs +76 -0
- package/fesm2022/frame-kit-ui-ng-directives-spotlight.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-directives-tooltip.mjs +425 -0
- package/fesm2022/frame-kit-ui-ng-directives-tooltip.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-docs-endpoint-link.mjs +63 -0
- package/fesm2022/frame-kit-ui-ng-docs-endpoint-link.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-docs-method-badge.mjs +43 -0
- package/fesm2022/frame-kit-ui-ng-docs-method-badge.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-forms.mjs +3632 -0
- package/fesm2022/frame-kit-ui-ng-forms.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-layouts-app-shell.mjs +239 -0
- package/fesm2022/frame-kit-ui-ng-layouts-app-shell.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs +132 -0
- package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-services-overlay-orchestrator.mjs +133 -0
- package/fesm2022/frame-kit-ui-ng-services-overlay-orchestrator.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-services-spotlight.mjs +60 -0
- package/fesm2022/frame-kit-ui-ng-services-spotlight.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-services-toast.mjs +166 -0
- package/fesm2022/frame-kit-ui-ng-services-toast.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-accordion.mjs +214 -0
- package/fesm2022/frame-kit-ui-ng-ui-accordion.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-alert.mjs +82 -0
- package/fesm2022/frame-kit-ui-ng-ui-alert.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-avatar-stack.mjs +76 -0
- package/fesm2022/frame-kit-ui-ng-ui-avatar-stack.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs +81 -0
- package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-badge.mjs +81 -0
- package/fesm2022/frame-kit-ui-ng-ui-badge.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-breadcrumb.mjs +68 -0
- package/fesm2022/frame-kit-ui-ng-ui-breadcrumb.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-button.mjs +108 -0
- package/fesm2022/frame-kit-ui-ng-ui-button.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-callout.mjs +58 -0
- package/fesm2022/frame-kit-ui-ng-ui-callout.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-card.mjs +70 -0
- package/fesm2022/frame-kit-ui-ng-ui-card.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-copyable-field.mjs +113 -0
- package/fesm2022/frame-kit-ui-ng-ui-copyable-field.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-data-table.mjs +1288 -0
- package/fesm2022/frame-kit-ui-ng-ui-data-table.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-dialog.mjs +456 -0
- package/fesm2022/frame-kit-ui-ng-ui-dialog.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-drawer.mjs +398 -0
- package/fesm2022/frame-kit-ui-ng-ui-drawer.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-dropdown-menu.mjs +398 -0
- package/fesm2022/frame-kit-ui-ng-ui-dropdown-menu.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-editable-field.mjs +125 -0
- package/fesm2022/frame-kit-ui-ng-ui-editable-field.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-icon-badge.mjs +113 -0
- package/fesm2022/frame-kit-ui-ng-ui-icon-badge.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-icon-list.mjs +111 -0
- package/fesm2022/frame-kit-ui-ng-ui-icon-list.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-inline-edit.mjs +103 -0
- package/fesm2022/frame-kit-ui-ng-ui-inline-edit.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-list-editor.mjs +135 -0
- package/fesm2022/frame-kit-ui-ng-ui-list-editor.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-loader.mjs +81 -0
- package/fesm2022/frame-kit-ui-ng-ui-loader.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-menu-item.mjs +79 -0
- package/fesm2022/frame-kit-ui-ng-ui-menu-item.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-brand.mjs +40 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-brand.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-group.mjs +110 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-group.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-separator.mjs +91 -0
- package/fesm2022/frame-kit-ui-ng-ui-nav-separator.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-node-tree-breadcrumb.mjs +86 -0
- package/fesm2022/frame-kit-ui-ng-ui-node-tree-breadcrumb.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-node-tree.mjs +443 -0
- package/fesm2022/frame-kit-ui-ng-ui-node-tree.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-note.mjs +56 -0
- package/fesm2022/frame-kit-ui-ng-ui-note.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-numbered-list.mjs +105 -0
- package/fesm2022/frame-kit-ui-ng-ui-numbered-list.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-pagination.mjs +110 -0
- package/fesm2022/frame-kit-ui-ng-ui-pagination.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-progress-bar.mjs +129 -0
- package/fesm2022/frame-kit-ui-ng-ui-progress-bar.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-sidenav-link.mjs +42 -0
- package/fesm2022/frame-kit-ui-ng-ui-sidenav-link.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-tabs.mjs +894 -0
- package/fesm2022/frame-kit-ui-ng-ui-tabs.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-timeline.mjs +81 -0
- package/fesm2022/frame-kit-ui-ng-ui-timeline.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-toast.mjs +179 -0
- package/fesm2022/frame-kit-ui-ng-ui-toast.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-user-menu.mjs +143 -0
- package/fesm2022/frame-kit-ui-ng-ui-user-menu.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng-ui-wizard-dialog.mjs +191 -0
- package/fesm2022/frame-kit-ui-ng-ui-wizard-dialog.mjs.map +1 -0
- package/fesm2022/frame-kit-ui-ng.mjs +58 -0
- package/fesm2022/frame-kit-ui-ng.mjs.map +1 -0
- package/layouts/app-shell/README.md +357 -0
- package/layouts/content-split/README.md +180 -0
- package/package.json +253 -0
- package/services/overlay-orchestrator/README.md +184 -0
- package/services/spotlight/README.md +61 -0
- package/services/toast/README.md +118 -0
- package/types/frame-kit-ui-ng-core-headline.d.ts +38 -0
- package/types/frame-kit-ui-ng-core-icon.d.ts +74 -0
- package/types/frame-kit-ui-ng-core-image.d.ts +93 -0
- package/types/frame-kit-ui-ng-core-link.d.ts +251 -0
- package/types/frame-kit-ui-ng-core-separator.d.ts +28 -0
- package/types/frame-kit-ui-ng-core-text.d.ts +186 -0
- package/types/frame-kit-ui-ng-directives-infinite-scroll.d.ts +42 -0
- package/types/frame-kit-ui-ng-directives-spotlight.d.ts +51 -0
- package/types/frame-kit-ui-ng-directives-tooltip.d.ts +70 -0
- package/types/frame-kit-ui-ng-docs-endpoint-link.d.ts +43 -0
- package/types/frame-kit-ui-ng-docs-method-badge.d.ts +30 -0
- package/types/frame-kit-ui-ng-forms.d.ts +1674 -0
- package/types/frame-kit-ui-ng-layouts-app-shell.d.ts +75 -0
- package/types/frame-kit-ui-ng-layouts-content-split.d.ts +43 -0
- package/types/frame-kit-ui-ng-services-overlay-orchestrator.d.ts +96 -0
- package/types/frame-kit-ui-ng-services-spotlight.d.ts +32 -0
- package/types/frame-kit-ui-ng-services-toast.d.ts +100 -0
- package/types/frame-kit-ui-ng-ui-accordion.d.ts +86 -0
- package/types/frame-kit-ui-ng-ui-alert.d.ts +34 -0
- package/types/frame-kit-ui-ng-ui-avatar-stack.d.ts +38 -0
- package/types/frame-kit-ui-ng-ui-avatar.d.ts +36 -0
- package/types/frame-kit-ui-ng-ui-badge.d.ts +33 -0
- package/types/frame-kit-ui-ng-ui-breadcrumb.d.ts +45 -0
- package/types/frame-kit-ui-ng-ui-button.d.ts +48 -0
- package/types/frame-kit-ui-ng-ui-callout.d.ts +26 -0
- package/types/frame-kit-ui-ng-ui-card.d.ts +30 -0
- package/types/frame-kit-ui-ng-ui-copyable-field.d.ts +62 -0
- package/types/frame-kit-ui-ng-ui-data-table.d.ts +482 -0
- package/types/frame-kit-ui-ng-ui-dialog.d.ts +166 -0
- package/types/frame-kit-ui-ng-ui-drawer.d.ts +130 -0
- package/types/frame-kit-ui-ng-ui-dropdown-menu.d.ts +77 -0
- package/types/frame-kit-ui-ng-ui-editable-field.d.ts +65 -0
- package/types/frame-kit-ui-ng-ui-icon-badge.d.ts +45 -0
- package/types/frame-kit-ui-ng-ui-icon-list.d.ts +67 -0
- package/types/frame-kit-ui-ng-ui-inline-edit.d.ts +44 -0
- package/types/frame-kit-ui-ng-ui-list-editor.d.ts +56 -0
- package/types/frame-kit-ui-ng-ui-loader.d.ts +32 -0
- package/types/frame-kit-ui-ng-ui-menu-item.d.ts +27 -0
- package/types/frame-kit-ui-ng-ui-nav-brand.d.ts +25 -0
- package/types/frame-kit-ui-ng-ui-nav-group.d.ts +60 -0
- package/types/frame-kit-ui-ng-ui-nav-separator.d.ts +33 -0
- package/types/frame-kit-ui-ng-ui-node-tree-breadcrumb.d.ts +35 -0
- package/types/frame-kit-ui-ng-ui-node-tree.d.ts +135 -0
- package/types/frame-kit-ui-ng-ui-note.d.ts +22 -0
- package/types/frame-kit-ui-ng-ui-numbered-list.d.ts +52 -0
- package/types/frame-kit-ui-ng-ui-pagination.d.ts +49 -0
- package/types/frame-kit-ui-ng-ui-progress-bar.d.ts +50 -0
- package/types/frame-kit-ui-ng-ui-sidenav-link.d.ts +24 -0
- package/types/frame-kit-ui-ng-ui-tabs.d.ts +266 -0
- package/types/frame-kit-ui-ng-ui-timeline.d.ts +42 -0
- package/types/frame-kit-ui-ng-ui-toast.d.ts +56 -0
- package/types/frame-kit-ui-ng-ui-user-menu.d.ts +87 -0
- package/types/frame-kit-ui-ng-ui-wizard-dialog.d.ts +116 -0
- package/types/frame-kit-ui-ng.d.ts +53 -0
- package/ui/accordion/README.md +261 -0
- package/ui/alert/README.md +211 -0
- package/ui/avatar/README.md +167 -0
- package/ui/avatar-stack/README.md +164 -0
- package/ui/badge/README.md +162 -0
- package/ui/breadcrumb/README.md +240 -0
- package/ui/button/README.md +184 -0
- package/ui/callout/README.md +159 -0
- package/ui/card/README.md +174 -0
- package/ui/copyable-field/README.md +235 -0
- package/ui/data-table/README.md +408 -0
- package/ui/dialog/README.md +222 -0
- package/ui/drawer/README.md +274 -0
- package/ui/dropdown-menu/README.md +336 -0
- package/ui/editable-field/README.md +171 -0
- package/ui/icon-badge/README.md +131 -0
- package/ui/icon-list/README.md +205 -0
- package/ui/inline-edit/README.md +135 -0
- package/ui/list-editor/README.md +162 -0
- package/ui/loader/README.md +160 -0
- package/ui/menu-item/README.md +204 -0
- package/ui/nav-brand/README.md +111 -0
- package/ui/nav-group/README.md +145 -0
- package/ui/nav-separator/README.md +44 -0
- package/ui/node-tree/README.md +278 -0
- package/ui/node-tree-breadcrumb/README.md +164 -0
- package/ui/note/README.md +146 -0
- package/ui/numbered-list/README.md +187 -0
- package/ui/pagination/README.md +174 -0
- package/ui/progress-bar/README.md +223 -0
- package/ui/sidenav-link/README.md +214 -0
- package/ui/tabs/README.md +204 -0
- package/ui/timeline/README.md +285 -0
- package/ui/toast/README.md +243 -0
- package/ui/user-menu/README.md +260 -0
- package/ui/wizard-dialog/README.md +283 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-kit-ui-ng-core-icon.mjs","sources":["../../../../packages/ui-ng/core/icon/icon-registry.service.ts","../../../../packages/ui-ng/core/icon/icon.component.ts","../../../../packages/ui-ng/core/icon/icon.component.html","../../../../packages/ui-ng/core/icon/provide-icons.ts","../../../../packages/ui-ng/core/icon/frame-kit-ui-ng-core-icon.ts"],"sourcesContent":["import { inject, Injectable } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\n\n@Injectable({ providedIn: 'root' })\nexport class IconRegistryService {\n private readonly icons = new Map<string, SafeHtml>();\n\n private readonly sanitizer = inject(DomSanitizer);\n\n registerIcon(name: string, svgContent: string): void {\n const normalized = this.normalizeSvg(svgContent);\n\n this.icons.set(name, this.sanitizer.bypassSecurityTrustHtml(normalized));\n }\n\n private normalizeSvg(svg: string): string {\n const svgStart = svg.indexOf('<svg');\n const svgOnly = svgStart > 0 ? svg.slice(svgStart) : svg;\n\n return svgOnly\n .replace(/<svg([^>]*)>/, (_match, attrs: string) => {\n const cleaned = attrs.replace(/\\s*(?:width|height)=\"[^\"]*\"/gi, '');\n\n const hasFill = /\\bfill\\s*=/i.test(cleaned);\n\n return `<svg${cleaned}${hasFill ? '' : ' fill=\"currentColor\"'}>`;\n })\n .replace(\n /\\b(stroke|fill)=\"(?!none|currentColor)[^\"]*\"/gi,\n '$1=\"currentColor\"',\n );\n }\n\n registerIcons(icons: Record<string, string>): void {\n for (const [name, svg] of Object.entries(icons)) {\n this.registerIcon(name, svg);\n }\n }\n\n getIcon(name: string): SafeHtml | null {\n return this.icons.get(name) ?? null;\n }\n\n hasIcon(name: string): boolean {\n return this.icons.has(name);\n }\n\n getRegisteredNames(): string[] {\n return Array.from(this.icons.keys());\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n inject,\n input,\n isDevMode,\n} from '@angular/core';\n\nimport type { IconColor, IconSize } from './icon.types';\nimport { IconRegistryService } from './icon-registry.service';\n\n@Component({\n selector: 'fk-icon',\n standalone: true,\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './icon.component.html',\n styleUrl: './icon.component.scss',\n})\nexport class IconComponent {\n private readonly registry = inject(IconRegistryService);\n\n // ===== INPUTS =====\n /** Registry key for the SVG icon to render. */\n readonly name = input.required<string>();\n /** Preset size token applied as a CSS class; pass a CSS value for custom sizes. */\n readonly size = input<IconSize>('md');\n /** Icon color — preset token names resolve to design-token colors, any other string is applied as a literal CSS `color`. */\n readonly color = input<IconColor>('inherit');\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n /** Explicitly sets `aria-hidden`; when null, the directive infers it from `ariaLabel`. */\n readonly ariaHidden = input<boolean | null>(null);\n\n // ===== COMPUTED =====\n\n readonly svgContent = computed(() => {\n const name = this.name();\n\n // No name bound (e.g. an optional/absent icon) — render nothing, no warning.\n if (!name) {\n return null;\n }\n\n const content = this.registry.getIcon(name);\n\n if (!content && isDevMode()) {\n console.warn(\n `[fk-icon] Icon \"${name}\" not found in registry. ` +\n `Register it via IconRegistryService.registerIcon() before use.`,\n );\n }\n\n return content;\n });\n\n readonly effectiveAriaHidden = computed(() => {\n const explicit = this.ariaHidden();\n\n if (explicit !== null) {\n return explicit;\n }\n\n return this.ariaLabel() === null;\n });\n\n private readonly sizePresets = ['xs', 'sm', 'md', 'lg', 'xl'];\n\n readonly isCustomSize = computed(\n () => !this.sizePresets.includes(this.size()),\n );\n\n readonly classes = computed(() => {\n return [\n 'fk-icon',\n this.isCustomSize() ? '' : `fk-icon--${this.size()}`,\n this.color() !== 'inherit' ? `fk-icon--${this.color()}` : '',\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('style.font-size')\n get hostFontSize(): string | null {\n return this.isCustomSize() ? this.size() : null;\n }\n\n @HostBinding('attr.role')\n get hostRole(): string | null {\n return this.ariaLabel() ? 'img' : null;\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n\n @HostBinding('attr.aria-hidden')\n get hostAriaHidden(): string | null {\n return this.effectiveAriaHidden() ? 'true' : null;\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('style.color')\n get hostColor(): string | null {\n const color = this.color();\n const presets = [\n 'inherit',\n 'default',\n 'muted',\n 'primary',\n 'danger',\n 'success',\n ];\n\n if (presets.includes(color)) {\n return null;\n }\n\n return color;\n }\n}\n","@if (svgContent()) {\n <span class=\"fk-icon__svg\" [innerHTML]=\"svgContent()\"></span>\n}\n","import {\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\n\nimport { IconRegistryService } from './icon-registry.service';\n\n/**\n * Registers icons into the `IconRegistryService` via Angular DI.\n *\n * Icons are provided as a record of name → SVG string pairs.\n * This is tree-shakeable: only icons you import and pass here are bundled.\n *\n * @example\n * ```ts\n * import { searchIcon, lockIcon } from '@my-app/icons';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideIcons({\n * search: searchIcon,\n * lock: lockIcon,\n * }),\n * ],\n * };\n * ```\n */\nexport function provideIcons(\n icons: Record<string, string>,\n): EnvironmentProviders {\n return makeEnvironmentProviders([\n IconRegistryService,\n provideAppInitializer(() => {\n inject(IconRegistryService).registerIcons(icons);\n }),\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, output, computed, signal, effect, isDevMode, HostBinding, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
class ImageComponent {
|
|
5
|
+
// ===== REQUIRED =====
|
|
6
|
+
/** URL of the image to display. */
|
|
7
|
+
src = input.required(...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
8
|
+
/** Accessible alternative text describing the image. */
|
|
9
|
+
alt = input.required(...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
|
|
10
|
+
// ===== DIMENSION MODE =====
|
|
11
|
+
/** When true, the image expands to fill its positioned parent; `width` and `height` are ignored. */
|
|
12
|
+
fill = input(false, ...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
|
|
13
|
+
/** Explicit pixel width; required when `fill` is false to prevent layout shift. */
|
|
14
|
+
width = input(null, ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
15
|
+
/** Explicit pixel height; required when `fill` is false to prevent layout shift. */
|
|
16
|
+
height = input(null, ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
17
|
+
// ===== IMAGE BEHAVIOR =====
|
|
18
|
+
/** `srcset` attribute value for responsive image resolution switching. */
|
|
19
|
+
srcSet = input(null, ...(ngDevMode ? [{ debugName: "srcSet" }] : /* istanbul ignore next */ []));
|
|
20
|
+
/** `sizes` attribute value used alongside `srcSet` for viewport-aware resolution selection. */
|
|
21
|
+
sizes = input(null, ...(ngDevMode ? [{ debugName: "sizes" }] : /* istanbul ignore next */ []));
|
|
22
|
+
/** Browser loading strategy — `"lazy"` defers off-screen images. */
|
|
23
|
+
loading = input('lazy', ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
24
|
+
/** Browser decode hint — `"async"` avoids blocking the main thread. */
|
|
25
|
+
decoding = input('async', ...(ngDevMode ? [{ debugName: "decoding" }] : /* istanbul ignore next */ []));
|
|
26
|
+
/** Fetch priority hint for the browser's preload scanner. */
|
|
27
|
+
fetchPriority = input('auto', ...(ngDevMode ? [{ debugName: "fetchPriority" }] : /* istanbul ignore next */ []));
|
|
28
|
+
/** Cross-origin policy for the image request. */
|
|
29
|
+
crossOrigin = input(null, ...(ngDevMode ? [{ debugName: "crossOrigin" }] : /* istanbul ignore next */ []));
|
|
30
|
+
/** `referrerpolicy` attribute value forwarded to the underlying `<img>` element. */
|
|
31
|
+
referrerPolicy = input(null, ...(ngDevMode ? [{ debugName: "referrerPolicy" }] : /* istanbul ignore next */ []));
|
|
32
|
+
// ===== PRESENTATION =====
|
|
33
|
+
/** `object-fit` strategy when the image is displayed inside a fixed container. */
|
|
34
|
+
fit = input('cover', ...(ngDevMode ? [{ debugName: "fit" }] : /* istanbul ignore next */ []));
|
|
35
|
+
/** `object-position` value controlling focal point within the container. */
|
|
36
|
+
position = input('center', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
37
|
+
/** Corner radius token applied to the image. */
|
|
38
|
+
radius = input('none', ...(ngDevMode ? [{ debugName: "radius" }] : /* istanbul ignore next */ []));
|
|
39
|
+
/** Locks the host to a specific aspect ratio expressed as a number (w/h) or CSS string. */
|
|
40
|
+
aspectRatio = input(null, ...(ngDevMode ? [{ debugName: "aspectRatio" }] : /* istanbul ignore next */ []));
|
|
41
|
+
// ===== FALLBACK =====
|
|
42
|
+
/** Fallback image URL displayed automatically when the primary `src` fails to load. */
|
|
43
|
+
fallbackSrc = input(null, ...(ngDevMode ? [{ debugName: "fallbackSrc" }] : /* istanbul ignore next */ []));
|
|
44
|
+
// ===== BASE PROPS =====
|
|
45
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
46
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
47
|
+
// ===== EVENTS =====
|
|
48
|
+
/** Fires when the image has finished loading successfully. */
|
|
49
|
+
imageLoad = output();
|
|
50
|
+
/** Fires when the image fails to load; switches to `fallbackSrc` if provided. */
|
|
51
|
+
imageError = output();
|
|
52
|
+
// ===== INTERNAL STATE =====
|
|
53
|
+
currentSrc = computed(() => {
|
|
54
|
+
const fallback = this.fallbackSrc();
|
|
55
|
+
if (this.hasError() && fallback) {
|
|
56
|
+
return fallback;
|
|
57
|
+
}
|
|
58
|
+
return this.src();
|
|
59
|
+
}, ...(ngDevMode ? [{ debugName: "currentSrc" }] : /* istanbul ignore next */ []));
|
|
60
|
+
hasError = signal(false, ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
61
|
+
constructor() {
|
|
62
|
+
// Reset error state when src changes
|
|
63
|
+
effect(() => {
|
|
64
|
+
this.src();
|
|
65
|
+
this.hasError.set(false);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Dev-mode dimension warning
|
|
69
|
+
dimensionWarning = computed(() => {
|
|
70
|
+
if (isDevMode() && !this.fill() && !this.width() && !this.height()) {
|
|
71
|
+
console.warn(`[fk-image] width and height are required when fill is not set. ` +
|
|
72
|
+
`Provide explicit dimensions to prevent layout shift.`);
|
|
73
|
+
}
|
|
74
|
+
}, ...(ngDevMode ? [{ debugName: "dimensionWarning" }] : /* istanbul ignore next */ []));
|
|
75
|
+
// ===== COMPUTED =====
|
|
76
|
+
classes = computed(() => {
|
|
77
|
+
// Trigger dimension warning in dev mode
|
|
78
|
+
this.dimensionWarning();
|
|
79
|
+
return [
|
|
80
|
+
'fk-image',
|
|
81
|
+
this.fill() ? 'fk-image--fill' : '',
|
|
82
|
+
`fk-image--radius-${this.radius()}`,
|
|
83
|
+
this.className(),
|
|
84
|
+
]
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.join(' ');
|
|
87
|
+
}, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
88
|
+
get hostClass() {
|
|
89
|
+
return this.classes();
|
|
90
|
+
}
|
|
91
|
+
get hostId() {
|
|
92
|
+
return this.id();
|
|
93
|
+
}
|
|
94
|
+
// ===== EVENT HANDLERS =====
|
|
95
|
+
onLoad() {
|
|
96
|
+
this.imageLoad.emit();
|
|
97
|
+
}
|
|
98
|
+
onError() {
|
|
99
|
+
this.imageError.emit();
|
|
100
|
+
if (this.fallbackSrc() && !this.hasError()) {
|
|
101
|
+
this.hasError.set(true);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ImageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
105
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: ImageComponent, isStandalone: true, selector: "fk-image", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: true, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: true, transformFunction: null }, fill: { classPropertyName: "fill", publicName: "fill", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, srcSet: { classPropertyName: "srcSet", publicName: "srcSet", isSignal: true, isRequired: false, transformFunction: null }, sizes: { classPropertyName: "sizes", publicName: "sizes", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, decoding: { classPropertyName: "decoding", publicName: "decoding", isSignal: true, isRequired: false, transformFunction: null }, fetchPriority: { classPropertyName: "fetchPriority", publicName: "fetchPriority", isSignal: true, isRequired: false, transformFunction: null }, crossOrigin: { classPropertyName: "crossOrigin", publicName: "crossOrigin", isSignal: true, isRequired: false, transformFunction: null }, referrerPolicy: { classPropertyName: "referrerPolicy", publicName: "referrerPolicy", isSignal: true, isRequired: false, transformFunction: null }, fit: { classPropertyName: "fit", publicName: "fit", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, radius: { classPropertyName: "radius", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, fallbackSrc: { classPropertyName: "fallbackSrc", publicName: "fallbackSrc", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imageLoad: "imageLoad", imageError: "imageError" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, ngImport: i0, template: "<img\n class=\"fk-image__native\"\n [src]=\"currentSrc()\"\n [alt]=\"alt()\"\n [attr.width]=\"fill() ? null : width()\"\n [attr.height]=\"fill() ? null : height()\"\n [attr.srcset]=\"srcSet()\"\n [attr.sizes]=\"sizes()\"\n [attr.loading]=\"loading()\"\n [attr.decoding]=\"decoding()\"\n [attr.fetchpriority]=\"fetchPriority()\"\n [attr.crossorigin]=\"crossOrigin()\"\n [attr.referrerpolicy]=\"referrerPolicy()\"\n [style.object-fit]=\"fit()\"\n [style.object-position]=\"position()\"\n [style.aspect-ratio]=\"aspectRatio()\"\n (load)=\"onLoad()\"\n (error)=\"onError()\"\n/>\n", styles: [":host{display:inline-block;overflow:hidden}:host.fk-image--fill{display:block;position:relative;width:100%;height:100%}:host.fk-image--fill .fk-image__native{position:absolute;inset:0;width:100%;height:100%}.fk-image__native{display:block;max-width:100%;object-fit:var(--fk-image-object-fit, cover);object-position:var(--fk-image-object-position, center)}:host.fk-image--radius-none{border-radius:var(--fk-radius-none, 0)}:host.fk-image--radius-sm{border-radius:var(--fk-radius-sm, .25rem)}:host.fk-image--radius-md{border-radius:var(--fk-radius-md, .5rem)}:host.fk-image--radius-lg{border-radius:var(--fk-radius-lg, .75rem)}:host.fk-image--radius-full{border-radius:var(--fk-radius-full, 9999px)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
106
|
+
}
|
|
107
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ImageComponent, decorators: [{
|
|
108
|
+
type: Component,
|
|
109
|
+
args: [{ selector: 'fk-image', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<img\n class=\"fk-image__native\"\n [src]=\"currentSrc()\"\n [alt]=\"alt()\"\n [attr.width]=\"fill() ? null : width()\"\n [attr.height]=\"fill() ? null : height()\"\n [attr.srcset]=\"srcSet()\"\n [attr.sizes]=\"sizes()\"\n [attr.loading]=\"loading()\"\n [attr.decoding]=\"decoding()\"\n [attr.fetchpriority]=\"fetchPriority()\"\n [attr.crossorigin]=\"crossOrigin()\"\n [attr.referrerpolicy]=\"referrerPolicy()\"\n [style.object-fit]=\"fit()\"\n [style.object-position]=\"position()\"\n [style.aspect-ratio]=\"aspectRatio()\"\n (load)=\"onLoad()\"\n (error)=\"onError()\"\n/>\n", styles: [":host{display:inline-block;overflow:hidden}:host.fk-image--fill{display:block;position:relative;width:100%;height:100%}:host.fk-image--fill .fk-image__native{position:absolute;inset:0;width:100%;height:100%}.fk-image__native{display:block;max-width:100%;object-fit:var(--fk-image-object-fit, cover);object-position:var(--fk-image-object-position, center)}:host.fk-image--radius-none{border-radius:var(--fk-radius-none, 0)}:host.fk-image--radius-sm{border-radius:var(--fk-radius-sm, .25rem)}:host.fk-image--radius-md{border-radius:var(--fk-radius-md, .5rem)}:host.fk-image--radius-lg{border-radius:var(--fk-radius-lg, .75rem)}:host.fk-image--radius-full{border-radius:var(--fk-radius-full, 9999px)}\n"] }]
|
|
110
|
+
}], ctorParameters: () => [], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: true }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: true }] }], fill: [{ type: i0.Input, args: [{ isSignal: true, alias: "fill", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], srcSet: [{ type: i0.Input, args: [{ isSignal: true, alias: "srcSet", required: false }] }], sizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizes", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], decoding: [{ type: i0.Input, args: [{ isSignal: true, alias: "decoding", required: false }] }], fetchPriority: [{ type: i0.Input, args: [{ isSignal: true, alias: "fetchPriority", required: false }] }], crossOrigin: [{ type: i0.Input, args: [{ isSignal: true, alias: "crossOrigin", required: false }] }], referrerPolicy: [{ type: i0.Input, args: [{ isSignal: true, alias: "referrerPolicy", required: false }] }], fit: [{ type: i0.Input, args: [{ isSignal: true, alias: "fit", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], radius: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], aspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspectRatio", required: false }] }], fallbackSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackSrc", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], imageLoad: [{ type: i0.Output, args: ["imageLoad"] }], imageError: [{ type: i0.Output, args: ["imageError"] }], hostClass: [{
|
|
111
|
+
type: HostBinding,
|
|
112
|
+
args: ['class']
|
|
113
|
+
}], hostId: [{
|
|
114
|
+
type: HostBinding,
|
|
115
|
+
args: ['attr.id']
|
|
116
|
+
}] } });
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Generated bundle index. Do not edit.
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
export { ImageComponent };
|
|
123
|
+
//# sourceMappingURL=frame-kit-ui-ng-core-image.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-kit-ui-ng-core-image.mjs","sources":["../../../../packages/ui-ng/core/image/image.component.ts","../../../../packages/ui-ng/core/image/image.component.html","../../../../packages/ui-ng/core/image/frame-kit-ui-ng-core-image.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n HostBinding,\n input,\n isDevMode,\n output,\n signal,\n} from '@angular/core';\n\nimport type {\n ImageCrossOrigin,\n ImageDecoding,\n ImageFetchPriority,\n ImageFit,\n ImageLoading,\n ImageRadius,\n} from './image.types';\n\n@Component({\n selector: 'fk-image',\n standalone: true,\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './image.component.html',\n styleUrl: './image.component.scss',\n})\nexport class ImageComponent {\n // ===== REQUIRED =====\n /** URL of the image to display. */\n readonly src = input.required<string>();\n /** Accessible alternative text describing the image. */\n readonly alt = input.required<string>();\n\n // ===== DIMENSION MODE =====\n /** When true, the image expands to fill its positioned parent; `width` and `height` are ignored. */\n readonly fill = input<boolean>(false);\n /** Explicit pixel width; required when `fill` is false to prevent layout shift. */\n readonly width = input<number | null>(null);\n /** Explicit pixel height; required when `fill` is false to prevent layout shift. */\n readonly height = input<number | null>(null);\n\n // ===== IMAGE BEHAVIOR =====\n /** `srcset` attribute value for responsive image resolution switching. */\n readonly srcSet = input<string | null>(null);\n /** `sizes` attribute value used alongside `srcSet` for viewport-aware resolution selection. */\n readonly sizes = input<string | null>(null);\n /** Browser loading strategy — `\"lazy\"` defers off-screen images. */\n readonly loading = input<ImageLoading>('lazy');\n /** Browser decode hint — `\"async\"` avoids blocking the main thread. */\n readonly decoding = input<ImageDecoding>('async');\n /** Fetch priority hint for the browser's preload scanner. */\n readonly fetchPriority = input<ImageFetchPriority>('auto');\n /** Cross-origin policy for the image request. */\n readonly crossOrigin = input<ImageCrossOrigin | null>(null);\n /** `referrerpolicy` attribute value forwarded to the underlying `<img>` element. */\n readonly referrerPolicy = input<string | null>(null);\n\n // ===== PRESENTATION =====\n /** `object-fit` strategy when the image is displayed inside a fixed container. */\n readonly fit = input<ImageFit>('cover');\n /** `object-position` value controlling focal point within the container. */\n readonly position = input<string>('center');\n /** Corner radius token applied to the image. */\n readonly radius = input<ImageRadius>('none');\n /** Locks the host to a specific aspect ratio expressed as a number (w/h) or CSS string. */\n readonly aspectRatio = input<number | string | null>(null);\n\n // ===== FALLBACK =====\n /** Fallback image URL displayed automatically when the primary `src` fails to load. */\n readonly fallbackSrc = input<string | null>(null);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n\n // ===== EVENTS =====\n /** Fires when the image has finished loading successfully. */\n readonly imageLoad = output<void>();\n /** Fires when the image fails to load; switches to `fallbackSrc` if provided. */\n readonly imageError = output<void>();\n\n // ===== INTERNAL STATE =====\n readonly currentSrc = computed(() => {\n const fallback = this.fallbackSrc();\n\n if (this.hasError() && fallback) {\n return fallback;\n }\n\n return this.src();\n });\n\n readonly hasError = signal<boolean>(false);\n\n constructor() {\n // Reset error state when src changes\n effect(() => {\n this.src();\n this.hasError.set(false);\n });\n }\n\n // Dev-mode dimension warning\n private readonly dimensionWarning = computed(() => {\n if (isDevMode() && !this.fill() && !this.width() && !this.height()) {\n console.warn(\n `[fk-image] width and height are required when fill is not set. ` +\n `Provide explicit dimensions to prevent layout shift.`,\n );\n }\n });\n\n // ===== COMPUTED =====\n\n readonly classes = computed(() => {\n // Trigger dimension warning in dev mode\n this.dimensionWarning();\n\n return [\n 'fk-image',\n this.fill() ? 'fk-image--fill' : '',\n `fk-image--radius-${this.radius()}`,\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 // ===== EVENT HANDLERS =====\n\n onLoad(): void {\n this.imageLoad.emit();\n }\n\n onError(): void {\n this.imageError.emit();\n\n if (this.fallbackSrc() && !this.hasError()) {\n this.hasError.set(true);\n }\n }\n}\n","<img\n class=\"fk-image__native\"\n [src]=\"currentSrc()\"\n [alt]=\"alt()\"\n [attr.width]=\"fill() ? null : width()\"\n [attr.height]=\"fill() ? null : height()\"\n [attr.srcset]=\"srcSet()\"\n [attr.sizes]=\"sizes()\"\n [attr.loading]=\"loading()\"\n [attr.decoding]=\"decoding()\"\n [attr.fetchpriority]=\"fetchPriority()\"\n [attr.crossorigin]=\"crossOrigin()\"\n [attr.referrerpolicy]=\"referrerPolicy()\"\n [style.object-fit]=\"fit()\"\n [style.object-position]=\"position()\"\n [style.aspect-ratio]=\"aspectRatio()\"\n (load)=\"onLoad()\"\n (error)=\"onError()\"\n/>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { input, output, computed, HostBinding, ChangeDetectionStrategy, Component, InjectionToken, Directive } from '@angular/core';
|
|
4
|
+
import { RouterLink } from '@angular/router';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Link Component
|
|
8
|
+
*
|
|
9
|
+
* A flexible, accessible link component that supports:
|
|
10
|
+
* - External and internal links
|
|
11
|
+
* - Angular Router integration
|
|
12
|
+
* - Variants, sizes, and underline styles
|
|
13
|
+
* - Analytics tracking via data attributes
|
|
14
|
+
* - Accessibility features (aria-label, keyboard navigation)
|
|
15
|
+
* - Icon support (leading/trailing)
|
|
16
|
+
* - Disabled and loading states
|
|
17
|
+
* - External link indicators
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```html
|
|
21
|
+
* <!-- Internal router link -->
|
|
22
|
+
* <fk-link routerLink="/dashboard">Dashboard</fk-link>
|
|
23
|
+
*
|
|
24
|
+
* <!-- External link with icon -->
|
|
25
|
+
* <fk-link href="https://example.com" [external]="true">
|
|
26
|
+
* Visit Site
|
|
27
|
+
* </fk-link>
|
|
28
|
+
*
|
|
29
|
+
* <!-- With analytics tracking -->
|
|
30
|
+
* <fk-link
|
|
31
|
+
* href="/products"
|
|
32
|
+
* trackingId="nav-products"
|
|
33
|
+
* trackingCategory="navigation"
|
|
34
|
+
* >
|
|
35
|
+
* Products
|
|
36
|
+
* </fk-link>
|
|
37
|
+
*
|
|
38
|
+
* <!-- Variants -->
|
|
39
|
+
* <fk-link variant="primary" href="/signup">Sign Up</fk-link>
|
|
40
|
+
* <fk-link variant="muted" href="/help">Help</fk-link>
|
|
41
|
+
* <fk-link variant="danger" href="/delete">Delete</fk-link>
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
class LinkComponent {
|
|
45
|
+
// ===== INPUTS =====
|
|
46
|
+
/**
|
|
47
|
+
* External href (for standard <a> tags)
|
|
48
|
+
* Use this for external links or when not using Angular Router
|
|
49
|
+
*/
|
|
50
|
+
href = input(null, ...(ngDevMode ? [{ debugName: "href" }] : /* istanbul ignore next */ []));
|
|
51
|
+
/**
|
|
52
|
+
* Angular Router link (for internal navigation)
|
|
53
|
+
* Use this for internal app navigation
|
|
54
|
+
*/
|
|
55
|
+
routerLink = input(null, ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
|
|
56
|
+
/**
|
|
57
|
+
* Query parameters object passed through to the underlying
|
|
58
|
+
* `routerLink` directive. Only meaningful when `routerLink` is set.
|
|
59
|
+
* Equivalent to `<a [routerLink]="..." [queryParams]="...">`.
|
|
60
|
+
*/
|
|
61
|
+
queryParams = input(null, ...(ngDevMode ? [{ debugName: "queryParams" }] : /* istanbul ignore next */ []));
|
|
62
|
+
/**
|
|
63
|
+
* Link variant (visual style)
|
|
64
|
+
* @default 'default'
|
|
65
|
+
*/
|
|
66
|
+
variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
67
|
+
/**
|
|
68
|
+
* Link size
|
|
69
|
+
* @default 'medium'
|
|
70
|
+
*/
|
|
71
|
+
size = input('medium', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
72
|
+
/**
|
|
73
|
+
* Underline behavior
|
|
74
|
+
* @default 'hover'
|
|
75
|
+
*/
|
|
76
|
+
underline = input('hover', ...(ngDevMode ? [{ debugName: "underline" }] : /* istanbul ignore next */ []));
|
|
77
|
+
/**
|
|
78
|
+
* Target attribute (e.g., '_blank', '_self')
|
|
79
|
+
* Automatically set to '_blank' if external is true
|
|
80
|
+
*/
|
|
81
|
+
target = input(null, ...(ngDevMode ? [{ debugName: "target" }] : /* istanbul ignore next */ []));
|
|
82
|
+
/**
|
|
83
|
+
* Rel attribute (e.g., 'noopener noreferrer')
|
|
84
|
+
* Automatically set to 'noopener noreferrer' for external links with target="_blank"
|
|
85
|
+
*/
|
|
86
|
+
rel = input(null, ...(ngDevMode ? [{ debugName: "rel" }] : /* istanbul ignore next */ []));
|
|
87
|
+
/**
|
|
88
|
+
* Mark as external link (shows external icon, sets target="_blank")
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
91
|
+
external = input(false, ...(ngDevMode ? [{ debugName: "external" }] : /* istanbul ignore next */ []));
|
|
92
|
+
/**
|
|
93
|
+
* Disable the link (prevents navigation, changes appearance)
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
97
|
+
/**
|
|
98
|
+
* Loading state (shows loading indicator, prevents navigation)
|
|
99
|
+
* @default false
|
|
100
|
+
*/
|
|
101
|
+
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
102
|
+
/**
|
|
103
|
+
* Download attribute (for file downloads)
|
|
104
|
+
*/
|
|
105
|
+
download = input(null, ...(ngDevMode ? [{ debugName: "download" }] : /* istanbul ignore next */ []));
|
|
106
|
+
/**
|
|
107
|
+
* Additional CSS classes
|
|
108
|
+
*/
|
|
109
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
110
|
+
/**
|
|
111
|
+
* ID attribute
|
|
112
|
+
*/
|
|
113
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
114
|
+
/**
|
|
115
|
+
* ARIA label (for accessibility)
|
|
116
|
+
*/
|
|
117
|
+
ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
118
|
+
/**
|
|
119
|
+
* ARIA described-by (for accessibility)
|
|
120
|
+
*/
|
|
121
|
+
ariaDescribedBy = input(null, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : /* istanbul ignore next */ []));
|
|
122
|
+
/**
|
|
123
|
+
* ARIA current (for indicating current page/location)
|
|
124
|
+
*/
|
|
125
|
+
ariaCurrent = input(null, ...(ngDevMode ? [{ debugName: "ariaCurrent" }] : /* istanbul ignore next */ []));
|
|
126
|
+
/**
|
|
127
|
+
* Role attribute (usually not needed for <a> tags)
|
|
128
|
+
*/
|
|
129
|
+
role = input(null, ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
|
|
130
|
+
/**
|
|
131
|
+
* Tab index (for keyboard navigation control)
|
|
132
|
+
*/
|
|
133
|
+
tabIndex = input(null, ...(ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []));
|
|
134
|
+
/**
|
|
135
|
+
* Analytics tracking ID
|
|
136
|
+
*/
|
|
137
|
+
trackingId = input(null, ...(ngDevMode ? [{ debugName: "trackingId" }] : /* istanbul ignore next */ []));
|
|
138
|
+
/**
|
|
139
|
+
* Analytics tracking category
|
|
140
|
+
*/
|
|
141
|
+
trackingCategory = input(null, ...(ngDevMode ? [{ debugName: "trackingCategory" }] : /* istanbul ignore next */ []));
|
|
142
|
+
/**
|
|
143
|
+
* Analytics tracking label
|
|
144
|
+
*/
|
|
145
|
+
trackingLabel = input(null, ...(ngDevMode ? [{ debugName: "trackingLabel" }] : /* istanbul ignore next */ []));
|
|
146
|
+
/**
|
|
147
|
+
* Custom data attributes (for analytics or other purposes)
|
|
148
|
+
*/
|
|
149
|
+
dataAttributes = input({}, ...(ngDevMode ? [{ debugName: "dataAttributes" }] : /* istanbul ignore next */ []));
|
|
150
|
+
// ===== OUTPUTS =====
|
|
151
|
+
/**
|
|
152
|
+
* Click event (emitted when link is clicked)
|
|
153
|
+
* Note: Not emitted if disabled or loading
|
|
154
|
+
*/
|
|
155
|
+
clicked = output();
|
|
156
|
+
// ===== COMPUTED PROPERTIES =====
|
|
157
|
+
/**
|
|
158
|
+
* Computed: is this an external link?
|
|
159
|
+
*/
|
|
160
|
+
isExternalLink = computed(() => {
|
|
161
|
+
if (this.external())
|
|
162
|
+
return true;
|
|
163
|
+
const href = this.href();
|
|
164
|
+
if (!href)
|
|
165
|
+
return false;
|
|
166
|
+
// Check if href starts with http://, https://, //, or mailto:, tel:, etc.
|
|
167
|
+
return /^(https?:)?\/\/|^mailto:|^tel:/i.test(href);
|
|
168
|
+
}, ...(ngDevMode ? [{ debugName: "isExternalLink" }] : /* istanbul ignore next */ []));
|
|
169
|
+
/**
|
|
170
|
+
* Computed: effective target attribute
|
|
171
|
+
*/
|
|
172
|
+
effectiveTarget = computed(() => {
|
|
173
|
+
if (this.target())
|
|
174
|
+
return this.target();
|
|
175
|
+
if (this.isExternalLink())
|
|
176
|
+
return '_blank';
|
|
177
|
+
return null;
|
|
178
|
+
}, ...(ngDevMode ? [{ debugName: "effectiveTarget" }] : /* istanbul ignore next */ []));
|
|
179
|
+
/**
|
|
180
|
+
* Computed: effective rel attribute
|
|
181
|
+
*/
|
|
182
|
+
effectiveRel = computed(() => {
|
|
183
|
+
if (this.rel())
|
|
184
|
+
return this.rel();
|
|
185
|
+
if (this.isExternalLink() && this.effectiveTarget() === '_blank') {
|
|
186
|
+
return 'noopener noreferrer';
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}, ...(ngDevMode ? [{ debugName: "effectiveRel" }] : /* istanbul ignore next */ []));
|
|
190
|
+
/**
|
|
191
|
+
* Computed: effective download attribute
|
|
192
|
+
*/
|
|
193
|
+
effectiveDownload = computed(() => {
|
|
194
|
+
const download = this.download();
|
|
195
|
+
if (download === null)
|
|
196
|
+
return null;
|
|
197
|
+
if (download === true)
|
|
198
|
+
return '';
|
|
199
|
+
return download;
|
|
200
|
+
}, ...(ngDevMode ? [{ debugName: "effectiveDownload" }] : /* istanbul ignore next */ []));
|
|
201
|
+
/**
|
|
202
|
+
* Computed: effective tab index
|
|
203
|
+
*/
|
|
204
|
+
effectiveTabIndex = computed(() => {
|
|
205
|
+
if (this.tabIndex() !== null)
|
|
206
|
+
return this.tabIndex();
|
|
207
|
+
if (this.disabled())
|
|
208
|
+
return -1;
|
|
209
|
+
return null;
|
|
210
|
+
}, ...(ngDevMode ? [{ debugName: "effectiveTabIndex" }] : /* istanbul ignore next */ []));
|
|
211
|
+
/**
|
|
212
|
+
* Host classes — single source of truth
|
|
213
|
+
*/
|
|
214
|
+
classes = computed(() => {
|
|
215
|
+
return [
|
|
216
|
+
'fk-link',
|
|
217
|
+
`fk-link--${this.variant()}`,
|
|
218
|
+
`fk-link--${this.size()}`,
|
|
219
|
+
`fk-link--underline-${this.underline()}`,
|
|
220
|
+
this.disabled() ? 'fk-link--disabled' : '',
|
|
221
|
+
this.loading() ? 'fk-link--loading' : '',
|
|
222
|
+
this.isExternalLink() ? 'fk-link--external' : '',
|
|
223
|
+
this.className(),
|
|
224
|
+
]
|
|
225
|
+
.filter(Boolean)
|
|
226
|
+
.join(' ');
|
|
227
|
+
}, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
228
|
+
get hostClass() {
|
|
229
|
+
return this.classes();
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Computed: data attributes for analytics
|
|
233
|
+
*/
|
|
234
|
+
computedDataAttributes = computed(() => {
|
|
235
|
+
const attrs = { ...this.dataAttributes() };
|
|
236
|
+
const trackingId = this.trackingId();
|
|
237
|
+
if (trackingId) {
|
|
238
|
+
attrs['data-tracking-id'] = trackingId;
|
|
239
|
+
}
|
|
240
|
+
const trackingCategory = this.trackingCategory();
|
|
241
|
+
if (trackingCategory) {
|
|
242
|
+
attrs['data-tracking-category'] = trackingCategory;
|
|
243
|
+
}
|
|
244
|
+
const trackingLabel = this.trackingLabel();
|
|
245
|
+
if (trackingLabel) {
|
|
246
|
+
attrs['data-tracking-label'] = trackingLabel;
|
|
247
|
+
}
|
|
248
|
+
return attrs;
|
|
249
|
+
}, ...(ngDevMode ? [{ debugName: "computedDataAttributes" }] : /* istanbul ignore next */ []));
|
|
250
|
+
/**
|
|
251
|
+
* Handle click events
|
|
252
|
+
*/
|
|
253
|
+
handleClick(event) {
|
|
254
|
+
// Prevent navigation if disabled or loading
|
|
255
|
+
if (this.disabled() || this.loading()) {
|
|
256
|
+
event.preventDefault();
|
|
257
|
+
event.stopPropagation();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// Emit click event
|
|
261
|
+
this.clicked.emit(event);
|
|
262
|
+
// Track analytics (if tracking service is available)
|
|
263
|
+
this.trackClick();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Track click for analytics
|
|
267
|
+
* Override this method or connect to your analytics service
|
|
268
|
+
*/
|
|
269
|
+
trackClick() {
|
|
270
|
+
const trackingId = this.trackingId();
|
|
271
|
+
if (!trackingId)
|
|
272
|
+
return;
|
|
273
|
+
// Example: integrate with your analytics service
|
|
274
|
+
// this.analytics.track('link_click', {
|
|
275
|
+
// id: trackingId,
|
|
276
|
+
// category: this.trackingCategory(),
|
|
277
|
+
// label: this.trackingLabel(),
|
|
278
|
+
// });
|
|
279
|
+
// For now, log to console in dev mode
|
|
280
|
+
if (typeof window !== 'undefined' &&
|
|
281
|
+
typeof console !== 'undefined' &&
|
|
282
|
+
console.debug) {
|
|
283
|
+
console.debug('[fk-link] Analytics:', {
|
|
284
|
+
id: trackingId,
|
|
285
|
+
category: this.trackingCategory(),
|
|
286
|
+
label: this.trackingLabel(),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
291
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: LinkComponent, isStandalone: true, selector: "fk-link", inputs: { href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, queryParams: { classPropertyName: "queryParams", publicName: "queryParams", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, underline: { classPropertyName: "underline", publicName: "underline", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, rel: { classPropertyName: "rel", publicName: "rel", isSignal: true, isRequired: false, transformFunction: null }, external: { classPropertyName: "external", publicName: "external", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, download: { classPropertyName: "download", publicName: "download", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, ariaCurrent: { classPropertyName: "ariaCurrent", publicName: "ariaCurrent", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, trackingId: { classPropertyName: "trackingId", publicName: "trackingId", isSignal: true, isRequired: false, transformFunction: null }, trackingCategory: { classPropertyName: "trackingCategory", publicName: "trackingCategory", isSignal: true, isRequired: false, transformFunction: null }, trackingLabel: { classPropertyName: "trackingLabel", publicName: "trackingLabel", isSignal: true, isRequired: false, transformFunction: null }, dataAttributes: { classPropertyName: "dataAttributes", publicName: "dataAttributes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<!-- Stable projection point for default content + leading/trailing icons -->\n<ng-template #projected>\n <ng-content select=\"[fkLinkIconStart]\" />\n <span class=\"fk-link__text\">\n <ng-content></ng-content>\n </span>\n <ng-content select=\"[fkLinkIconEnd]\" />\n</ng-template>\n\n<!-- Loading indicator template -->\n<ng-template #loadingIndicator>\n <span class=\"fk-link__loading\" aria-hidden=\"true\">\n <span class=\"fk-link__spinner\"></span>\n </span>\n</ng-template>\n\n<!-- External link icon template -->\n<ng-template #externalIcon>\n <span class=\"fk-link__external-icon\" aria-hidden=\"true\">\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M12 8.66667V12.6667C12 13.0203 11.8595 13.3594 11.6095 13.6095C11.3594 13.8595 11.0203 14 10.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V5.33333C2 4.97971 2.14048 4.64057 2.39052 4.39052C2.64057 4.14048 2.97971 4 3.33333 4H7.33333M10 2H14M14 2V6M14 2L6.66667 9.33333\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n</ng-template>\n\n<!-- Router Link (Internal Navigation) -->\n@if (routerLink() && !href()) {\n <a\n [routerLink]=\"routerLink()\"\n [queryParams]=\"queryParams()\"\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n\n @if (isExternalLink()) {\n <ng-container [ngTemplateOutlet]=\"externalIcon\"></ng-container>\n }\n </a>\n}\n\n<!-- Standard Link (External or href-based) -->\n@if (href() && !routerLink()) {\n <a\n [href]=\"href()\"\n [target]=\"effectiveTarget()\"\n [rel]=\"effectiveRel()\"\n [download]=\"effectiveDownload()\"\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n\n @if (isExternalLink()) {\n <ng-container [ngTemplateOutlet]=\"externalIcon\"></ng-container>\n }\n </a>\n}\n\n<!-- Fallback: Button-style link (when no href or routerLink) -->\n@if (!href() && !routerLink()) {\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n </button>\n}\n", styles: [":host{display:inline-flex;align-items:center;gap:var(--fk-rhythm-1)}:host a,:host button{display:inline-flex;align-items:center;gap:var(--fk-rhythm-1);font-family:inherit;background:none;border:none;padding:0;margin:0;cursor:pointer;text-decoration:none;transition:color .15s ease,text-decoration .15s ease,opacity .15s ease;appearance:none;-webkit-appearance:none;-moz-appearance:none}:host a:focus-visible,:host button:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-link-focus-radius, 2px)}:host.fk-link--small{font-size:var(--fk-link-font-size-small);line-height:var(--fk-link-line-height-small)}:host.fk-link--medium{font-size:var(--fk-link-font-size-medium);line-height:var(--fk-link-line-height-medium)}:host.fk-link--large{font-size:var(--fk-link-font-size-large);line-height:var(--fk-link-line-height-large)}:host.fk-link--default a,:host.fk-link--default button{color:var(--fk-link-color-default)}:host.fk-link--default a:hover,:host.fk-link--default button:hover{color:var(--fk-link-color-default-hover)}:host.fk-link--primary a,:host.fk-link--primary button{color:var(--fk-link-color-primary)}:host.fk-link--primary a:hover,:host.fk-link--primary button:hover{color:var(--fk-link-color-primary-hover)}:host.fk-link--primary a:active,:host.fk-link--primary button:active{color:var(--fk-link-color-primary-active)}:host.fk-link--muted a,:host.fk-link--muted button{color:var(--fk-link-color-muted)}:host.fk-link--muted a:hover,:host.fk-link--muted button:hover{color:var(--fk-link-color-muted-hover)}:host.fk-link--danger a,:host.fk-link--danger button{color:var(--fk-link-color-danger)}:host.fk-link--danger a:hover,:host.fk-link--danger button:hover{color:var(--fk-link-color-danger-hover)}:host.fk-link--underline-none a,:host.fk-link--underline-none button{text-decoration:none}:host.fk-link--underline-hover a,:host.fk-link--underline-hover button{text-decoration:none}:host.fk-link--underline-hover a:hover,:host.fk-link--underline-hover button:hover{text-decoration:underline}:host.fk-link--underline-always a,:host.fk-link--underline-always button{text-decoration:underline}:host.fk-link--disabled{pointer-events:none}:host.fk-link--disabled a,:host.fk-link--disabled button{color:var(--fk-link-color-disabled);cursor:not-allowed;opacity:var(--fk-link-opacity-disabled, .6)}:host.fk-link--loading{pointer-events:none}:host.fk-link--loading a,:host.fk-link--loading button{opacity:var(--fk-link-opacity-loading, .7)}.fk-link__content{display:inline-flex;align-items:center;gap:var(--fk-link-icon-gap, .375rem)}.fk-link__text{display:inline}.fk-link__external-icon{display:inline-flex;align-items:center;flex-shrink:0;margin-left:var(--fk-rhythm-1)}.fk-link__external-icon svg{width:1em;height:1em}.fk-link__loading{display:inline-flex;align-items:center;margin-right:var(--fk-rhythm-1)}.fk-link__spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:fk-link-spin .75s linear infinite}@keyframes fk-link-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
292
|
+
}
|
|
293
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LinkComponent, decorators: [{
|
|
294
|
+
type: Component,
|
|
295
|
+
args: [{ selector: 'fk-link', standalone: true, imports: [RouterLink, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Stable projection point for default content + leading/trailing icons -->\n<ng-template #projected>\n <ng-content select=\"[fkLinkIconStart]\" />\n <span class=\"fk-link__text\">\n <ng-content></ng-content>\n </span>\n <ng-content select=\"[fkLinkIconEnd]\" />\n</ng-template>\n\n<!-- Loading indicator template -->\n<ng-template #loadingIndicator>\n <span class=\"fk-link__loading\" aria-hidden=\"true\">\n <span class=\"fk-link__spinner\"></span>\n </span>\n</ng-template>\n\n<!-- External link icon template -->\n<ng-template #externalIcon>\n <span class=\"fk-link__external-icon\" aria-hidden=\"true\">\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M12 8.66667V12.6667C12 13.0203 11.8595 13.3594 11.6095 13.6095C11.3594 13.8595 11.0203 14 10.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V5.33333C2 4.97971 2.14048 4.64057 2.39052 4.39052C2.64057 4.14048 2.97971 4 3.33333 4H7.33333M10 2H14M14 2V6M14 2L6.66667 9.33333\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n</ng-template>\n\n<!-- Router Link (Internal Navigation) -->\n@if (routerLink() && !href()) {\n <a\n [routerLink]=\"routerLink()\"\n [queryParams]=\"queryParams()\"\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n\n @if (isExternalLink()) {\n <ng-container [ngTemplateOutlet]=\"externalIcon\"></ng-container>\n }\n </a>\n}\n\n<!-- Standard Link (External or href-based) -->\n@if (href() && !routerLink()) {\n <a\n [href]=\"href()\"\n [target]=\"effectiveTarget()\"\n [rel]=\"effectiveRel()\"\n [download]=\"effectiveDownload()\"\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n\n @if (isExternalLink()) {\n <ng-container [ngTemplateOutlet]=\"externalIcon\"></ng-container>\n }\n </a>\n}\n\n<!-- Fallback: Button-style link (when no href or routerLink) -->\n@if (!href() && !routerLink()) {\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n </button>\n}\n", styles: [":host{display:inline-flex;align-items:center;gap:var(--fk-rhythm-1)}:host a,:host button{display:inline-flex;align-items:center;gap:var(--fk-rhythm-1);font-family:inherit;background:none;border:none;padding:0;margin:0;cursor:pointer;text-decoration:none;transition:color .15s ease,text-decoration .15s ease,opacity .15s ease;appearance:none;-webkit-appearance:none;-moz-appearance:none}:host a:focus-visible,:host button:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-link-focus-radius, 2px)}:host.fk-link--small{font-size:var(--fk-link-font-size-small);line-height:var(--fk-link-line-height-small)}:host.fk-link--medium{font-size:var(--fk-link-font-size-medium);line-height:var(--fk-link-line-height-medium)}:host.fk-link--large{font-size:var(--fk-link-font-size-large);line-height:var(--fk-link-line-height-large)}:host.fk-link--default a,:host.fk-link--default button{color:var(--fk-link-color-default)}:host.fk-link--default a:hover,:host.fk-link--default button:hover{color:var(--fk-link-color-default-hover)}:host.fk-link--primary a,:host.fk-link--primary button{color:var(--fk-link-color-primary)}:host.fk-link--primary a:hover,:host.fk-link--primary button:hover{color:var(--fk-link-color-primary-hover)}:host.fk-link--primary a:active,:host.fk-link--primary button:active{color:var(--fk-link-color-primary-active)}:host.fk-link--muted a,:host.fk-link--muted button{color:var(--fk-link-color-muted)}:host.fk-link--muted a:hover,:host.fk-link--muted button:hover{color:var(--fk-link-color-muted-hover)}:host.fk-link--danger a,:host.fk-link--danger button{color:var(--fk-link-color-danger)}:host.fk-link--danger a:hover,:host.fk-link--danger button:hover{color:var(--fk-link-color-danger-hover)}:host.fk-link--underline-none a,:host.fk-link--underline-none button{text-decoration:none}:host.fk-link--underline-hover a,:host.fk-link--underline-hover button{text-decoration:none}:host.fk-link--underline-hover a:hover,:host.fk-link--underline-hover button:hover{text-decoration:underline}:host.fk-link--underline-always a,:host.fk-link--underline-always button{text-decoration:underline}:host.fk-link--disabled{pointer-events:none}:host.fk-link--disabled a,:host.fk-link--disabled button{color:var(--fk-link-color-disabled);cursor:not-allowed;opacity:var(--fk-link-opacity-disabled, .6)}:host.fk-link--loading{pointer-events:none}:host.fk-link--loading a,:host.fk-link--loading button{opacity:var(--fk-link-opacity-loading, .7)}.fk-link__content{display:inline-flex;align-items:center;gap:var(--fk-link-icon-gap, .375rem)}.fk-link__text{display:inline}.fk-link__external-icon{display:inline-flex;align-items:center;flex-shrink:0;margin-left:var(--fk-rhythm-1)}.fk-link__external-icon svg{width:1em;height:1em}.fk-link__loading{display:inline-flex;align-items:center;margin-right:var(--fk-rhythm-1)}.fk-link__spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:fk-link-spin .75s linear infinite}@keyframes fk-link-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
296
|
+
}], propDecorators: { href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], queryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryParams", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], underline: [{ type: i0.Input, args: [{ isSignal: true, alias: "underline", required: false }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], rel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rel", required: false }] }], external: [{ type: i0.Input, args: [{ isSignal: true, alias: "external", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], download: [{ type: i0.Input, args: [{ isSignal: true, alias: "download", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], ariaCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaCurrent", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], trackingId: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackingId", required: false }] }], trackingCategory: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackingCategory", required: false }] }], trackingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackingLabel", required: false }] }], dataAttributes: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataAttributes", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }], hostClass: [{
|
|
297
|
+
type: HostBinding,
|
|
298
|
+
args: ['class']
|
|
299
|
+
}] } });
|
|
300
|
+
|
|
301
|
+
const FK_LINK_ICON_START = new InjectionToken('FK_LINK_ICON_START');
|
|
302
|
+
const FK_LINK_ICON_END = new InjectionToken('FK_LINK_ICON_END');
|
|
303
|
+
/**
|
|
304
|
+
* Marker directive for the leading icon slot inside `<fk-link>`.
|
|
305
|
+
* Mirrors the `[fkButtonIconStart]` pattern on `<fk-button>`.
|
|
306
|
+
*
|
|
307
|
+
* Apply to an `<fk-icon>` (or any element) projected as a child:
|
|
308
|
+
*
|
|
309
|
+
* ```html
|
|
310
|
+
* <fk-link variant="primary" (clicked)="download()">
|
|
311
|
+
* <fk-icon name="download" fkLinkIconStart />
|
|
312
|
+
* Download sample CSV
|
|
313
|
+
* </fk-link>
|
|
314
|
+
* ```
|
|
315
|
+
*
|
|
316
|
+
* The directive itself only marks the projection slot and applies
|
|
317
|
+
* `aria-hidden="true"` so screen readers don't announce decorative
|
|
318
|
+
* icons twice.
|
|
319
|
+
*/
|
|
320
|
+
class FkLinkIconStartDirective {
|
|
321
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLinkIconStartDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
322
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkLinkIconStartDirective, isStandalone: true, selector: "[fkLinkIconStart]", host: { attributes: { "aria-hidden": "true" } }, providers: [
|
|
323
|
+
{ provide: FK_LINK_ICON_START, useExisting: FkLinkIconStartDirective },
|
|
324
|
+
], ngImport: i0 });
|
|
325
|
+
}
|
|
326
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLinkIconStartDirective, decorators: [{
|
|
327
|
+
type: Directive,
|
|
328
|
+
args: [{
|
|
329
|
+
selector: '[fkLinkIconStart]',
|
|
330
|
+
standalone: true,
|
|
331
|
+
providers: [
|
|
332
|
+
{ provide: FK_LINK_ICON_START, useExisting: FkLinkIconStartDirective },
|
|
333
|
+
],
|
|
334
|
+
host: {
|
|
335
|
+
'aria-hidden': 'true',
|
|
336
|
+
},
|
|
337
|
+
}]
|
|
338
|
+
}] });
|
|
339
|
+
/**
|
|
340
|
+
* Marker directive for the trailing icon slot inside `<fk-link>`.
|
|
341
|
+
* Note: the auto-shown external-link icon (when `href` points
|
|
342
|
+
* off-site) renders separately and is not affected by this slot.
|
|
343
|
+
*/
|
|
344
|
+
class FkLinkIconEndDirective {
|
|
345
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLinkIconEndDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
346
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkLinkIconEndDirective, isStandalone: true, selector: "[fkLinkIconEnd]", host: { attributes: { "aria-hidden": "true" } }, providers: [
|
|
347
|
+
{ provide: FK_LINK_ICON_END, useExisting: FkLinkIconEndDirective },
|
|
348
|
+
], ngImport: i0 });
|
|
349
|
+
}
|
|
350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLinkIconEndDirective, decorators: [{
|
|
351
|
+
type: Directive,
|
|
352
|
+
args: [{
|
|
353
|
+
selector: '[fkLinkIconEnd]',
|
|
354
|
+
standalone: true,
|
|
355
|
+
providers: [
|
|
356
|
+
{ provide: FK_LINK_ICON_END, useExisting: FkLinkIconEndDirective },
|
|
357
|
+
],
|
|
358
|
+
host: {
|
|
359
|
+
'aria-hidden': 'true',
|
|
360
|
+
},
|
|
361
|
+
}]
|
|
362
|
+
}] });
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Generated bundle index. Do not edit.
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
export { FK_LINK_ICON_END, FK_LINK_ICON_START, FkLinkIconEndDirective, FkLinkIconStartDirective, LinkComponent };
|
|
369
|
+
//# sourceMappingURL=frame-kit-ui-ng-core-link.mjs.map
|