@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-link.mjs","sources":["../../../../packages/ui-ng/core/link/link.component.ts","../../../../packages/ui-ng/core/link/link.component.html","../../../../packages/ui-ng/core/link/link-icon.directive.ts","../../../../packages/ui-ng/core/link/frame-kit-ui-ng-core-link.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n output,\n} from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nimport type { LinkSize, LinkUnderline, LinkVariant } from './link.types';\n\n/**\n * Link Component\n *\n * A flexible, accessible link component that supports:\n * - External and internal links\n * - Angular Router integration\n * - Variants, sizes, and underline styles\n * - Analytics tracking via data attributes\n * - Accessibility features (aria-label, keyboard navigation)\n * - Icon support (leading/trailing)\n * - Disabled and loading states\n * - External link indicators\n *\n * @example\n * ```html\n * <!-- Internal router link -->\n * <fk-link routerLink=\"/dashboard\">Dashboard</fk-link>\n *\n * <!-- External link with icon -->\n * <fk-link href=\"https://example.com\" [external]=\"true\">\n * Visit Site\n * </fk-link>\n *\n * <!-- With analytics tracking -->\n * <fk-link\n * href=\"/products\"\n * trackingId=\"nav-products\"\n * trackingCategory=\"navigation\"\n * >\n * Products\n * </fk-link>\n *\n * <!-- Variants -->\n * <fk-link variant=\"primary\" href=\"/signup\">Sign Up</fk-link>\n * <fk-link variant=\"muted\" href=\"/help\">Help</fk-link>\n * <fk-link variant=\"danger\" href=\"/delete\">Delete</fk-link>\n * ```\n */\n@Component({\n selector: 'fk-link',\n standalone: true,\n imports: [RouterLink, NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: './link.component.scss',\n templateUrl: './link.component.html',\n})\nexport class LinkComponent {\n // ===== INPUTS =====\n\n /**\n * External href (for standard <a> tags)\n * Use this for external links or when not using Angular Router\n */\n readonly href = input<string | null>(null);\n\n /**\n * Angular Router link (for internal navigation)\n * Use this for internal app navigation\n */\n readonly routerLink = input<string | string[] | null>(null);\n\n /**\n * Query parameters object passed through to the underlying\n * `routerLink` directive. Only meaningful when `routerLink` is set.\n * Equivalent to `<a [routerLink]=\"...\" [queryParams]=\"...\">`.\n */\n readonly queryParams = input<Record<\n string,\n string | number | boolean | null | undefined\n > | null>(null);\n\n /**\n * Link variant (visual style)\n * @default 'default'\n */\n readonly variant = input<LinkVariant>('default');\n\n /**\n * Link size\n * @default 'medium'\n */\n readonly size = input<LinkSize>('medium');\n\n /**\n * Underline behavior\n * @default 'hover'\n */\n readonly underline = input<LinkUnderline>('hover');\n\n /**\n * Target attribute (e.g., '_blank', '_self')\n * Automatically set to '_blank' if external is true\n */\n readonly target = input<string | null>(null);\n\n /**\n * Rel attribute (e.g., 'noopener noreferrer')\n * Automatically set to 'noopener noreferrer' for external links with target=\"_blank\"\n */\n readonly rel = input<string | null>(null);\n\n /**\n * Mark as external link (shows external icon, sets target=\"_blank\")\n * @default false\n */\n readonly external = input<boolean>(false);\n\n /**\n * Disable the link (prevents navigation, changes appearance)\n * @default false\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Loading state (shows loading indicator, prevents navigation)\n * @default false\n */\n readonly loading = input<boolean>(false);\n\n /**\n * Download attribute (for file downloads)\n */\n readonly download = input<string | boolean | null>(null);\n\n /**\n * Additional CSS classes\n */\n readonly className = input<string>('');\n\n /**\n * ID attribute\n */\n readonly id = input<string | null>(null);\n\n /**\n * ARIA label (for accessibility)\n */\n readonly ariaLabel = input<string | null>(null);\n\n /**\n * ARIA described-by (for accessibility)\n */\n readonly ariaDescribedBy = input<string | null>(null);\n\n /**\n * ARIA current (for indicating current page/location)\n */\n readonly ariaCurrent = input<\n 'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false' | null\n >(null);\n\n /**\n * Role attribute (usually not needed for <a> tags)\n */\n readonly role = input<string | null>(null);\n\n /**\n * Tab index (for keyboard navigation control)\n */\n readonly tabIndex = input<number | null>(null);\n\n /**\n * Analytics tracking ID\n */\n readonly trackingId = input<string | null>(null);\n\n /**\n * Analytics tracking category\n */\n readonly trackingCategory = input<string | null>(null);\n\n /**\n * Analytics tracking label\n */\n readonly trackingLabel = input<string | null>(null);\n\n /**\n * Custom data attributes (for analytics or other purposes)\n */\n readonly dataAttributes = input<Record<string, string>>({});\n\n // ===== OUTPUTS =====\n\n /**\n * Click event (emitted when link is clicked)\n * Note: Not emitted if disabled or loading\n */\n readonly clicked = output<MouseEvent>();\n\n // ===== COMPUTED PROPERTIES =====\n\n /**\n * Computed: is this an external link?\n */\n readonly isExternalLink = computed(() => {\n if (this.external()) return true;\n\n const href = this.href();\n\n if (!href) return false;\n\n // Check if href starts with http://, https://, //, or mailto:, tel:, etc.\n return /^(https?:)?\\/\\/|^mailto:|^tel:/i.test(href);\n });\n\n /**\n * Computed: effective target attribute\n */\n readonly effectiveTarget = computed(() => {\n if (this.target()) return this.target();\n if (this.isExternalLink()) return '_blank';\n\n return null;\n });\n\n /**\n * Computed: effective rel attribute\n */\n readonly effectiveRel = computed(() => {\n if (this.rel()) return this.rel();\n\n if (this.isExternalLink() && this.effectiveTarget() === '_blank') {\n return 'noopener noreferrer';\n }\n\n return null;\n });\n\n /**\n * Computed: effective download attribute\n */\n readonly effectiveDownload = computed(() => {\n const download = this.download();\n\n if (download === null) return null;\n if (download === true) return '';\n\n return download;\n });\n\n /**\n * Computed: effective tab index\n */\n readonly effectiveTabIndex = computed(() => {\n if (this.tabIndex() !== null) return this.tabIndex();\n if (this.disabled()) return -1;\n\n return null;\n });\n\n /**\n * Host classes — single source of truth\n */\n readonly classes = computed(() => {\n return [\n 'fk-link',\n `fk-link--${this.variant()}`,\n `fk-link--${this.size()}`,\n `fk-link--underline-${this.underline()}`,\n this.disabled() ? 'fk-link--disabled' : '',\n this.loading() ? 'fk-link--loading' : '',\n this.isExternalLink() ? 'fk-link--external' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n /**\n * Computed: data attributes for analytics\n */\n readonly computedDataAttributes = computed(() => {\n const attrs: Record<string, string> = { ...this.dataAttributes() };\n\n const trackingId = this.trackingId();\n\n if (trackingId) {\n attrs['data-tracking-id'] = trackingId;\n }\n\n const trackingCategory = this.trackingCategory();\n\n if (trackingCategory) {\n attrs['data-tracking-category'] = trackingCategory;\n }\n\n const trackingLabel = this.trackingLabel();\n\n if (trackingLabel) {\n attrs['data-tracking-label'] = trackingLabel;\n }\n\n return attrs;\n });\n\n /**\n * Handle click events\n */\n handleClick(event: MouseEvent): void {\n // Prevent navigation if disabled or loading\n if (this.disabled() || this.loading()) {\n event.preventDefault();\n event.stopPropagation();\n\n return;\n }\n\n // Emit click event\n this.clicked.emit(event);\n\n // Track analytics (if tracking service is available)\n this.trackClick();\n }\n\n /**\n * Track click for analytics\n * Override this method or connect to your analytics service\n */\n private trackClick(): void {\n const trackingId = this.trackingId();\n\n if (!trackingId) return;\n\n // Example: integrate with your analytics service\n // this.analytics.track('link_click', {\n // id: trackingId,\n // category: this.trackingCategory(),\n // label: this.trackingLabel(),\n // });\n\n // For now, log to console in dev mode\n if (\n typeof window !== 'undefined' &&\n typeof console !== 'undefined' &&\n console.debug\n ) {\n console.debug('[fk-link] Analytics:', {\n id: trackingId,\n category: this.trackingCategory(),\n label: this.trackingLabel(),\n });\n }\n }\n}\n","<!-- 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","import { Directive, InjectionToken } from '@angular/core';\n\nexport const FK_LINK_ICON_START = new InjectionToken<FkLinkIconStartDirective>(\n 'FK_LINK_ICON_START',\n);\n\nexport const FK_LINK_ICON_END = new InjectionToken<FkLinkIconEndDirective>(\n 'FK_LINK_ICON_END',\n);\n\n/**\n * Marker directive for the leading icon slot inside `<fk-link>`.\n * Mirrors the `[fkButtonIconStart]` pattern on `<fk-button>`.\n *\n * Apply to an `<fk-icon>` (or any element) projected as a child:\n *\n * ```html\n * <fk-link variant=\"primary\" (clicked)=\"download()\">\n * <fk-icon name=\"download\" fkLinkIconStart />\n * Download sample CSV\n * </fk-link>\n * ```\n *\n * The directive itself only marks the projection slot and applies\n * `aria-hidden=\"true\"` so screen readers don't announce decorative\n * icons twice.\n */\n@Directive({\n selector: '[fkLinkIconStart]',\n standalone: true,\n providers: [\n { provide: FK_LINK_ICON_START, useExisting: FkLinkIconStartDirective },\n ],\n host: {\n 'aria-hidden': 'true',\n },\n})\nexport class FkLinkIconStartDirective {}\n\n/**\n * Marker directive for the trailing icon slot inside `<fk-link>`.\n * Note: the auto-shown external-link icon (when `href` points\n * off-site) renders separately and is not affected by this slot.\n */\n@Directive({\n selector: '[fkLinkIconEnd]',\n standalone: true,\n providers: [\n { provide: FK_LINK_ICON_END, useExisting: FkLinkIconEndDirective },\n ],\n host: {\n 'aria-hidden': 'true',\n },\n})\nexport class FkLinkIconEndDirective {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, computed, HostBinding, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
class SeparatorComponent {
|
|
5
|
+
// ===== INPUTS =====
|
|
6
|
+
/** Space above the separator line; any valid CSS length or token. */
|
|
7
|
+
marginTop = input(null, ...(ngDevMode ? [{ debugName: "marginTop" }] : /* istanbul ignore next */ []));
|
|
8
|
+
/** Space below the separator line; any valid CSS length or token. */
|
|
9
|
+
marginBottom = input(null, ...(ngDevMode ? [{ debugName: "marginBottom" }] : /* istanbul ignore next */ []));
|
|
10
|
+
/** Color of the separator line; any valid CSS color value or token. */
|
|
11
|
+
color = input(null, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
12
|
+
// ===== BASE PROPS =====
|
|
13
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
14
|
+
// ===== COMPUTED =====
|
|
15
|
+
classes = computed(() => {
|
|
16
|
+
return ['fk-separator', this.className()].filter(Boolean).join(' ');
|
|
17
|
+
}, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
18
|
+
get hostClass() {
|
|
19
|
+
return this.classes();
|
|
20
|
+
}
|
|
21
|
+
get hostMarginTop() {
|
|
22
|
+
return this.marginTop();
|
|
23
|
+
}
|
|
24
|
+
get hostMarginBottom() {
|
|
25
|
+
return this.marginBottom();
|
|
26
|
+
}
|
|
27
|
+
get hostColor() {
|
|
28
|
+
return this.color();
|
|
29
|
+
}
|
|
30
|
+
role = 'separator';
|
|
31
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SeparatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
32
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: SeparatorComponent, isStandalone: true, selector: "fk-separator", inputs: { marginTop: { classPropertyName: "marginTop", publicName: "marginTop", isSignal: true, isRequired: false, transformFunction: null }, marginBottom: { classPropertyName: "marginBottom", publicName: "marginBottom", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "style.--fk-separator-margin-top": "this.hostMarginTop", "style.--fk-separator-margin-bottom": "this.hostMarginBottom", "style.--fk-separator-color": "this.hostColor", "attr.role": "this.role" } }, ngImport: i0, template: '<hr />', isInline: true, styles: [":host{display:block;align-self:stretch;margin-top:var(--fk-separator-margin-top, var(--fk-rhythm-6, 1.5rem));margin-bottom:var(--fk-separator-margin-bottom, 0)}hr{border:none;border-top:var(--fk-separator-border-width, 1px) solid var(--fk-separator-color, var(--fk-color-border, #d9e2ee));margin:0;padding:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
33
|
+
}
|
|
34
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SeparatorComponent, decorators: [{
|
|
35
|
+
type: Component,
|
|
36
|
+
args: [{ selector: 'fk-separator', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: '<hr />', styles: [":host{display:block;align-self:stretch;margin-top:var(--fk-separator-margin-top, var(--fk-rhythm-6, 1.5rem));margin-bottom:var(--fk-separator-margin-bottom, 0)}hr{border:none;border-top:var(--fk-separator-border-width, 1px) solid var(--fk-separator-color, var(--fk-color-border, #d9e2ee));margin:0;padding:0}\n"] }]
|
|
37
|
+
}], propDecorators: { marginTop: [{ type: i0.Input, args: [{ isSignal: true, alias: "marginTop", required: false }] }], marginBottom: [{ type: i0.Input, args: [{ isSignal: true, alias: "marginBottom", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], hostClass: [{
|
|
38
|
+
type: HostBinding,
|
|
39
|
+
args: ['class']
|
|
40
|
+
}], hostMarginTop: [{
|
|
41
|
+
type: HostBinding,
|
|
42
|
+
args: ['style.--fk-separator-margin-top']
|
|
43
|
+
}], hostMarginBottom: [{
|
|
44
|
+
type: HostBinding,
|
|
45
|
+
args: ['style.--fk-separator-margin-bottom']
|
|
46
|
+
}], hostColor: [{
|
|
47
|
+
type: HostBinding,
|
|
48
|
+
args: ['style.--fk-separator-color']
|
|
49
|
+
}], role: [{
|
|
50
|
+
type: HostBinding,
|
|
51
|
+
args: ['attr.role']
|
|
52
|
+
}] } });
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generated bundle index. Do not edit.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
export { SeparatorComponent };
|
|
59
|
+
//# sourceMappingURL=frame-kit-ui-ng-core-separator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-kit-ui-ng-core-separator.mjs","sources":["../../../../packages/ui-ng/core/separator/separator.component.ts","../../../../packages/ui-ng/core/separator/frame-kit-ui-ng-core-separator.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\n\n@Component({\n selector: 'fk-separator',\n standalone: true,\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: './separator.component.scss',\n template: '<hr />',\n})\nexport class SeparatorComponent {\n // ===== INPUTS =====\n /** Space above the separator line; any valid CSS length or token. */\n readonly marginTop = input<string | null>(null);\n /** Space below the separator line; any valid CSS length or token. */\n readonly marginBottom = input<string | null>(null);\n /** Color of the separator line; any valid CSS color value or token. */\n readonly color = input<string | null>(null);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n\n // ===== COMPUTED =====\n readonly classes = computed(() => {\n return ['fk-separator', this.className()].filter(Boolean).join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n @HostBinding('style.--fk-separator-margin-top')\n get hostMarginTop() {\n return this.marginTop();\n }\n\n @HostBinding('style.--fk-separator-margin-bottom')\n get hostMarginBottom() {\n return this.marginBottom();\n }\n\n @HostBinding('style.--fk-separator-color')\n get hostColor() {\n return this.color();\n }\n\n @HostBinding('attr.role')\n readonly role = 'separator';\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;MAgBa,kBAAkB,CAAA;;;AAGpB,IAAA,SAAS,GAAG,KAAK,CAAgB,IAAI,gFAAC;;AAEtC,IAAA,YAAY,GAAG,KAAK,CAAgB,IAAI,mFAAC;;AAEzC,IAAA,KAAK,GAAG,KAAK,CAAgB,IAAI,4EAAC;;AAGlC,IAAA,SAAS,GAAG,KAAK,CAAS,EAAE,gFAAC;;AAG7B,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AAC/B,QAAA,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;AACrE,IAAA,CAAC,8EAAC;AAEF,IAAA,IACI,SAAS,GAAA;AACX,QAAA,OAAO,IAAI,CAAC,OAAO,EAAE;IACvB;AAEA,IAAA,IACI,aAAa,GAAA;AACf,QAAA,OAAO,IAAI,CAAC,SAAS,EAAE;IACzB;AAEA,IAAA,IACI,gBAAgB,GAAA;AAClB,QAAA,OAAO,IAAI,CAAC,YAAY,EAAE;IAC5B;AAEA,IAAA,IACI,SAAS,GAAA;AACX,QAAA,OAAO,IAAI,CAAC,KAAK,EAAE;IACrB;IAGS,IAAI,GAAG,WAAW;uGAtChB,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,kBAAkB,w1BAFnB,QAAQ,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,wTAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAEP,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAR9B,SAAS;+BACE,cAAc,EAAA,UAAA,EACZ,IAAI,EAAA,OAAA,EACP,EAAE,mBACM,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAErC,QAAQ,EAAA,MAAA,EAAA,CAAA,wTAAA,CAAA,EAAA;;sBAmBjB,WAAW;uBAAC,OAAO;;sBAKnB,WAAW;uBAAC,iCAAiC;;sBAK7C,WAAW;uBAAC,oCAAoC;;sBAKhD,WAAW;uBAAC,4BAA4B;;sBAKxC,WAAW;uBAAC,WAAW;;;ACrD1B;;AAEG;;;;"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { NgStyle, NgTemplateOutlet } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { input, viewChild, computed, HostBinding, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Text Component
|
|
7
|
+
*
|
|
8
|
+
* A flexible typography component that supports:
|
|
9
|
+
* - Semantic HTML elements (p, span, div, label, small)
|
|
10
|
+
* - Design token-based variants (type scale)
|
|
11
|
+
* - Tone/color variants
|
|
12
|
+
* - Text truncation (single-line and multi-line)
|
|
13
|
+
* - Text alignment
|
|
14
|
+
* - Font weight control
|
|
15
|
+
* - Accessibility features (visually hidden, ARIA)
|
|
16
|
+
* - Responsive typography
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```html
|
|
20
|
+
* <!-- Basic paragraph -->
|
|
21
|
+
* <fk-text>This is body text</fk-text>
|
|
22
|
+
*
|
|
23
|
+
* <!-- Small caption -->
|
|
24
|
+
* <fk-text variant="caption" tone="muted">
|
|
25
|
+
* Last updated: 2 hours ago
|
|
26
|
+
* </fk-text>
|
|
27
|
+
*
|
|
28
|
+
* <!-- Truncated text -->
|
|
29
|
+
* <fk-text [truncate]="true">
|
|
30
|
+
* This is a very long text that will be truncated...
|
|
31
|
+
* </fk-text>
|
|
32
|
+
*
|
|
33
|
+
* <!-- Multi-line clamp -->
|
|
34
|
+
* <fk-text [maxLines]="3">
|
|
35
|
+
* Long description that will be clamped to 3 lines with ellipsis
|
|
36
|
+
* </fk-text>
|
|
37
|
+
*
|
|
38
|
+
* <!-- Inline span -->
|
|
39
|
+
* <fk-text as="span" variant="small" tone="muted">
|
|
40
|
+
* Inline text
|
|
41
|
+
* </fk-text>
|
|
42
|
+
*
|
|
43
|
+
* <!-- Form label -->
|
|
44
|
+
* <fk-text as="label" variant="label" weight="semibold" htmlFor="email-input">
|
|
45
|
+
* Email Address
|
|
46
|
+
* </fk-text>
|
|
47
|
+
* <input id="email-input" type="email" />
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
class TextComponent {
|
|
51
|
+
// ===== INPUTS =====
|
|
52
|
+
/**
|
|
53
|
+
* Semantic HTML element to render
|
|
54
|
+
* @default 'p'
|
|
55
|
+
*/
|
|
56
|
+
as = input('p', ...(ngDevMode ? [{ debugName: "as" }] : /* istanbul ignore next */ []));
|
|
57
|
+
/**
|
|
58
|
+
* Typography variant (type scale)
|
|
59
|
+
* @default 'body'
|
|
60
|
+
*/
|
|
61
|
+
variant = input('body', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
62
|
+
/**
|
|
63
|
+
* Text tone/color
|
|
64
|
+
* @default 'default'
|
|
65
|
+
*/
|
|
66
|
+
tone = input('default', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
67
|
+
/**
|
|
68
|
+
* Text alignment
|
|
69
|
+
* @default 'inherit'
|
|
70
|
+
*/
|
|
71
|
+
align = input('inherit', ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
72
|
+
/**
|
|
73
|
+
* Font weight
|
|
74
|
+
* @default null (uses variant default)
|
|
75
|
+
*/
|
|
76
|
+
weight = input(null, ...(ngDevMode ? [{ debugName: "weight" }] : /* istanbul ignore next */ []));
|
|
77
|
+
/**
|
|
78
|
+
* Enable single-line truncation with ellipsis
|
|
79
|
+
* @default false
|
|
80
|
+
*/
|
|
81
|
+
truncate = input(false, ...(ngDevMode ? [{ debugName: "truncate" }] : /* istanbul ignore next */ []));
|
|
82
|
+
/**
|
|
83
|
+
* Maximum number of lines (multi-line clamp)
|
|
84
|
+
* @default null (no clamping)
|
|
85
|
+
*/
|
|
86
|
+
maxLines = input(null, ...(ngDevMode ? [{ debugName: "maxLines" }] : /* istanbul ignore next */ []));
|
|
87
|
+
/**
|
|
88
|
+
* Add bottom margin/gutter
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
91
|
+
gutterBottom = input(false, ...(ngDevMode ? [{ debugName: "gutterBottom" }] : /* istanbul ignore next */ []));
|
|
92
|
+
/**
|
|
93
|
+
* Italic text style
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
italic = input(false, ...(ngDevMode ? [{ debugName: "italic" }] : /* istanbul ignore next */ []));
|
|
97
|
+
/**
|
|
98
|
+
* Visually hide the text (screen reader only)
|
|
99
|
+
* @default false
|
|
100
|
+
*/
|
|
101
|
+
visuallyHidden = input(false, ...(ngDevMode ? [{ debugName: "visuallyHidden" }] : /* istanbul ignore next */ []));
|
|
102
|
+
/**
|
|
103
|
+
* Additional CSS classes
|
|
104
|
+
*/
|
|
105
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
106
|
+
/**
|
|
107
|
+
* ID attribute
|
|
108
|
+
*/
|
|
109
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
110
|
+
/**
|
|
111
|
+
* ARIA label (for accessibility)
|
|
112
|
+
*/
|
|
113
|
+
ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
114
|
+
/**
|
|
115
|
+
* ARIA described-by (for accessibility)
|
|
116
|
+
*/
|
|
117
|
+
ariaDescribedBy = input(null, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : /* istanbul ignore next */ []));
|
|
118
|
+
/**
|
|
119
|
+
* Role attribute (usually not needed)
|
|
120
|
+
*/
|
|
121
|
+
role = input(null, ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
|
|
122
|
+
/**
|
|
123
|
+
* For attribute (when using as="label" to associate with an input)
|
|
124
|
+
*/
|
|
125
|
+
htmlFor = input(null, ...(ngDevMode ? [{ debugName: "htmlFor" }] : /* istanbul ignore next */ []));
|
|
126
|
+
// ===== TEMPLATE REFS =====
|
|
127
|
+
pTemplate = viewChild.required('p');
|
|
128
|
+
spanTemplate = viewChild.required('span');
|
|
129
|
+
divTemplate = viewChild.required('div');
|
|
130
|
+
labelTemplate = viewChild.required('label');
|
|
131
|
+
smallTemplate = viewChild.required('small');
|
|
132
|
+
// ===== COMPUTED PROPERTIES =====
|
|
133
|
+
/**
|
|
134
|
+
* Host classes — single source of truth
|
|
135
|
+
*/
|
|
136
|
+
classes = computed(() => {
|
|
137
|
+
const weight = this.weight();
|
|
138
|
+
return [
|
|
139
|
+
'fk-text',
|
|
140
|
+
`fk-text--${this.variant()}`,
|
|
141
|
+
`fk-text--${this.tone()}`,
|
|
142
|
+
`fk-text--align-${this.align()}`,
|
|
143
|
+
weight ? `fk-text--weight-${weight}` : '',
|
|
144
|
+
this.truncate() ? 'fk-text--truncate' : '',
|
|
145
|
+
this.maxLines() !== null ? 'fk-text--line-clamp' : '',
|
|
146
|
+
this.gutterBottom() ? 'fk-text--gutter-bottom' : '',
|
|
147
|
+
this.italic() ? 'fk-text--italic' : '',
|
|
148
|
+
this.visuallyHidden() ? 'sr-only' : '',
|
|
149
|
+
this.className(),
|
|
150
|
+
]
|
|
151
|
+
.filter(Boolean)
|
|
152
|
+
.join(' ');
|
|
153
|
+
}, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
154
|
+
get hostClass() {
|
|
155
|
+
return this.classes();
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Computed: inline styles for line clamping
|
|
159
|
+
*/
|
|
160
|
+
lineClampStyle = computed(() => {
|
|
161
|
+
const maxLines = this.maxLines();
|
|
162
|
+
if (maxLines === null) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
'-webkit-line-clamp': maxLines.toString(),
|
|
167
|
+
};
|
|
168
|
+
}, ...(ngDevMode ? [{ debugName: "lineClampStyle" }] : /* istanbul ignore next */ []));
|
|
169
|
+
/**
|
|
170
|
+
* Computed: choose the correct element template
|
|
171
|
+
*/
|
|
172
|
+
template = computed(() => {
|
|
173
|
+
switch (this.as()) {
|
|
174
|
+
case 'p':
|
|
175
|
+
return this.pTemplate();
|
|
176
|
+
case 'span':
|
|
177
|
+
return this.spanTemplate();
|
|
178
|
+
case 'div':
|
|
179
|
+
return this.divTemplate();
|
|
180
|
+
case 'label':
|
|
181
|
+
return this.labelTemplate();
|
|
182
|
+
case 'small':
|
|
183
|
+
return this.smallTemplate();
|
|
184
|
+
default:
|
|
185
|
+
return this.pTemplate();
|
|
186
|
+
}
|
|
187
|
+
}, ...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
|
|
188
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
189
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: TextComponent, isStandalone: true, selector: "fk-text", inputs: { as: { classPropertyName: "as", publicName: "as", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, weight: { classPropertyName: "weight", publicName: "weight", isSignal: true, isRequired: false, transformFunction: null }, truncate: { classPropertyName: "truncate", publicName: "truncate", isSignal: true, isRequired: false, transformFunction: null }, maxLines: { classPropertyName: "maxLines", publicName: "maxLines", isSignal: true, isRequired: false, transformFunction: null }, gutterBottom: { classPropertyName: "gutterBottom", publicName: "gutterBottom", isSignal: true, isRequired: false, transformFunction: null }, italic: { classPropertyName: "italic", publicName: "italic", isSignal: true, isRequired: false, transformFunction: null }, visuallyHidden: { classPropertyName: "visuallyHidden", publicName: "visuallyHidden", 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 }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, htmlFor: { classPropertyName: "htmlFor", publicName: "htmlFor", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, viewQueries: [{ propertyName: "pTemplate", first: true, predicate: ["p"], descendants: true, isSignal: true }, { propertyName: "spanTemplate", first: true, predicate: ["span"], descendants: true, isSignal: true }, { propertyName: "divTemplate", first: true, predicate: ["div"], descendants: true, isSignal: true }, { propertyName: "labelTemplate", first: true, predicate: ["label"], descendants: true, isSignal: true }, { propertyName: "smallTemplate", first: true, predicate: ["small"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Stable projection point -->\n<ng-template #projected>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- Render chosen element template -->\n<ng-container\n [ngTemplateOutlet]=\"template()\"\n [ngTemplateOutletContext]=\"{ $implicit: projected }\"\n></ng-container>\n\n<!-- Element templates -->\n<ng-template #p let-content>\n <p\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </p>\n</ng-template>\n\n<ng-template #span let-content>\n <span\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </span>\n</ng-template>\n\n<ng-template #div let-content>\n <div\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </div>\n</ng-template>\n\n<ng-template #label let-content>\n <label\n [id]=\"id()\"\n [attr.for]=\"htmlFor()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </label>\n</ng-template>\n\n<ng-template #small let-content>\n <small\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </small>\n</ng-template>\n", styles: [":host p,:host span,:host div,:host label,:host small{margin:0;padding:0;font-size:inherit;font-weight:inherit;line-height:inherit;color:inherit}:host{display:block;margin-block:var(--fk-text-margin-block-start, var(--fk-rhythm-4, 1rem)) var(--fk-text-margin-block-end, 0)}:host(:first-child){margin-block-start:0}:host.fk-text--body{font-size:var(--fk-typography-body-font-size, .9375rem);font-weight:var(--fk-typography-body-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-typography-body-line-height, 1.5);letter-spacing:var(--fk-typography-body-letter-spacing, .01em)}:host.fk-text--lead{font-size:var(--fk-typography-lead-font-size, 1rem);font-weight:var(--fk-typography-lead-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-typography-lead-line-height, 1.4);letter-spacing:var(--fk-typography-lead-letter-spacing, 0em)}:host.fk-text--small{font-size:var(--fk-typography-small-font-size, .8125rem);font-weight:var(--fk-typography-small-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-typography-small-line-height, 1.4);letter-spacing:var(--fk-typography-small-letter-spacing, .01em)}:host.fk-text--caption{font-size:var(--fk-typography-caption-font-size, .6875rem);font-weight:var(--fk-typography-caption-font-weight, var(--fk-font-weight-medium, 500));line-height:var(--fk-typography-caption-line-height, 1.3);letter-spacing:var(--fk-typography-caption-letter-spacing, .02em)}:host.fk-text--label{font-size:var(--fk-typography-label-font-size, .8125rem);font-weight:var(--fk-typography-label-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-label-line-height, 1.25);letter-spacing:var(--fk-typography-label-letter-spacing, .01em)}:host.fk-text--code{font-family:var(--fk-typography-code-font-family, ui-monospace, \"SFMono-Regular\", \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace);font-size:var(--fk-typography-code-font-size, .8125rem);font-weight:var(--fk-typography-code-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-typography-code-line-height, 1.45);letter-spacing:var(--fk-typography-code-letter-spacing, 0em)}:host.fk-text--default{color:var(--fk-text-color-default, var(--fk-color-text, #1f2d3d))}:host.fk-text--muted{color:var(--fk-text-color-muted, var(--fk-color-muted, #8a98a8))}:host.fk-text--subtle{color:var(--fk-text-color-subtle, #9ca3af)}:host.fk-text--danger{color:var(--fk-text-color-danger, var(--fk-color-danger, #e02424))}:host.fk-text--success{color:var(--fk-text-color-success, var(--fk-color-success, #10b981))}:host.fk-text--align-inherit{text-align:inherit}:host.fk-text--align-start{text-align:start}:host.fk-text--align-center{text-align:center}:host.fk-text--align-end{text-align:end}:host.fk-text--align-justify{text-align:justify}:host.fk-text--weight-normal{font-weight:var(--fk-font-weight-normal, 400)}:host.fk-text--weight-medium{font-weight:var(--fk-font-weight-medium, 500)}:host.fk-text--weight-semibold{font-weight:var(--fk-font-weight-semibold, 600)}:host.fk-text--weight-bold{font-weight:var(--fk-font-weight-bold, 700)}:host.fk-text--truncate p,:host.fk-text--truncate span,:host.fk-text--truncate div,:host.fk-text--truncate label,:host.fk-text--truncate small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host.fk-text--line-clamp p,:host.fk-text--line-clamp span,:host.fk-text--line-clamp div,:host.fk-text--line-clamp label,:host.fk-text--line-clamp small{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}:host.fk-text--gutter-bottom{margin-bottom:var(--fk-typography-gutter-bottom, 1rem)}:host.fk-text--italic{font-style:italic}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
190
|
+
}
|
|
191
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TextComponent, decorators: [{
|
|
192
|
+
type: Component,
|
|
193
|
+
args: [{ selector: 'fk-text', standalone: true, imports: [NgStyle, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Stable projection point -->\n<ng-template #projected>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- Render chosen element template -->\n<ng-container\n [ngTemplateOutlet]=\"template()\"\n [ngTemplateOutletContext]=\"{ $implicit: projected }\"\n></ng-container>\n\n<!-- Element templates -->\n<ng-template #p let-content>\n <p\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </p>\n</ng-template>\n\n<ng-template #span let-content>\n <span\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </span>\n</ng-template>\n\n<ng-template #div let-content>\n <div\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </div>\n</ng-template>\n\n<ng-template #label let-content>\n <label\n [id]=\"id()\"\n [attr.for]=\"htmlFor()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </label>\n</ng-template>\n\n<ng-template #small let-content>\n <small\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </small>\n</ng-template>\n", styles: [":host p,:host span,:host div,:host label,:host small{margin:0;padding:0;font-size:inherit;font-weight:inherit;line-height:inherit;color:inherit}:host{display:block;margin-block:var(--fk-text-margin-block-start, var(--fk-rhythm-4, 1rem)) var(--fk-text-margin-block-end, 0)}:host(:first-child){margin-block-start:0}:host.fk-text--body{font-size:var(--fk-typography-body-font-size, .9375rem);font-weight:var(--fk-typography-body-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-typography-body-line-height, 1.5);letter-spacing:var(--fk-typography-body-letter-spacing, .01em)}:host.fk-text--lead{font-size:var(--fk-typography-lead-font-size, 1rem);font-weight:var(--fk-typography-lead-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-typography-lead-line-height, 1.4);letter-spacing:var(--fk-typography-lead-letter-spacing, 0em)}:host.fk-text--small{font-size:var(--fk-typography-small-font-size, .8125rem);font-weight:var(--fk-typography-small-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-typography-small-line-height, 1.4);letter-spacing:var(--fk-typography-small-letter-spacing, .01em)}:host.fk-text--caption{font-size:var(--fk-typography-caption-font-size, .6875rem);font-weight:var(--fk-typography-caption-font-weight, var(--fk-font-weight-medium, 500));line-height:var(--fk-typography-caption-line-height, 1.3);letter-spacing:var(--fk-typography-caption-letter-spacing, .02em)}:host.fk-text--label{font-size:var(--fk-typography-label-font-size, .8125rem);font-weight:var(--fk-typography-label-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-label-line-height, 1.25);letter-spacing:var(--fk-typography-label-letter-spacing, .01em)}:host.fk-text--code{font-family:var(--fk-typography-code-font-family, ui-monospace, \"SFMono-Regular\", \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace);font-size:var(--fk-typography-code-font-size, .8125rem);font-weight:var(--fk-typography-code-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-typography-code-line-height, 1.45);letter-spacing:var(--fk-typography-code-letter-spacing, 0em)}:host.fk-text--default{color:var(--fk-text-color-default, var(--fk-color-text, #1f2d3d))}:host.fk-text--muted{color:var(--fk-text-color-muted, var(--fk-color-muted, #8a98a8))}:host.fk-text--subtle{color:var(--fk-text-color-subtle, #9ca3af)}:host.fk-text--danger{color:var(--fk-text-color-danger, var(--fk-color-danger, #e02424))}:host.fk-text--success{color:var(--fk-text-color-success, var(--fk-color-success, #10b981))}:host.fk-text--align-inherit{text-align:inherit}:host.fk-text--align-start{text-align:start}:host.fk-text--align-center{text-align:center}:host.fk-text--align-end{text-align:end}:host.fk-text--align-justify{text-align:justify}:host.fk-text--weight-normal{font-weight:var(--fk-font-weight-normal, 400)}:host.fk-text--weight-medium{font-weight:var(--fk-font-weight-medium, 500)}:host.fk-text--weight-semibold{font-weight:var(--fk-font-weight-semibold, 600)}:host.fk-text--weight-bold{font-weight:var(--fk-font-weight-bold, 700)}:host.fk-text--truncate p,:host.fk-text--truncate span,:host.fk-text--truncate div,:host.fk-text--truncate label,:host.fk-text--truncate small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host.fk-text--line-clamp p,:host.fk-text--line-clamp span,:host.fk-text--line-clamp div,:host.fk-text--line-clamp label,:host.fk-text--line-clamp small{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}:host.fk-text--gutter-bottom{margin-bottom:var(--fk-typography-gutter-bottom, 1rem)}:host.fk-text--italic{font-style:italic}\n"] }]
|
|
194
|
+
}], propDecorators: { as: [{ type: i0.Input, args: [{ isSignal: true, alias: "as", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], weight: [{ type: i0.Input, args: [{ isSignal: true, alias: "weight", required: false }] }], truncate: [{ type: i0.Input, args: [{ isSignal: true, alias: "truncate", required: false }] }], maxLines: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLines", required: false }] }], gutterBottom: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterBottom", required: false }] }], italic: [{ type: i0.Input, args: [{ isSignal: true, alias: "italic", required: false }] }], visuallyHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "visuallyHidden", 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 }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], htmlFor: [{ type: i0.Input, args: [{ isSignal: true, alias: "htmlFor", required: false }] }], pTemplate: [{ type: i0.ViewChild, args: ['p', { isSignal: true }] }], spanTemplate: [{ type: i0.ViewChild, args: ['span', { isSignal: true }] }], divTemplate: [{ type: i0.ViewChild, args: ['div', { isSignal: true }] }], labelTemplate: [{ type: i0.ViewChild, args: ['label', { isSignal: true }] }], smallTemplate: [{ type: i0.ViewChild, args: ['small', { isSignal: true }] }], hostClass: [{
|
|
195
|
+
type: HostBinding,
|
|
196
|
+
args: ['class']
|
|
197
|
+
}] } });
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Generated bundle index. Do not edit.
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
export { TextComponent };
|
|
204
|
+
//# sourceMappingURL=frame-kit-ui-ng-core-text.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-kit-ui-ng-core-text.mjs","sources":["../../../../packages/ui-ng/core/text/text.component.ts","../../../../packages/ui-ng/core/text/text.component.html","../../../../packages/ui-ng/core/text/frame-kit-ui-ng-core-text.ts"],"sourcesContent":["import { NgStyle, NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n TemplateRef,\n viewChild,\n} from '@angular/core';\n\nimport type {\n TextAlign,\n TextTone,\n TextVariant,\n TextWeight,\n} from './text.types';\n\n/**\n * Text Component\n *\n * A flexible typography component that supports:\n * - Semantic HTML elements (p, span, div, label, small)\n * - Design token-based variants (type scale)\n * - Tone/color variants\n * - Text truncation (single-line and multi-line)\n * - Text alignment\n * - Font weight control\n * - Accessibility features (visually hidden, ARIA)\n * - Responsive typography\n *\n * @example\n * ```html\n * <!-- Basic paragraph -->\n * <fk-text>This is body text</fk-text>\n *\n * <!-- Small caption -->\n * <fk-text variant=\"caption\" tone=\"muted\">\n * Last updated: 2 hours ago\n * </fk-text>\n *\n * <!-- Truncated text -->\n * <fk-text [truncate]=\"true\">\n * This is a very long text that will be truncated...\n * </fk-text>\n *\n * <!-- Multi-line clamp -->\n * <fk-text [maxLines]=\"3\">\n * Long description that will be clamped to 3 lines with ellipsis\n * </fk-text>\n *\n * <!-- Inline span -->\n * <fk-text as=\"span\" variant=\"small\" tone=\"muted\">\n * Inline text\n * </fk-text>\n *\n * <!-- Form label -->\n * <fk-text as=\"label\" variant=\"label\" weight=\"semibold\" htmlFor=\"email-input\">\n * Email Address\n * </fk-text>\n * <input id=\"email-input\" type=\"email\" />\n * ```\n */\n@Component({\n selector: 'fk-text',\n standalone: true,\n imports: [NgStyle, NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: './text.component.scss',\n templateUrl: './text.component.html',\n})\nexport class TextComponent {\n // ===== INPUTS =====\n\n /**\n * Semantic HTML element to render\n * @default 'p'\n */\n readonly as = input<'p' | 'span' | 'div' | 'label' | 'small'>('p');\n\n /**\n * Typography variant (type scale)\n * @default 'body'\n */\n readonly variant = input<TextVariant>('body');\n\n /**\n * Text tone/color\n * @default 'default'\n */\n readonly tone = input<TextTone>('default');\n\n /**\n * Text alignment\n * @default 'inherit'\n */\n readonly align = input<TextAlign>('inherit');\n\n /**\n * Font weight\n * @default null (uses variant default)\n */\n readonly weight = input<TextWeight | null>(null);\n\n /**\n * Enable single-line truncation with ellipsis\n * @default false\n */\n readonly truncate = input<boolean>(false);\n\n /**\n * Maximum number of lines (multi-line clamp)\n * @default null (no clamping)\n */\n readonly maxLines = input<number | null>(null);\n\n /**\n * Add bottom margin/gutter\n * @default false\n */\n readonly gutterBottom = input<boolean>(false);\n\n /**\n * Italic text style\n * @default false\n */\n readonly italic = input<boolean>(false);\n\n /**\n * Visually hide the text (screen reader only)\n * @default false\n */\n readonly visuallyHidden = input<boolean>(false);\n\n /**\n * Additional CSS classes\n */\n readonly className = input<string>('');\n\n /**\n * ID attribute\n */\n readonly id = input<string | null>(null);\n\n /**\n * ARIA label (for accessibility)\n */\n readonly ariaLabel = input<string | null>(null);\n\n /**\n * ARIA described-by (for accessibility)\n */\n readonly ariaDescribedBy = input<string | null>(null);\n\n /**\n * Role attribute (usually not needed)\n */\n readonly role = input<string | null>(null);\n\n /**\n * For attribute (when using as=\"label\" to associate with an input)\n */\n readonly htmlFor = input<string | null>(null);\n\n // ===== TEMPLATE REFS =====\n\n private readonly pTemplate = viewChild.required<TemplateRef<unknown>>('p');\n private readonly spanTemplate =\n viewChild.required<TemplateRef<unknown>>('span');\n private readonly divTemplate =\n viewChild.required<TemplateRef<unknown>>('div');\n private readonly labelTemplate =\n viewChild.required<TemplateRef<unknown>>('label');\n private readonly smallTemplate =\n viewChild.required<TemplateRef<unknown>>('small');\n\n // ===== COMPUTED PROPERTIES =====\n\n /**\n * Host classes — single source of truth\n */\n readonly classes = computed(() => {\n const weight = this.weight();\n\n return [\n 'fk-text',\n `fk-text--${this.variant()}`,\n `fk-text--${this.tone()}`,\n `fk-text--align-${this.align()}`,\n weight ? `fk-text--weight-${weight}` : '',\n this.truncate() ? 'fk-text--truncate' : '',\n this.maxLines() !== null ? 'fk-text--line-clamp' : '',\n this.gutterBottom() ? 'fk-text--gutter-bottom' : '',\n this.italic() ? 'fk-text--italic' : '',\n this.visuallyHidden() ? 'sr-only' : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n /**\n * Computed: inline styles for line clamping\n */\n readonly lineClampStyle = computed(() => {\n const maxLines = this.maxLines();\n\n if (maxLines === null) {\n return null;\n }\n\n return {\n '-webkit-line-clamp': maxLines.toString(),\n };\n });\n\n /**\n * Computed: choose the correct element template\n */\n readonly template = computed(() => {\n switch (this.as()) {\n case 'p':\n return this.pTemplate();\n case 'span':\n return this.spanTemplate();\n case 'div':\n return this.divTemplate();\n case 'label':\n return this.labelTemplate();\n case 'small':\n return this.smallTemplate();\n default:\n return this.pTemplate();\n }\n });\n}\n","<!-- Stable projection point -->\n<ng-template #projected>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- Render chosen element template -->\n<ng-container\n [ngTemplateOutlet]=\"template()\"\n [ngTemplateOutletContext]=\"{ $implicit: projected }\"\n></ng-container>\n\n<!-- Element templates -->\n<ng-template #p let-content>\n <p\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </p>\n</ng-template>\n\n<ng-template #span let-content>\n <span\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </span>\n</ng-template>\n\n<ng-template #div let-content>\n <div\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </div>\n</ng-template>\n\n<ng-template #label let-content>\n <label\n [id]=\"id()\"\n [attr.for]=\"htmlFor()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </label>\n</ng-template>\n\n<ng-template #small let-content>\n <small\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.role]=\"role()\"\n [ngStyle]=\"lineClampStyle()\"\n >\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </small>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, ElementRef, PLATFORM_ID, input, output, Directive } from '@angular/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Infinite Scroll Directive
|
|
7
|
+
*
|
|
8
|
+
* Place on a sentinel element at the bottom of a scrollable list.
|
|
9
|
+
* Uses IntersectionObserver — no scroll event listeners, no throttling.
|
|
10
|
+
*
|
|
11
|
+
* When the sentinel enters the viewport, emits `scrolled`.
|
|
12
|
+
* Remove the sentinel from the DOM (via @if) to stop triggering.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```html
|
|
16
|
+
* @for (item of items(); track item.id) {
|
|
17
|
+
* <div>{{ item.name }}</div>
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* @if (hasMore()) {
|
|
21
|
+
* <div fkInfiniteScroll (scrolled)="loadNextPage()">
|
|
22
|
+
* @if (loading()) { <fk-loader /> }
|
|
23
|
+
* </div>
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
class FkInfiniteScrollDirective {
|
|
28
|
+
el = inject((ElementRef));
|
|
29
|
+
platformId = inject(PLATFORM_ID);
|
|
30
|
+
observer = null;
|
|
31
|
+
/** Visibility threshold (0–1). Default 0.1 = 10% visible triggers. */
|
|
32
|
+
threshold = input(0.1, ...(ngDevMode ? [{ debugName: "threshold" }] : /* istanbul ignore next */ []));
|
|
33
|
+
/** Root margin to expand/shrink the detection area. */
|
|
34
|
+
rootMargin = input('0px', ...(ngDevMode ? [{ debugName: "rootMargin" }] : /* istanbul ignore next */ []));
|
|
35
|
+
/** Emits when the sentinel element enters the viewport. */
|
|
36
|
+
scrolled = output();
|
|
37
|
+
ngAfterViewInit() {
|
|
38
|
+
// IntersectionObserver is browser-only — the SSR pass has no
|
|
39
|
+
// viewport to observe, so we skip setup entirely and let the
|
|
40
|
+
// browser pass wire it up after hydration.
|
|
41
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.observer = new IntersectionObserver(([entry]) => {
|
|
45
|
+
if (entry.isIntersecting) {
|
|
46
|
+
this.scrolled.emit();
|
|
47
|
+
}
|
|
48
|
+
}, {
|
|
49
|
+
threshold: this.threshold(),
|
|
50
|
+
rootMargin: this.rootMargin(),
|
|
51
|
+
});
|
|
52
|
+
this.observer.observe(this.el.nativeElement);
|
|
53
|
+
}
|
|
54
|
+
ngOnDestroy() {
|
|
55
|
+
this.observer?.disconnect();
|
|
56
|
+
this.observer = null;
|
|
57
|
+
}
|
|
58
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkInfiniteScrollDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
59
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: FkInfiniteScrollDirective, isStandalone: true, selector: "[fkInfiniteScroll]", inputs: { threshold: { classPropertyName: "threshold", publicName: "threshold", isSignal: true, isRequired: false, transformFunction: null }, rootMargin: { classPropertyName: "rootMargin", publicName: "rootMargin", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { scrolled: "scrolled" }, ngImport: i0 });
|
|
60
|
+
}
|
|
61
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkInfiniteScrollDirective, decorators: [{
|
|
62
|
+
type: Directive,
|
|
63
|
+
args: [{
|
|
64
|
+
selector: '[fkInfiniteScroll]',
|
|
65
|
+
standalone: true,
|
|
66
|
+
}]
|
|
67
|
+
}], propDecorators: { threshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "threshold", required: false }] }], rootMargin: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootMargin", required: false }] }], scrolled: [{ type: i0.Output, args: ["scrolled"] }] } });
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Generated bundle index. Do not edit.
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
export { FkInfiniteScrollDirective };
|
|
74
|
+
//# sourceMappingURL=frame-kit-ui-ng-directives-infinite-scroll.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-kit-ui-ng-directives-infinite-scroll.mjs","sources":["../../../../packages/ui-ng/directives/infinite-scroll/infinite-scroll.directive.ts","../../../../packages/ui-ng/directives/infinite-scroll/frame-kit-ui-ng-directives-infinite-scroll.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n AfterViewInit,\n Directive,\n ElementRef,\n inject,\n input,\n OnDestroy,\n output,\n PLATFORM_ID,\n} from '@angular/core';\n\n/**\n * Infinite Scroll Directive\n *\n * Place on a sentinel element at the bottom of a scrollable list.\n * Uses IntersectionObserver — no scroll event listeners, no throttling.\n *\n * When the sentinel enters the viewport, emits `scrolled`.\n * Remove the sentinel from the DOM (via @if) to stop triggering.\n *\n * @example\n * ```html\n * @for (item of items(); track item.id) {\n * <div>{{ item.name }}</div>\n * }\n *\n * @if (hasMore()) {\n * <div fkInfiniteScroll (scrolled)=\"loadNextPage()\">\n * @if (loading()) { <fk-loader /> }\n * </div>\n * }\n * ```\n */\n@Directive({\n selector: '[fkInfiniteScroll]',\n standalone: true,\n})\nexport class FkInfiniteScrollDirective implements AfterViewInit, OnDestroy {\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly platformId = inject(PLATFORM_ID);\n private observer: IntersectionObserver | null = null;\n\n /** Visibility threshold (0–1). Default 0.1 = 10% visible triggers. */\n readonly threshold = input(0.1);\n\n /** Root margin to expand/shrink the detection area. */\n readonly rootMargin = input('0px');\n\n /** Emits when the sentinel element enters the viewport. */\n readonly scrolled = output<void>();\n\n ngAfterViewInit(): void {\n // IntersectionObserver is browser-only — the SSR pass has no\n // viewport to observe, so we skip setup entirely and let the\n // browser pass wire it up after hydration.\n if (!isPlatformBrowser(this.platformId)) {\n return;\n }\n\n this.observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n this.scrolled.emit();\n }\n },\n {\n threshold: this.threshold(),\n rootMargin: this.rootMargin(),\n },\n );\n\n this.observer.observe(this.el.nativeElement);\n }\n\n ngOnDestroy(): void {\n this.observer?.disconnect();\n this.observer = null;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAYA;;;;;;;;;;;;;;;;;;;;;AAqBG;MAKU,yBAAyB,CAAA;AACnB,IAAA,EAAE,GAAG,MAAM,EAAC,UAAuB,EAAC;AACpC,IAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;IACzC,QAAQ,GAAgC,IAAI;;AAG3C,IAAA,SAAS,GAAG,KAAK,CAAC,GAAG,gFAAC;;AAGtB,IAAA,UAAU,GAAG,KAAK,CAAC,KAAK,iFAAC;;IAGzB,QAAQ,GAAG,MAAM,EAAQ;IAElC,eAAe,GAAA;;;;QAIb,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE;YACvC;QACF;QAEA,IAAI,CAAC,QAAQ,GAAG,IAAI,oBAAoB,CACtC,CAAC,CAAC,KAAK,CAAC,KAAI;AACV,YAAA,IAAI,KAAK,CAAC,cAAc,EAAE;AACxB,gBAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;YACtB;AACF,QAAA,CAAC,EACD;AACE,YAAA,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE;AAC3B,YAAA,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;AAC9B,SAAA,CACF;QAED,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC;IAC9C;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE;AAC3B,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;IACtB;uGAxCW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAzB,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,QAAA,EAAA,UAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAzB,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAJrC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,oBAAoB;AAC9B,oBAAA,UAAU,EAAE,IAAI;AACjB,iBAAA;;;ACrCD;;AAEG;;;;"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, inject, computed, Directive } from '@angular/core';
|
|
3
|
+
import { SpotlightService } from '@frame-kit/ui-ng/services/spotlight';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Marks an element to react when its spotlight key is active. By
|
|
7
|
+
* default the visual is a pulsing ring (`pulse` treatment) drawn by the
|
|
8
|
+
* `.fk-spotlight-target::after` pseudo-element. Pass `treatment="overlay"`
|
|
9
|
+
* for a static tinted overlay that's friendlier on wide elements.
|
|
10
|
+
*
|
|
11
|
+
* Many targets may share the same key — they all activate in sync, and
|
|
12
|
+
* each may pick its own treatment.
|
|
13
|
+
*/
|
|
14
|
+
class SpotlightTargetDirective {
|
|
15
|
+
/** Spotlight key this element reacts to; matches the key set on the trigger. */
|
|
16
|
+
fkSpotlightTarget = input.required(...(ngDevMode ? [{ debugName: "fkSpotlightTarget" }] : /* istanbul ignore next */ []));
|
|
17
|
+
/** Visual treatment applied when the spotlight is active — `"pulse"` animates a ring, `"overlay"` applies a tinted fade. */
|
|
18
|
+
treatment = input('pulse', ...(ngDevMode ? [{ debugName: "treatment" }] : /* istanbul ignore next */ []));
|
|
19
|
+
spotlight = inject(SpotlightService);
|
|
20
|
+
active = computed(() => this.spotlight.activeKey() === this.fkSpotlightTarget(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
21
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SpotlightTargetDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
22
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: SpotlightTargetDirective, isStandalone: true, selector: "[fkSpotlightTarget]", inputs: { fkSpotlightTarget: { classPropertyName: "fkSpotlightTarget", publicName: "fkSpotlightTarget", isSignal: true, isRequired: true, transformFunction: null }, treatment: { classPropertyName: "treatment", publicName: "treatment", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.fk-spotlight-target--active": "active()", "class.fk-spotlight-target--overlay": "treatment() === 'overlay'" }, classAttribute: "fk-spotlight-target" }, ngImport: i0 });
|
|
23
|
+
}
|
|
24
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SpotlightTargetDirective, decorators: [{
|
|
25
|
+
type: Directive,
|
|
26
|
+
args: [{
|
|
27
|
+
selector: '[fkSpotlightTarget]',
|
|
28
|
+
standalone: true,
|
|
29
|
+
host: {
|
|
30
|
+
class: 'fk-spotlight-target',
|
|
31
|
+
'[class.fk-spotlight-target--active]': 'active()',
|
|
32
|
+
'[class.fk-spotlight-target--overlay]': "treatment() === 'overlay'",
|
|
33
|
+
},
|
|
34
|
+
}]
|
|
35
|
+
}], propDecorators: { fkSpotlightTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "fkSpotlightTarget", required: true }] }], treatment: [{ type: i0.Input, args: [{ isSignal: true, alias: "treatment", required: false }] }] } });
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Marks an element as the hover/focus zone that activates a spotlight key.
|
|
39
|
+
*
|
|
40
|
+
* Apply to a container — e.g. a docs-panel section — so the activation
|
|
41
|
+
* area is generous, not just the button itself. Any number of triggers
|
|
42
|
+
* can broadcast the same key; the last enter wins.
|
|
43
|
+
*/
|
|
44
|
+
class SpotlightTriggerDirective {
|
|
45
|
+
/** Spotlight key this element activates on hover or focus. */
|
|
46
|
+
fkSpotlightTrigger = input.required(...(ngDevMode ? [{ debugName: "fkSpotlightTrigger" }] : /* istanbul ignore next */ []));
|
|
47
|
+
spotlight = inject(SpotlightService);
|
|
48
|
+
onEnter() {
|
|
49
|
+
this.spotlight.activate(this.fkSpotlightTrigger());
|
|
50
|
+
}
|
|
51
|
+
onLeave() {
|
|
52
|
+
this.spotlight.deactivate(this.fkSpotlightTrigger());
|
|
53
|
+
}
|
|
54
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SpotlightTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
55
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: SpotlightTriggerDirective, isStandalone: true, selector: "[fkSpotlightTrigger]", inputs: { fkSpotlightTrigger: { classPropertyName: "fkSpotlightTrigger", publicName: "fkSpotlightTrigger", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "mouseenter": "onEnter()", "mouseleave": "onLeave()", "focusin": "onEnter()", "focusout": "onLeave()" } }, ngImport: i0 });
|
|
56
|
+
}
|
|
57
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SpotlightTriggerDirective, decorators: [{
|
|
58
|
+
type: Directive,
|
|
59
|
+
args: [{
|
|
60
|
+
selector: '[fkSpotlightTrigger]',
|
|
61
|
+
standalone: true,
|
|
62
|
+
host: {
|
|
63
|
+
'(mouseenter)': 'onEnter()',
|
|
64
|
+
'(mouseleave)': 'onLeave()',
|
|
65
|
+
'(focusin)': 'onEnter()',
|
|
66
|
+
'(focusout)': 'onLeave()',
|
|
67
|
+
},
|
|
68
|
+
}]
|
|
69
|
+
}], propDecorators: { fkSpotlightTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "fkSpotlightTrigger", required: true }] }] } });
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generated bundle index. Do not edit.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
export { SpotlightTargetDirective, SpotlightTriggerDirective };
|
|
76
|
+
//# sourceMappingURL=frame-kit-ui-ng-directives-spotlight.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-kit-ui-ng-directives-spotlight.mjs","sources":["../../../../packages/ui-ng/directives/spotlight/spotlight-target.directive.ts","../../../../packages/ui-ng/directives/spotlight/spotlight-trigger.directive.ts","../../../../packages/ui-ng/directives/spotlight/frame-kit-ui-ng-directives-spotlight.ts"],"sourcesContent":["import { computed, Directive, inject, input } from '@angular/core';\n\nimport { SpotlightService } from '@frame-kit/ui-ng/services/spotlight';\n\n/**\n * Visual treatment applied while the spotlight is active.\n *\n * - `pulse` (default) — animated ring around the element. Best for\n * small, action-anchored targets like buttons.\n * - `overlay` — static tinted overlay over the element with a brief\n * fade in/out. Better for wide surfaces (list columns, panels) where\n * the pulsing animation would feel busy.\n */\nexport type SpotlightTreatment = 'pulse' | 'overlay';\n\n/**\n * Marks an element to react when its spotlight key is active. By\n * default the visual is a pulsing ring (`pulse` treatment) drawn by the\n * `.fk-spotlight-target::after` pseudo-element. Pass `treatment=\"overlay\"`\n * for a static tinted overlay that's friendlier on wide elements.\n *\n * Many targets may share the same key — they all activate in sync, and\n * each may pick its own treatment.\n */\n@Directive({\n selector: '[fkSpotlightTarget]',\n standalone: true,\n host: {\n class: 'fk-spotlight-target',\n '[class.fk-spotlight-target--active]': 'active()',\n '[class.fk-spotlight-target--overlay]': \"treatment() === 'overlay'\",\n },\n})\nexport class SpotlightTargetDirective {\n /** Spotlight key this element reacts to; matches the key set on the trigger. */\n readonly fkSpotlightTarget = input.required<string>();\n /** Visual treatment applied when the spotlight is active — `\"pulse\"` animates a ring, `\"overlay\"` applies a tinted fade. */\n readonly treatment = input<SpotlightTreatment>('pulse');\n\n private readonly spotlight = inject(SpotlightService);\n\n protected readonly active = computed(\n () => this.spotlight.activeKey() === this.fkSpotlightTarget(),\n );\n}\n","import { Directive, inject, input } from '@angular/core';\n\nimport { SpotlightService } from '@frame-kit/ui-ng/services/spotlight';\n\n/**\n * Marks an element as the hover/focus zone that activates a spotlight key.\n *\n * Apply to a container — e.g. a docs-panel section — so the activation\n * area is generous, not just the button itself. Any number of triggers\n * can broadcast the same key; the last enter wins.\n */\n@Directive({\n selector: '[fkSpotlightTrigger]',\n standalone: true,\n host: {\n '(mouseenter)': 'onEnter()',\n '(mouseleave)': 'onLeave()',\n '(focusin)': 'onEnter()',\n '(focusout)': 'onLeave()',\n },\n})\nexport class SpotlightTriggerDirective {\n /** Spotlight key this element activates on hover or focus. */\n readonly fkSpotlightTrigger = input.required<string>();\n private readonly spotlight = inject(SpotlightService);\n\n protected onEnter(): void {\n this.spotlight.activate(this.fkSpotlightTrigger());\n }\n\n protected onLeave(): void {\n this.spotlight.deactivate(this.fkSpotlightTrigger());\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|