{"version":3,"file":"fundamental-ngx-core-skeleton.mjs","sources":["../../../../libs/core/skeleton/components/skeleton.component.ts","../../../../libs/core/skeleton/components/skeleton.component.html","../../../../libs/core/skeleton/skeleton.module.ts","../../../../libs/core/skeleton/fundamental-ngx-core-skeleton.ts"],"sourcesContent":["import { coerceNumberProperty, NumberInput } from '@angular/cdk/coercion';\nimport { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core';\nimport { range } from '@fundamental-ngx/cdk/utils';\n\n/**\n * Type of skeleton visualization.\n * - `'rectangle'` - Rectangular skeleton with rounded corners\n * - `'circle'` - Circular skeleton\n * - `'text'` - Multi-line text skeleton\n * - `null` - No predefined shape, allows custom SVG projection\n */\nexport type SkeletonType = 'rectangle' | 'circle' | 'text' | null;\n\nlet skeletonUniqueId = 0;\n\n/**\n * Skeleton component for displaying loading placeholders with animated pulse effect.\n */\n@Component({\n    selector: 'fd-skeleton',\n    templateUrl: './skeleton.component.html',\n    styleUrl: './skeleton.component.scss',\n    encapsulation: ViewEncapsulation.None,\n    changeDetection: ChangeDetectionStrategy.OnPush,\n    host: {\n        class: 'fd-skeleton',\n        '[class.fd-skeleton--animated]': 'animated()',\n        '[style.width]': 'computedWidth()',\n        '[style.height]': 'computedHeight()'\n    },\n    imports: []\n})\nexport class SkeletonComponent {\n    /**\n     * Whether the skeleton displays animated pulse effect.\n     *\n     * @type {boolean}\n     * @default true\n     */\n    readonly animated = input(true);\n\n    /**\n     * Type of the skeleton visualization.\n     *\n     * Available types:\n     * - `'rectangle'` - Rectangular skeleton with rounded corners\n     * - `'circle'` - Circular skeleton\n     * - `'text'` - Multi-line text skeleton\n     * - `null` - No predefined shape, allows custom SVG projection\n     *\n     * @default null\n     */\n    readonly type = input<SkeletonType>(null);\n\n    /**\n     * Number of lines when type is `'text'`. Last line is 60% width if more than 1 line.\n     *\n     * Accepts numbers or coercible number values (e.g., `'3'` will be coerced to `3`).\n     *\n     * @type {number}\n     * @default 3\n     */\n    readonly textLines = input<number, NumberInput>(3, {\n        transform: coerceNumberProperty\n    });\n\n    /**\n     * Width of skeleton.\n     *\n     * Accepts any valid CSS width value (e.g., `'100px'`, `'50%'`, `'10rem'`).\n     * Auto-calculated for text and circle types if not provided.\n     *\n     * @type {string | null}\n     * @default null\n     */\n    readonly width = input<string | null>(null);\n\n    /**\n     * Height of skeleton.\n     *\n     * Accepts any valid CSS height value (e.g., `'100px'`, `'50%'`, `'10rem'`).\n     * Auto-calculated for text and circle types if not provided.\n     *\n     * @type {string | null}\n     * @default null\n     */\n    readonly height = input<string | null>(null);\n\n    /**\n     * Vertical spacing between text lines in pixels.\n     * Used for positioning lines in SVG and calculating total height.\n     * @hidden\n     */\n    protected readonly LINE_SPACING = 20;\n\n    /**\n     * Computed dimensions (width and height) based on skeleton type.\n     * @hidden\n     */\n    protected readonly dimensions = computed(() => {\n        const currentType = this.type();\n        const currentWidth = this.width();\n        const currentHeight = this.height();\n\n        if (currentType === 'text') {\n            const lines = Math.max(1, this.textLines());\n            return {\n                width: currentWidth ?? '100%',\n                height: currentHeight ?? (lines > 1 ? `${this.LINE_SPACING * lines}px` : '8px')\n            };\n        }\n\n        if (currentType === 'circle') {\n            // Make circle square by mirroring the provided dimension\n            if (!currentWidth && currentHeight) {\n                return { width: currentHeight, height: currentHeight };\n            }\n            if (!currentHeight && currentWidth) {\n                return { width: currentWidth, height: currentWidth };\n            }\n        }\n\n        // Default: pass through provided values\n        return {\n            width: currentWidth,\n            height: currentHeight\n        };\n    });\n\n    /**\n     * Computed width value from dimensions.\n     * @hidden\n     */\n    protected readonly computedWidth = computed(() => this.dimensions().width);\n\n    /**\n     * Computed height value from dimensions.\n     * @hidden\n     */\n    protected readonly computedHeight = computed(() => this.dimensions().height);\n\n    /**\n     * Unique ID for SVG mask element.\n     * @hidden\n     */\n    protected readonly svgMaskId = `fd-skeleton-${skeletonUniqueId++}`;\n\n    /**\n     * Computed array of line widths for text type.\n     * Last line is 60% width when there are multiple lines.\n     * @hidden\n     */\n    protected readonly textLineWidths = computed(() => {\n        const totalLines = this.textLines();\n        return range(totalLines, (i) => {\n            const isLastLine = i + 1 === totalLines;\n            const hasMultipleLines = i > 0;\n            return isLastLine && hasMultipleLines ? '60%' : '100%';\n        });\n    });\n}\n","<svg width=\"100%\" height=\"100%\" class=\"fd-skeleton__canvas\">\n    <defs>\n        <linearGradient id=\"skeletonGradient\">\n            <stop offset=\"0\" stop-color=\"var(--sapContent_Placeholderloading_Background)\"></stop>\n            <stop class=\"fd-skeleton__middle\">\n                <animate attributeName=\"offset\" values=\"0; 0; 0.5; 1; 1\" dur=\"2s\" repeatCount=\"indefinite\" />\n            </stop>\n            <stop offset=\"1\" stop-color=\"var(--sapContent_Placeholderloading_Background)\"></stop>\n        </linearGradient>\n        <mask [id]=\"svgMaskId\">\n            @if (type() === 'rectangle') {\n                <rect x=\"0\" y=\"0\" width=\"100%\" rx=\"4\" height=\"100%\"></rect>\n            }\n            @if (type() === 'circle') {\n                <circle cx=\"50%\" cy=\"50%\" r=\"50%\"></circle>\n            }\n            @if (type() === 'text') {\n                @for (width of textLineWidths(); track $index; let i = $index) {\n                    <rect x=\"0\" [attr.y]=\"i * LINE_SPACING\" rx=\"4\" height=\"8\" [attr.width]=\"width\"></rect>\n                }\n            }\n            <ng-content></ng-content>\n        </mask>\n    </defs>\n    <rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.mask]=\"'url(#' + svgMaskId + ')'\"></rect>\n</svg>\n","import { NgModule } from '@angular/core';\n\nimport { SkeletonComponent } from './components/skeleton.component';\n\n/**\n * @deprecated\n * Use direct `SkeletonComponent` import.\n */\n@NgModule({\n    imports: [SkeletonComponent],\n    exports: [SkeletonComponent]\n})\nexport class SkeletonModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}