@theseam/ui-common 1.0.2-beta.60 → 1.0.2-beta.63

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theseam-ui-common-file-input.mjs","sources":["../../../projects/ui-common/file-input/file-item.utils.ts","../../../projects/ui-common/file-input/file-input-validation.ts","../../../projects/ui-common/file-input/file-drop-zone.directive.ts","../../../projects/ui-common/file-input/file-input.component.ts","../../../projects/ui-common/file-input/file-input.component.html","../../../projects/ui-common/file-input/file-tile.component.ts","../../../projects/ui-common/file-input/file-tile.component.html","../../../projects/ui-common/file-input/file-field.component.ts","../../../projects/ui-common/file-input/file-field.component.html","../../../projects/ui-common/file-input/theseam-ui-common-file-input.ts"],"sourcesContent":["import {\n faFile,\n faFileExcel,\n faFileImage,\n faFilePdf,\n faFileWord,\n} from '@fortawesome/free-solid-svg-icons'\n\nimport { SeamIcon } from '@theseam/ui-common/icon'\n\nimport { SeamFileItem } from './file-item.models'\n\nexport function seamFileItemFromFile(file: File, id?: string): SeamFileItem {\n return {\n name: file.name,\n size: file.size,\n type: file.type,\n source: { kind: 'file', file },\n id,\n }\n}\n\nexport interface SeamFileItemFromUrlOptions {\n name?: string\n type?: string\n size?: number\n id?: string\n thumbnailUrl?: string\n}\n\nexport function seamFileItemFromUrl(\n url: string,\n opts: SeamFileItemFromUrlOptions = {},\n): SeamFileItem {\n return {\n name: opts.name ?? _basenameFromUrl(url) ?? url,\n size: opts.size,\n type: opts.type,\n source: { kind: 'url', url },\n id: opts.id,\n thumbnailUrl: opts.thumbnailUrl,\n }\n}\n\nfunction _basenameFromUrl(url: string): string | null {\n // Strip query string and fragment before pulling the final path segment.\n const hashIdx = url.indexOf('#')\n const noHash = hashIdx >= 0 ? url.slice(0, hashIdx) : url\n const queryIdx = noHash.indexOf('?')\n const path = queryIdx >= 0 ? noHash.slice(0, queryIdx) : noHash\n\n // Find the last path segment (after the last /)\n const lastSlash = path.lastIndexOf('/')\n if (lastSlash < 0) return null\n\n const basename = path.slice(lastSlash + 1)\n\n // If there's an actual filename after the last slash, decode and return it\n if (basename) {\n try {\n return decodeURIComponent(basename)\n } catch {\n return basename\n }\n }\n\n // No basename (e.g., trailing slash or just protocol/domain), return null\n return null\n}\n\n/**\n * Extracts native `File` objects from items whose source is `file`. Items\n * backed by a URL or a Blob are ignored. Useful for submit-side mapping\n * when the consumer only cares about newly-uploaded blobs.\n */\nexport function seamFilesFromItems(items: SeamFileItem[]): File[] {\n const files: File[] = []\n for (const item of items) {\n if (item.source.kind === 'file') {\n files.push(item.source.file)\n }\n }\n return files\n}\n\nconst WORD_MIMES = new Set<string>([\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n])\n\nconst EXCEL_MIMES = new Set<string>([\n 'application/vnd.ms-excel',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'text/csv',\n])\n\n/**\n * Maps a MIME type to a built-in SeamIcon. Returns a generic file icon for\n * unknown, empty, or missing types. Returns SeamIcon (not IconDefinition) so\n * the icon set can change later without a breaking signature change.\n */\nexport function iconForMime(type: string | undefined): SeamIcon {\n if (!type) return faFile\n if (type === 'application/pdf') return faFilePdf\n if (type.startsWith('image/')) return faFileImage\n if (WORD_MIMES.has(type)) return faFileWord\n if (EXCEL_MIMES.has(type)) return faFileExcel\n return faFile\n}\n","import { SeamFileRejection, SeamFileRejectionReason } from './file-item.models'\n\nexport interface FileValidationOptions {\n accept: string\n maxSize: number | null\n maxFiles: number | null\n}\n\nexport interface FileValidationResult {\n accepted: File[]\n rejected: SeamFileRejection[]\n}\n\n/**\n * Validates a batch of files against accept / maxSize / maxFiles.\n *\n * `accept` is parsed as the standard comma-separated list: `.ext`, `mime/*`,\n * or `mime/subtype`. Matching against `file.type` is case-insensitive; when\n * `file.type` is empty, extension tokens (`.csv`) are tried against the file\n * name. Each rejected file accumulates ALL applicable reasons rather than\n * short-circuiting, so consumers can display comprehensive errors.\n *\n * `maxFiles` caps the total accepted count; files past the cap are rejected\n * with reason `'count'` in arrival order.\n */\nexport function validateFiles(\n files: File[],\n opts: FileValidationOptions,\n): FileValidationResult {\n const acceptTokens = _parseAccept(opts.accept)\n const accepted: File[] = []\n const rejected: SeamFileRejection[] = []\n\n for (const file of files) {\n const reasons: SeamFileRejectionReason[] = []\n\n if (acceptTokens.length > 0 && !_matchesAccept(file, acceptTokens)) {\n reasons.push('type')\n }\n\n if (opts.maxSize !== null && file.size > opts.maxSize) {\n reasons.push('size')\n }\n\n if (reasons.length > 0) {\n rejected.push({ file, reasons })\n continue\n }\n\n if (opts.maxFiles !== null && accepted.length >= opts.maxFiles) {\n rejected.push({ file, reasons: ['count'] })\n continue\n }\n\n accepted.push(file)\n }\n\n return { accepted, rejected }\n}\n\nfunction _parseAccept(accept: string): string[] {\n return accept\n .split(',')\n .map((t) => t.trim().toLowerCase())\n .filter((t) => t.length > 0)\n}\n\nfunction _matchesAccept(file: File, tokens: string[]): boolean {\n const mime = file.type.toLowerCase()\n const name = file.name.toLowerCase()\n\n for (const token of tokens) {\n if (token.startsWith('.')) {\n if (name.endsWith(token)) return true\n continue\n }\n if (!mime) continue\n if (token.endsWith('/*')) {\n const prefix = token.slice(0, -1) // keep the slash\n if (mime.startsWith(prefix)) return true\n continue\n }\n if (token === mime) return true\n }\n return false\n}\n","import {\n booleanAttribute,\n computed,\n Directive,\n input,\n output,\n signal,\n} from '@angular/core'\n\nimport { SeamFileRejection } from './file-item.models'\nimport { validateFiles } from './file-input-validation'\n\n@Directive({\n selector: '[seamFileDropZone]',\n host: {\n '[class.seam-file-drop-zone--over]': '_isOver()',\n '(dragenter)': '_onDragEnter($event)',\n '(dragover)': '_onDragOver($event)',\n '(dragleave)': '_onDragLeave($event)',\n '(drop)': '_onDrop($event)',\n },\n})\nexport class TheSeamFileDropZoneDirective {\n readonly accept = input<string>('')\n readonly maxSize = input<number | null>(null)\n readonly maxFiles = input<number | null>(null)\n readonly disabled = input(false, { transform: booleanAttribute })\n\n readonly seamFileDrop = output<File[]>()\n readonly seamFileDropRejected = output<SeamFileRejection[]>()\n\n /** Counter-based dragenter/leave tracking to avoid child-element flicker. */\n private readonly _dragDepth = signal(0)\n\n protected readonly _isOver = computed(\n () => !this.disabled() && this._dragDepth() > 0,\n )\n\n protected _onDragEnter(event: DragEvent): void {\n if (this.disabled()) return\n event.preventDefault()\n this._dragDepth.update((n) => n + 1)\n }\n\n protected _onDragOver(event: DragEvent): void {\n if (this.disabled()) return\n // preventDefault is required for the drop event to fire.\n event.preventDefault()\n }\n\n protected _onDragLeave(event: DragEvent): void {\n if (this.disabled()) return\n this._dragDepth.update((n) => Math.max(0, n - 1))\n }\n\n protected _onDrop(event: DragEvent): void {\n if (this.disabled()) return\n event.preventDefault()\n this._dragDepth.set(0)\n\n const files = event.dataTransfer ? Array.from(event.dataTransfer.files) : []\n if (files.length === 0) return\n\n const { accepted, rejected } = validateFiles(files, {\n accept: this.accept(),\n maxSize: this.maxSize(),\n maxFiles: this.maxFiles(),\n })\n\n this.seamFileDrop.emit(accepted)\n if (rejected.length > 0) this.seamFileDropRejected.emit(rejected)\n }\n}\n","import {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core'\n\nimport { faUpload } from '@fortawesome/free-solid-svg-icons'\n\nimport { TheSeamIconModule } from '@theseam/ui-common/icon'\n\nimport { TheSeamFileDropZoneDirective } from './file-drop-zone.directive'\nimport { validateFiles } from './file-input-validation'\nimport { SeamFileRejection, SeamFileRejectionReason } from './file-item.models'\n\n@Component({\n selector: 'seam-file-input',\n templateUrl: './file-input.component.html',\n styleUrls: ['./file-input.component.scss'],\n imports: [TheSeamFileDropZoneDirective, TheSeamIconModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TheSeamFileInputComponent {\n readonly multiple = input(false, { transform: booleanAttribute })\n readonly accept = input<string>('')\n readonly maxSize = input<number | null>(null)\n readonly maxFiles = input<number | null>(null)\n readonly disabled = input(false, { transform: booleanAttribute })\n readonly hideErrors = input(false, { transform: booleanAttribute })\n readonly promptText = input<string>('Choose a file')\n readonly promptSuffix = input<string>('or drag it here')\n\n readonly filesAdded = output<File[]>()\n readonly rejected = output<SeamFileRejection[]>()\n\n protected readonly _faUpload = faUpload\n protected readonly _lastRejections = signal<SeamFileRejection[]>([])\n protected readonly _effectiveMaxFiles = computed(() => {\n const explicit = this.maxFiles()\n if (!this.multiple()) {\n return explicit !== null ? Math.min(explicit, 1) : 1\n }\n return explicit\n })\n protected readonly _errorMessage = computed(() =>\n _formatErrors(\n this._lastRejections(),\n this.maxSize(),\n this._effectiveMaxFiles(),\n ),\n )\n\n private readonly _nativeInput =\n viewChild.required<ElementRef<HTMLInputElement>>('native')\n\n _openPicker(): void {\n if (this.disabled()) return\n this._nativeInput().nativeElement.click()\n }\n\n protected _onFilesDropped(files: File[]): void {\n this._lastRejections.set([])\n if (files.length > 0) this.filesAdded.emit(files)\n }\n\n protected _onRejected(rejections: SeamFileRejection[]): void {\n this._lastRejections.set(rejections)\n this.rejected.emit(rejections)\n }\n\n protected _onNativeChange(event: Event): void {\n const nativeInput = event.target as HTMLInputElement\n const files = nativeInput.files ? Array.from(nativeInput.files) : []\n // Clear the value so the same file can be re-selected next time.\n nativeInput.value = ''\n\n if (files.length === 0) return\n\n const { accepted, rejected } = validateFiles(files, {\n accept: this.accept(),\n maxSize: this.maxSize(),\n maxFiles: this._effectiveMaxFiles(),\n })\n\n if (rejected.length > 0) {\n this._lastRejections.set(rejected)\n this.rejected.emit(rejected)\n } else {\n this._lastRejections.set([])\n }\n\n if (accepted.length > 0) this.filesAdded.emit(accepted)\n }\n}\n\nfunction _formatErrors(\n rejections: SeamFileRejection[],\n maxSize: number | null,\n maxFiles: number | null,\n): string | null {\n if (rejections.length === 0) return null\n const firstReason: SeamFileRejectionReason = rejections[0].reasons[0]\n switch (firstReason) {\n case 'type':\n return 'File type not accepted.'\n case 'size': {\n const mb = maxSize !== null ? (maxSize / (1024 * 1024)).toFixed(1) : null\n return mb\n ? `File exceeds the maximum size (${mb} MB).`\n : 'File exceeds the maximum size.'\n }\n case 'count':\n return maxFiles !== null\n ? `Only ${maxFiles} file(s) can be added.`\n : 'Too many files selected.'\n default:\n return 'File could not be accepted.'\n }\n}\n","<div\n class=\"seam-file-input__zone\"\n seamFileDropZone\n [accept]=\"accept()\"\n [maxSize]=\"maxSize()\"\n [maxFiles]=\"_effectiveMaxFiles()\"\n [disabled]=\"disabled()\"\n (seamFileDrop)=\"_onFilesDropped($event)\"\n (seamFileDropRejected)=\"_onRejected($event)\"\n role=\"button\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (click)=\"_openPicker()\"\n (keydown.enter)=\"_openPicker(); $event.preventDefault()\"\n (keydown.space)=\"_openPicker(); $event.preventDefault()\"\n>\n <span class=\"seam-file-input__icon\">\n <seam-icon [icon]=\"_faUpload\"></seam-icon>\n </span>\n <p class=\"seam-file-input__prompt\">\n <strong>{{ promptText() }}</strong> {{ promptSuffix() }}\n </p>\n</div>\n\n<input\n #native\n type=\"file\"\n hidden\n [multiple]=\"multiple()\"\n [attr.accept]=\"accept() || null\"\n (change)=\"_onNativeChange($event)\"\n/>\n\n@if (!hideErrors() && _errorMessage(); as msg) {\n <p class=\"seam-file-input__errors\">{{ msg }}</p>\n}\n","import {\n AfterViewInit,\n booleanAttribute,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core'\n\nimport { faTimes } from '@fortawesome/free-solid-svg-icons'\n\nimport { NgTemplateOutlet } from '@angular/common'\n\nimport { TheSeamIconModule } from '@theseam/ui-common/icon'\n\nimport { iconForMime } from './file-item.utils'\nimport { SeamFileItem, SeamFileTileVariant } from './file-item.models'\n\n@Component({\n selector: 'seam-file-tile',\n templateUrl: './file-tile.component.html',\n styleUrls: ['./file-tile.component.scss'],\n imports: [TheSeamIconModule, NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TheSeamFileTileComponent implements AfterViewInit {\n private readonly _cdr = inject(ChangeDetectorRef)\n readonly item = input.required<SeamFileItem>()\n readonly variant = input<SeamFileTileVariant>('row')\n readonly showName = input(true, { transform: booleanAttribute })\n readonly showMeta = input(true, { transform: booleanAttribute })\n readonly removable = input(true, { transform: booleanAttribute })\n readonly disabled = input(false, { transform: booleanAttribute })\n\n readonly remove = output<SeamFileItem>()\n readonly itemClick = output<SeamFileItem>()\n\n protected readonly _faTimes = faTimes\n\n protected readonly _mimeIcon = computed(() => iconForMime(this.item().type))\n protected readonly _metaLine = computed(() => _formatMeta(this.item()))\n protected readonly _showRemoveBtn = computed(\n () => this.removable() && !this.disabled(),\n )\n\n /** True when itemClick is observed AND the tile is not disabled. */\n protected readonly _isInteractive = computed(\n () => this._clickObserved() && !this.disabled(),\n )\n\n /**\n * Thumbnail URL for image items. Tracked across item changes so object\n * URLs are revoked when the item changes or the component is destroyed.\n */\n private _ownedObjectUrl: string | null = null\n private _pendingObjectUrl: string | null = null\n\n protected readonly _thumbUrl = computed(() => {\n const item = this.item()\n\n if (item.thumbnailUrl) return item.thumbnailUrl\n\n const isImage = _isImageMime(item.type)\n\n if (\n (item.source.kind === 'file' || item.source.kind === 'blob') &&\n isImage\n ) {\n const blob =\n item.source.kind === 'file' ? item.source.file : item.source.blob\n const url = URL.createObjectURL(blob)\n this._pendingObjectUrl = url\n return url\n }\n\n if (item.source.kind === 'url' && _looksLikeImage(item)) {\n return item.source.url\n }\n\n return null\n })\n\n /**\n * True once we detect that a consumer has wired (itemClick).\n * Detected in ngAfterViewInit — by that point the parent's template binding\n * has called subscribe() on the OutputEmitterRef, populating its internal\n * `listeners` array (Option A: access via internal field on Angular 20's\n * OutputEmitterRef). If the internal shape is absent, conservatively stays\n * false (opt-out default — no unwanted role=button on inert tiles).\n */\n protected readonly _clickObserved = signal(false)\n\n constructor() {\n // When _thumbUrl changes, revoke the previous owned URL (if any).\n effect(() => {\n // Read the signal so this effect re-runs when the thumbnail changes.\n this._thumbUrl()\n const previous = this._ownedObjectUrl\n this._ownedObjectUrl = this._pendingObjectUrl\n this._pendingObjectUrl = null\n if (previous && previous !== this._ownedObjectUrl) {\n URL.revokeObjectURL(previous)\n }\n })\n // Destroy cleanup: revoke the last owned URL.\n effect((onCleanup) => {\n onCleanup(() => {\n if (this._ownedObjectUrl) {\n URL.revokeObjectURL(this._ownedObjectUrl)\n this._ownedObjectUrl = null\n }\n })\n })\n }\n\n ngAfterViewInit(): void {\n // Detect whether (itemClick) is bound by checking the OutputEmitterRef's\n // internal listeners array. By ngAfterViewInit, the parent's template\n // bindings (including event bindings via subscribe()) have already been\n // applied during the parent's change-detection pass.\n //\n // Option A: access (this.itemClick as any).listeners which is the internal\n // array in Angular 20's OutputEmitterRef (null initially, an array once\n // subscribed). If the internal shape changes, we conservatively keep false.\n const ref = this.itemClick as unknown as { listeners?: unknown[] | null }\n const observed =\n ref.listeners !== undefined ? (ref.listeners?.length ?? 0) > 0 : false\n if (observed) {\n this._clickObserved.set(true)\n this._cdr.markForCheck()\n }\n }\n\n protected _onRemove(event: MouseEvent): void {\n event.stopPropagation()\n this.remove.emit(this.item())\n }\n\n protected _onBodyClick(): void {\n if (!this._clickObserved() || this.disabled()) return\n this.itemClick.emit(this.item())\n }\n\n protected _onBodyKey(event: KeyboardEvent): void {\n if (!this._clickObserved() || this.disabled()) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n this.itemClick.emit(this.item())\n }\n }\n}\n\nfunction _formatMeta(item: SeamFileItem): string {\n const parts: string[] = []\n if (item.size !== undefined) parts.push(_formatBytes(item.size))\n if (item.type) parts.push(item.type)\n return parts.join(' · ')\n}\n\nfunction _formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction _isImageMime(type: string | undefined): boolean {\n return !!type && type.toLowerCase().startsWith('image/')\n}\n\nconst IMAGE_EXT = /\\.(png|jpe?g|gif|webp|bmp|svg)(\\?|$)/i\n\nfunction _looksLikeImage(item: SeamFileItem): boolean {\n if (_isImageMime(item.type)) return true\n if (item.source.kind === 'url' && IMAGE_EXT.test(item.source.url)) return true\n return false\n}\n","<div\n class=\"seam-file-tile\"\n [class.seam-file-tile--row]=\"variant() === 'row'\"\n [class.seam-file-tile--preview]=\"variant() === 'preview'\"\n [class.seam-file-tile--clickable]=\"_isInteractive()\"\n>\n @if (variant() === 'row') {\n @if (_isInteractive()) {\n <div\n class=\"seam-file-tile__clickable-body\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"_onBodyClick()\"\n (keydown)=\"_onBodyKey($event)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent\"></ng-container>\n </div>\n } @else {\n <ng-container *ngTemplateOutlet=\"rowContent\"></ng-container>\n }\n\n @if (_showRemoveBtn()) {\n <button\n type=\"button\"\n class=\"seam-file-tile__remove\"\n (click)=\"_onRemove($event)\"\n title=\"Remove file\"\n >\n <seam-icon [icon]=\"_faTimes\"></seam-icon>\n </button>\n }\n } @else {\n @if (_showRemoveBtn()) {\n <button\n type=\"button\"\n class=\"seam-file-tile__remove seam-file-tile__remove--overlay\"\n (click)=\"_onRemove($event)\"\n title=\"Remove file\"\n >\n <seam-icon [icon]=\"_faTimes\"></seam-icon>\n </button>\n }\n @if (_isInteractive()) {\n <div\n class=\"seam-file-tile__clickable-body\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"_onBodyClick()\"\n (keydown)=\"_onBodyKey($event)\"\n >\n <ng-container *ngTemplateOutlet=\"previewContent\"></ng-container>\n </div>\n } @else {\n <ng-container *ngTemplateOutlet=\"previewContent\"></ng-container>\n }\n }\n</div>\n\n<ng-template #rowContent>\n <span\n class=\"seam-file-tile__visual\"\n [class.seam-file-tile__visual--image]=\"!!_thumbUrl()\"\n >\n @if (_thumbUrl(); as url) {\n <img class=\"seam-file-tile__thumb\" [src]=\"url\" [alt]=\"item().name\" />\n } @else {\n <seam-icon [icon]=\"_mimeIcon()\"></seam-icon>\n }\n </span>\n <div class=\"seam-file-tile__body\">\n <div class=\"seam-file-tile__name\" [attr.title]=\"item().name\">\n {{ item().name }}\n </div>\n @if (showMeta() && _metaLine(); as meta) {\n <div class=\"seam-file-tile__meta\">{{ meta }}</div>\n }\n </div>\n</ng-template>\n\n<ng-template #previewContent>\n <span class=\"seam-file-tile__preview-media\">\n @if (_thumbUrl(); as url) {\n <img class=\"seam-file-tile__thumb\" [src]=\"url\" [alt]=\"item().name\" />\n } @else {\n <span class=\"seam-file-tile__visual\">\n <seam-icon [icon]=\"_mimeIcon()\"></seam-icon>\n </span>\n }\n </span>\n @if (showName()) {\n <div class=\"seam-file-tile__preview-name\" [attr.title]=\"item().name\">\n {{ item().name }}\n </div>\n }\n</ng-template>\n","import {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n forwardRef,\n input,\n output,\n signal,\n} from '@angular/core'\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'\n\nimport { TheSeamFileInputComponent } from './file-input.component'\nimport { TheSeamFileTileComponent } from './file-tile.component'\nimport { SeamFileItem, SeamFileRejection } from './file-item.models'\nimport { seamFileItemFromFile } from './file-item.utils'\n\n@Component({\n selector: 'seam-file-field',\n templateUrl: './file-field.component.html',\n styleUrls: ['./file-field.component.scss'],\n imports: [TheSeamFileInputComponent, TheSeamFileTileComponent],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TheSeamFileFieldComponent),\n multi: true,\n },\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TheSeamFileFieldComponent implements ControlValueAccessor {\n readonly multiple = input(false, { transform: booleanAttribute })\n readonly accept = input<string>('')\n readonly maxSize = input<number | null>(null)\n readonly maxFiles = input<number | null>(null)\n readonly disabled = input(false, { transform: booleanAttribute })\n readonly previewMode = input(false, { transform: booleanAttribute })\n readonly showTileName = input(true, { transform: booleanAttribute })\n readonly promptText = input<string>('Choose a file')\n readonly promptSuffix = input<string>('or drag it here')\n readonly replaceText = input<string>('choose a different file')\n readonly hideErrors = input(false, { transform: booleanAttribute })\n\n readonly rejected = output<SeamFileRejection[]>()\n\n protected readonly _items = signal<SeamFileItem[]>([])\n protected readonly _cvaDisabled = signal(false)\n\n protected readonly _effectiveDisabled = computed(\n () => this.disabled() || this._cvaDisabled(),\n )\n\n protected readonly _hasFile = computed(\n () => !this.multiple() && this._items().length > 0,\n )\n\n protected readonly _remainingMaxFiles = computed(() => {\n const max = this.maxFiles()\n if (!this.multiple()) {\n // Single-mode: cap at 1 always. If explicit lower, honor.\n return max !== null ? Math.min(max, 1) : 1\n }\n if (max === null) return null\n return Math.max(0, max - this._items().length)\n })\n\n protected readonly _tileVariant = computed(() =>\n this.previewMode() ? 'preview' : 'row',\n )\n\n private _onChange: (value: SeamFileItem[]) => void = () => undefined\n private _onTouched: () => void = () => undefined\n\n writeValue(value: SeamFileItem[] | null): void {\n this._items.set(value ?? [])\n }\n\n registerOnChange(fn: (value: SeamFileItem[]) => void): void {\n this._onChange = fn\n }\n\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn\n }\n\n setDisabledState(isDisabled: boolean): void {\n this._cvaDisabled.set(isDisabled)\n }\n\n protected _onFilesAdded(files: File[]): void {\n if (files.length === 0) return\n const added = files.map((f) => seamFileItemFromFile(f))\n if (!this.multiple()) {\n this._items.set(added.slice(0, 1))\n } else {\n const remaining = this._remainingMaxFiles()\n const toAdd = remaining !== null ? added.slice(0, remaining) : added\n if (toAdd.length === 0) return\n this._items.update((prev) => [...prev, ...toAdd])\n }\n this._emit()\n }\n\n protected _onRejected(rejections: SeamFileRejection[]): void {\n this.rejected.emit(rejections)\n }\n\n protected _onTileRemove(item: SeamFileItem): void {\n this._items.update((prev) => prev.filter((i) => i !== item))\n this._emit()\n }\n\n private _emit(): void {\n this._onChange(this._items())\n this._onTouched()\n }\n}\n","@if (_hasFile()) {\n <seam-file-tile\n [item]=\"_items()[0]\"\n [variant]=\"_tileVariant()\"\n [showName]=\"showTileName()\"\n [disabled]=\"_effectiveDisabled()\"\n (remove)=\"_onTileRemove($event)\"\n ></seam-file-tile>\n <button\n type=\"button\"\n class=\"seam-file-field__replace\"\n [disabled]=\"_effectiveDisabled()\"\n (click)=\"inputComponentWhenFilled._openPicker()\"\n >\n or <strong>{{ replaceText() }}</strong>\n </button>\n <!-- Hidden mounted input so the replace button has a picker to delegate to. -->\n <seam-file-input\n #inputComponentWhenFilled\n hidden\n [multiple]=\"multiple()\"\n [accept]=\"accept()\"\n [maxSize]=\"maxSize()\"\n [maxFiles]=\"_remainingMaxFiles()\"\n [disabled]=\"_effectiveDisabled()\"\n [hideErrors]=\"true\"\n (filesAdded)=\"_onFilesAdded($event)\"\n (rejected)=\"_onRejected($event)\"\n ></seam-file-input>\n} @else {\n <seam-file-input\n #inputComponent\n [multiple]=\"multiple()\"\n [accept]=\"accept()\"\n [maxSize]=\"maxSize()\"\n [maxFiles]=\"_remainingMaxFiles()\"\n [disabled]=\"_effectiveDisabled()\"\n [hideErrors]=\"hideErrors()\"\n [promptText]=\"promptText()\"\n [promptSuffix]=\"promptSuffix()\"\n (filesAdded)=\"_onFilesAdded($event)\"\n (rejected)=\"_onRejected($event)\"\n ></seam-file-input>\n\n @if (multiple() && _items().length > 0) {\n <div\n class=\"seam-file-field__tiles\"\n [class.seam-file-field__tiles--preview]=\"previewMode()\"\n >\n @for (item of _items(); track item.id ?? item.name) {\n <seam-file-tile\n [item]=\"item\"\n [variant]=\"_tileVariant()\"\n [showName]=\"showTileName()\"\n [disabled]=\"_effectiveDisabled()\"\n (remove)=\"_onTileRemove($event)\"\n ></seam-file-tile>\n }\n </div>\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, DestroyRef, computed, signal, ChangeDetectionStrategy, Component, input, output, viewChild, afterNextRender, effect, isDevMode, ElementRef, EventEmitter, HostListener, Output, Input, Directive } from '@angular/core';
3
- import * as i1$2 from '@angular/cdk/a11y';
2
+ import { InjectionToken, inject, DestroyRef, computed, signal, viewChild, ChangeDetectionStrategy, Component, input, output, afterNextRender, effect, isDevMode, ElementRef, EventEmitter, HostListener, Output, Input, Directive } from '@angular/core';
3
+ import * as i1$1 from '@angular/cdk/a11y';
4
4
  import { A11yModule } from '@angular/cdk/a11y';
5
5
  import { toSignal, takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
6
6
  import * as i2 from '@angular/forms';
@@ -16,9 +16,8 @@ import { ModalRef, Modal } from '@theseam/ui-common/modal';
16
16
  import { TheSeamAutoFocusDirective, TheSeamDisableControlDirective } from '@theseam/ui-common/shared';
17
17
  import { readFileAsDataUrlAsync, observeControlValue, observeControlValid } from '@theseam/ui-common/utils';
18
18
  import { switchMap, debounceTime, take } from 'rxjs/operators';
19
- import * as i1 from 'ngx-file-drop';
20
- import { NgxFileDropModule } from 'ngx-file-drop';
21
- import * as i1$1 from '@almothafar/angular-signature-pad';
19
+ import { TheSeamFileDropZoneDirective } from '@theseam/ui-common/file-input';
20
+ import * as i1 from '@almothafar/angular-signature-pad';
22
21
  import { AngularSignaturePadModule } from '@almothafar/angular-signature-pad';
23
22
  import { TheSeamFontLoaderService } from '@theseam/ui-common/services';
24
23
  import * as i2$1 from '@theseam/ui-common/form-field';
@@ -41,12 +40,6 @@ class TheSeamSignatureInputImgComponent {
41
40
  optional: true,
42
41
  });
43
42
  _destroyRef = inject(DestroyRef);
44
- /**
45
- * The File is only needed for validation at selection time. Once it's been
46
- * converted to a data URL and stored in the form value, we don't need the
47
- * File again — so there's no point trying to round-trip it through
48
- * writeValue / form state. The preview renders off the current form value.
49
- */
50
43
  _fileControl = new FormControl(null, {
51
44
  validators: [maxFileSizeValidator],
52
45
  });
@@ -54,24 +47,18 @@ class TheSeamSignatureInputImgComponent {
54
47
  initialValue: this._fileControl.status,
55
48
  });
56
49
  _sizeError = computed(() => {
57
- // Touch the status signal so this re-runs on validity changes.
58
50
  this._fileStatus();
59
51
  return this._fileControl.getError('maxFileSize')
60
52
  ? 'File size has exceeded 2MB.'
61
53
  : null;
62
54
  }, ...(ngDevMode ? [{ debugName: "_sizeError" }] : []));
63
- /**
64
- * Single source of truth for both the form value and the preview image.
65
- * External writes (writeValue) and successful uploads both funnel through
66
- * here, so switching tabs and coming back always shows the last committed
67
- * signature.
68
- */
69
55
  _value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : []));
70
56
  _previewDataUrl = computed(() => this._value(), ...(ngDevMode ? [{ debugName: "_previewDataUrl" }] : []));
71
57
  _previewBackgroundImage = computed(() => {
72
58
  const url = this._value();
73
59
  return url ? `url("${url}")` : null;
74
60
  }, ...(ngDevMode ? [{ debugName: "_previewBackgroundImage" }] : []));
61
+ _nativeInput = viewChild.required('filesInput');
75
62
  _onChange = () => undefined;
76
63
  _onTouched = () => undefined;
77
64
  constructor() {
@@ -79,8 +66,6 @@ class TheSeamSignatureInputImgComponent {
79
66
  this._container.registerInputItem('img', this);
80
67
  this._destroyRef.onDestroy(() => this._container?.unregisterInputItem('img', this));
81
68
  }
82
- // Valid file uploads convert to a data URL and become both the preview
83
- // and the form value. Invalid (too large) files clear both.
84
69
  this._fileControl.valueChanges
85
70
  .pipe(switchMap(() => {
86
71
  const file = this._fileControl.value;
@@ -109,44 +94,21 @@ class TheSeamSignatureInputImgComponent {
109
94
  this._fileControl.enable();
110
95
  }
111
96
  clear() {
112
- // valueChanges subscription propagates this to `_value` (null) and to the
113
- // form value.
114
97
  this._fileControl.setValue(null);
115
98
  }
116
99
  openFileBrowse() {
117
- const fileInput = document.createElement('input');
118
- fileInput.setAttribute('type', 'file');
119
- const cleanup = () => {
120
- // Give the 'change' event a moment to fire before assuming the user
121
- // canceled the dialog.
122
- setTimeout(() => {
123
- fileInput.removeEventListener('change', onFileChange);
124
- document.body.removeEventListener('focus', onFocusReturned);
125
- window.removeEventListener('focus', onFocusReturned);
126
- }, 1000);
127
- };
128
- const onFileChange = (event) => {
129
- const input = event.target;
130
- if (input.files && input.files.length > 0) {
131
- this._fileControl.setValue(input.files[0]);
132
- }
133
- cleanup();
134
- };
135
- fileInput.addEventListener('change', onFileChange);
136
- // Detect file browser canceled without making a selection.
137
- const onFocusReturned = () => cleanup();
138
- document.body.addEventListener('focus', onFocusReturned);
139
- window.addEventListener('focus', onFocusReturned);
140
- fileInput.click();
141
- }
142
- _onFileDropped(files) {
143
- for (const droppedFile of files) {
144
- if (droppedFile.fileEntry.isFile) {
145
- const fileEntry = droppedFile.fileEntry;
146
- fileEntry.file((file) => this._fileControl.setValue(file));
147
- break;
148
- }
100
+ this._nativeInput().nativeElement.click();
101
+ }
102
+ _onFilesDropped(files) {
103
+ if (files.length > 0)
104
+ this._fileControl.setValue(files[0]);
105
+ }
106
+ _onNativeChange(event) {
107
+ const input = event.target;
108
+ if (input.files && input.files.length > 0) {
109
+ this._fileControl.setValue(input.files[0]);
149
110
  }
111
+ input.value = '';
150
112
  }
151
113
  _setValue(value) {
152
114
  this._value.set(value);
@@ -160,18 +122,18 @@ class TheSeamSignatureInputImgComponent {
160
122
  useExisting: TheSeamSignatureInputImgComponent,
161
123
  multi: true,
162
124
  },
163
- ], ngImport: i0, template: "<div class=\"seam-signature-input-img\">\n <div class=\"seam-signature-input-img__upload-container\">\n <div class=\"seam-signature-input-img__header h-100\">\n <div\n class=\"seam-signature-input-img__upload-box h-100\"\n [class.has-preview]=\"!!_previewDataUrl()\"\n [style.background-image]=\"_previewBackgroundImage()\"\n tabindex=\"0\"\n (click)=\"openFileBrowse()\"\n (keydown.enter)=\"openFileBrowse()\"\n >\n <ngx-file-drop\n contentClassName=\"border-0\"\n dropZoneClassName=\"border-0\"\n (onFileDrop)=\"_onFileDropped($event)\"\n >\n <ng-template ngx-file-drop-content-tmp>\n @if (!_previewDataUrl()) {\n <div class=\"seam-signature-input-img__drop-prompt\">\n <strong>Choose a file</strong> or drag it here\n </div>\n }\n </ng-template>\n </ngx-file-drop>\n </div>\n\n @if (_sizeError(); as err) {\n <div class=\"seam-signature-input-img__size-error\">{{ err }}</div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{display:block}.seam-signature-input-img{border-bottom:1px solid black}.seam-signature-input-img__header{font-weight:500}.seam-signature-input-img__upload-container{height:174px;position:relative}.seam-signature-input-img__upload-box{position:relative;text-align:center;cursor:pointer;background-repeat:no-repeat;background-position:center;background-size:contain}.seam-signature-input-img__upload-box.has-preview{background-color:#fff}.seam-signature-input-img__size-error{color:var(--bs-danger, #dc3545)}ngx-file-drop ::ng-deep>div{height:100%}ngx-file-drop ::ng-deep .content{height:100%}ngx-file-drop ::ng-deep #dropZone{min-height:125px}\n"], dependencies: [{ kind: "ngmodule", type: NgxFileDropModule }, { kind: "component", type: i1.NgxFileDropComponent, selector: "ngx-file-drop", inputs: ["accept", "directory", "multiple", "dropZoneLabel", "dropZoneClassName", "useDragEnter", "contentClassName", "showBrowseBtn", "browseBtnClassName", "browseBtnLabel", "disabled"], outputs: ["onFileDrop", "onFileOver", "onFileLeave"] }, { kind: "directive", type: i1.NgxFileDropContentTemplateDirective, selector: "[ngx-file-drop-content-tmp]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
125
+ ], viewQueries: [{ propertyName: "_nativeInput", first: true, predicate: ["filesInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"seam-signature-input-img\">\n <div class=\"seam-signature-input-img__upload-container\">\n <div class=\"seam-signature-input-img__header h-100\">\n <div\n class=\"seam-signature-input-img__upload-box h-100\"\n [class.has-preview]=\"!!_previewDataUrl()\"\n [style.background-image]=\"_previewBackgroundImage()\"\n tabindex=\"0\"\n seamFileDropZone\n accept=\"image/*\"\n (seamFileDrop)=\"_onFilesDropped($event)\"\n (click)=\"openFileBrowse()\"\n (keydown.enter)=\"openFileBrowse()\"\n >\n @if (!_previewDataUrl()) {\n <div class=\"seam-signature-input-img__drop-prompt\">\n <strong>Choose a file</strong> or drag it here\n </div>\n }\n </div>\n\n @if (_sizeError(); as err) {\n <div class=\"seam-signature-input-img__size-error\">{{ err }}</div>\n }\n </div>\n </div>\n\n <input\n #filesInput\n type=\"file\"\n hidden\n accept=\"image/*\"\n (change)=\"_onNativeChange($event)\"\n />\n</div>\n", styles: [":host{display:block}.seam-signature-input-img{border-bottom:1px solid black}.seam-signature-input-img__header{font-weight:500}.seam-signature-input-img__upload-container{height:174px;position:relative}.seam-signature-input-img__upload-box{position:relative;text-align:center;cursor:pointer;background-repeat:no-repeat;background-position:center;background-size:contain}.seam-signature-input-img__upload-box.has-preview{background-color:#fff}.seam-signature-input-img__size-error{color:var(--bs-danger, #dc3545)}\n"], dependencies: [{ kind: "directive", type: TheSeamFileDropZoneDirective, selector: "[seamFileDropZone]", inputs: ["accept", "maxSize", "maxFiles", "disabled"], outputs: ["seamFileDrop", "seamFileDropRejected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
164
126
  }
165
127
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamSignatureInputImgComponent, decorators: [{
166
128
  type: Component,
167
- args: [{ selector: 'seam-signature-input-img', imports: [NgxFileDropModule], providers: [
129
+ args: [{ selector: 'seam-signature-input-img', imports: [TheSeamFileDropZoneDirective], providers: [
168
130
  {
169
131
  provide: NG_VALUE_ACCESSOR,
170
132
  useExisting: TheSeamSignatureInputImgComponent,
171
133
  multi: true,
172
134
  },
173
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"seam-signature-input-img\">\n <div class=\"seam-signature-input-img__upload-container\">\n <div class=\"seam-signature-input-img__header h-100\">\n <div\n class=\"seam-signature-input-img__upload-box h-100\"\n [class.has-preview]=\"!!_previewDataUrl()\"\n [style.background-image]=\"_previewBackgroundImage()\"\n tabindex=\"0\"\n (click)=\"openFileBrowse()\"\n (keydown.enter)=\"openFileBrowse()\"\n >\n <ngx-file-drop\n contentClassName=\"border-0\"\n dropZoneClassName=\"border-0\"\n (onFileDrop)=\"_onFileDropped($event)\"\n >\n <ng-template ngx-file-drop-content-tmp>\n @if (!_previewDataUrl()) {\n <div class=\"seam-signature-input-img__drop-prompt\">\n <strong>Choose a file</strong> or drag it here\n </div>\n }\n </ng-template>\n </ngx-file-drop>\n </div>\n\n @if (_sizeError(); as err) {\n <div class=\"seam-signature-input-img__size-error\">{{ err }}</div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{display:block}.seam-signature-input-img{border-bottom:1px solid black}.seam-signature-input-img__header{font-weight:500}.seam-signature-input-img__upload-container{height:174px;position:relative}.seam-signature-input-img__upload-box{position:relative;text-align:center;cursor:pointer;background-repeat:no-repeat;background-position:center;background-size:contain}.seam-signature-input-img__upload-box.has-preview{background-color:#fff}.seam-signature-input-img__size-error{color:var(--bs-danger, #dc3545)}ngx-file-drop ::ng-deep>div{height:100%}ngx-file-drop ::ng-deep .content{height:100%}ngx-file-drop ::ng-deep #dropZone{min-height:125px}\n"] }]
174
- }], ctorParameters: () => [] });
135
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"seam-signature-input-img\">\n <div class=\"seam-signature-input-img__upload-container\">\n <div class=\"seam-signature-input-img__header h-100\">\n <div\n class=\"seam-signature-input-img__upload-box h-100\"\n [class.has-preview]=\"!!_previewDataUrl()\"\n [style.background-image]=\"_previewBackgroundImage()\"\n tabindex=\"0\"\n seamFileDropZone\n accept=\"image/*\"\n (seamFileDrop)=\"_onFilesDropped($event)\"\n (click)=\"openFileBrowse()\"\n (keydown.enter)=\"openFileBrowse()\"\n >\n @if (!_previewDataUrl()) {\n <div class=\"seam-signature-input-img__drop-prompt\">\n <strong>Choose a file</strong> or drag it here\n </div>\n }\n </div>\n\n @if (_sizeError(); as err) {\n <div class=\"seam-signature-input-img__size-error\">{{ err }}</div>\n }\n </div>\n </div>\n\n <input\n #filesInput\n type=\"file\"\n hidden\n accept=\"image/*\"\n (change)=\"_onNativeChange($event)\"\n />\n</div>\n", styles: [":host{display:block}.seam-signature-input-img{border-bottom:1px solid black}.seam-signature-input-img__header{font-weight:500}.seam-signature-input-img__upload-container{height:174px;position:relative}.seam-signature-input-img__upload-box{position:relative;text-align:center;cursor:pointer;background-repeat:no-repeat;background-position:center;background-size:contain}.seam-signature-input-img__upload-box.has-preview{background-color:#fff}.seam-signature-input-img__size-error{color:var(--bs-danger, #dc3545)}\n"] }]
136
+ }], ctorParameters: () => [], propDecorators: { _nativeInput: [{ type: i0.ViewChild, args: ['filesInput', { isSignal: true }] }] } });
175
137
 
176
138
  const DEFAULT_OPTIONS = {
177
139
  canvasWidth: 500,
@@ -271,7 +233,7 @@ class TheSeamSignatureInputPenComponent {
271
233
  useExisting: TheSeamSignatureInputPenComponent,
272
234
  multi: true,
273
235
  },
274
- ], viewQueries: [{ propertyName: "_signaturePad", first: true, predicate: ["sigPad"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"seam-signature-input-pen__canvas-wrap\"\n [style.width.px]=\"_canvasWidth()\"\n [style.height.px]=\"_canvasHeight()\"\n>\n <signature-pad\n #sigPad\n [options]=\"options()\"\n (drawStart)=\"_drawStart($event)\"\n (drawEnd)=\"_drawComplete($event)\"\n ></signature-pad>\n</div>\n", styles: [":host{display:block;overflow:hidden;position:relative}.seam-signature-input-pen__canvas-wrap{border-bottom:1px solid black}signature-pad{background:none!important}signature-pad ::ng-deep .signature-pad-canvas{border:none!important}\n"], dependencies: [{ kind: "ngmodule", type: AngularSignaturePadModule }, { kind: "component", type: i1$1.SignaturePadComponent, selector: "signature-pad", inputs: ["options"], outputs: ["drawStart", "drawBeforeUpdate", "drawAfterUpdate", "drawEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
236
+ ], viewQueries: [{ propertyName: "_signaturePad", first: true, predicate: ["sigPad"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"seam-signature-input-pen__canvas-wrap\"\n [style.width.px]=\"_canvasWidth()\"\n [style.height.px]=\"_canvasHeight()\"\n>\n <signature-pad\n #sigPad\n [options]=\"options()\"\n (drawStart)=\"_drawStart($event)\"\n (drawEnd)=\"_drawComplete($event)\"\n ></signature-pad>\n</div>\n", styles: [":host{display:block;overflow:hidden;position:relative}.seam-signature-input-pen__canvas-wrap{border-bottom:1px solid black}signature-pad{background:none!important}signature-pad ::ng-deep .signature-pad-canvas{border:none!important}\n"], dependencies: [{ kind: "ngmodule", type: AngularSignaturePadModule }, { kind: "component", type: i1.SignaturePadComponent, selector: "signature-pad", inputs: ["options"], outputs: ["drawStart", "drawBeforeUpdate", "drawAfterUpdate", "drawEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
275
237
  }
276
238
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamSignatureInputPenComponent, decorators: [{
277
239
  type: Component,
@@ -583,7 +545,7 @@ class TheSeamSignatureInputPanelComponent {
583
545
  provide: THESEAM_SIGNATURE_INPUT_CONTAINER,
584
546
  useExisting: TheSeamSignatureInputPanelComponent,
585
547
  },
586
- ], ngImport: i0, template: "<div class=\"seam-signature-input-panel\">\n <div class=\"seam-signature-input-panel__content\" cdkTrapFocus>\n <div class=\"seam-signature-input-panel__options pb-2 mb-1\">\n <button\n seamButton\n theme=\"primary\"\n [class.btn-sm]=\"_isSm()\"\n class=\"text-nowrap mr-1\"\n [class.active]=\"_activeType() === 'pen'\"\n (click)=\"showType('pen')\"\n >\n <seam-icon [icon]=\"_faSignature\" size=\"sm\"></seam-icon> Draw\n </button>\n <button\n seamButton\n theme=\"primary\"\n [class.btn-sm]=\"_isSm()\"\n class=\"text-nowrap mx-1\"\n [class.active]=\"_activeType() === 'text'\"\n (click)=\"showType('text')\"\n >\n <seam-icon [icon]=\"_faKeyboard\" size=\"sm\"></seam-icon> Type\n </button>\n <button\n seamButton\n theme=\"primary\"\n [class.btn-sm]=\"_isSm()\"\n class=\"text-nowrap ml-1\"\n [class.active]=\"_activeType() === 'img'\"\n (click)=\"showType('img')\"\n >\n <seam-icon [icon]=\"_faUpload\" size=\"sm\"></seam-icon> Upload\n </button>\n </div>\n\n <form [formGroup]=\"_form\">\n @switch (_activeType()) {\n @case ('pen') {\n <seam-signature-input-pen\n formControlName=\"pen\"\n ></seam-signature-input-pen>\n }\n @case ('text') {\n <seam-signature-input-text\n formControlName=\"text\"\n ></seam-signature-input-text>\n }\n @case ('img') {\n <seam-signature-input-img\n formControlName=\"img\"\n ></seam-signature-input-img>\n }\n }\n </form>\n\n <div class=\"seam-signature-input-panel__footer mt-1\">\n <div>\n @if (_resetType() === 'delete') {\n <button\n seamButton\n theme=\"danger\"\n [class.btn-sm]=\"_isSm()\"\n [disabled]=\"_valueEmpty()\"\n (click)=\"_onClearBtnClick($event)\"\n >\n Delete\n </button>\n } @else {\n <button\n seamButton\n theme=\"lightgray\"\n [class.btn-sm]=\"_isSm()\"\n [disabled]=\"_valueEmpty()\"\n (click)=\"_onClearBtnClick($event)\"\n >\n Clear\n </button>\n }\n </div>\n <div>\n <button\n seamButton\n theme=\"lightgray\"\n [class.btn-sm]=\"_isSm()\"\n seamAutoFocus\n (click)=\"_onCancelBtnClick($event)\"\n >\n Cancel\n </button>\n <button\n seamButton\n theme=\"primary\"\n [class.btn-sm]=\"_isSm()\"\n [disabled]=\"!_canSubmit()\"\n (click)=\"_onSubmitBtnClick($event)\"\n >\n Apply Signature\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block}.seam-signature-input-panel{background-color:#fff;border-radius:4px}.seam-signature-input-panel__options{padding:8px 8px 0;border-bottom:1px solid black;display:flex;flex-direction:row;justify-content:flex-end}.seam-signature-input-panel__footer{padding:0 8px 8px;display:flex;flex-direction:row;justify-content:space-between;margin-top:2px}.seam-signature-input-panel__footer>div>*:not(:last-child){margin-right:6px}.seam-signature-input-panel__footer button{height:36px;margin-top:6px}\n"], dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i1$2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TheSeamButtonsModule }, { kind: "component", type: i3.TheSeamButtonComponent, selector: "button[seamButton]", inputs: ["disabled", "theme", "size", "type"], exportAs: ["seamButton"] }, { kind: "ngmodule", type: TheSeamIconModule }, { kind: "component", type: i4.IconComponent, selector: "seam-icon", inputs: ["grayscaleOnDisable", "disabled", "iconClass", "icon", "size", "showDefaultOnError", "defaultIcon", "iconType"] }, { kind: "directive", type: TheSeamAutoFocusDirective, selector: "[seamAutoFocus]", inputs: ["seamAutoFocus"], exportAs: ["seamAutoFocus"] }, { kind: "component", type: TheSeamSignatureInputPenComponent, selector: "seam-signature-input-pen", inputs: ["options"], outputs: ["beginDrawing", "endDrawing"] }, { kind: "component", type: TheSeamSignatureInputTextComponent, selector: "seam-signature-input-text" }, { kind: "component", type: TheSeamSignatureInputImgComponent, selector: "seam-signature-input-img" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
548
+ ], ngImport: i0, template: "<div class=\"seam-signature-input-panel\">\n <div class=\"seam-signature-input-panel__content\" cdkTrapFocus>\n <div class=\"seam-signature-input-panel__options pb-2 mb-1\">\n <button\n seamButton\n theme=\"primary\"\n [class.btn-sm]=\"_isSm()\"\n class=\"text-nowrap mr-1\"\n [class.active]=\"_activeType() === 'pen'\"\n (click)=\"showType('pen')\"\n >\n <seam-icon [icon]=\"_faSignature\" size=\"sm\"></seam-icon> Draw\n </button>\n <button\n seamButton\n theme=\"primary\"\n [class.btn-sm]=\"_isSm()\"\n class=\"text-nowrap mx-1\"\n [class.active]=\"_activeType() === 'text'\"\n (click)=\"showType('text')\"\n >\n <seam-icon [icon]=\"_faKeyboard\" size=\"sm\"></seam-icon> Type\n </button>\n <button\n seamButton\n theme=\"primary\"\n [class.btn-sm]=\"_isSm()\"\n class=\"text-nowrap ml-1\"\n [class.active]=\"_activeType() === 'img'\"\n (click)=\"showType('img')\"\n >\n <seam-icon [icon]=\"_faUpload\" size=\"sm\"></seam-icon> Upload\n </button>\n </div>\n\n <form [formGroup]=\"_form\">\n @switch (_activeType()) {\n @case ('pen') {\n <seam-signature-input-pen\n formControlName=\"pen\"\n ></seam-signature-input-pen>\n }\n @case ('text') {\n <seam-signature-input-text\n formControlName=\"text\"\n ></seam-signature-input-text>\n }\n @case ('img') {\n <seam-signature-input-img\n formControlName=\"img\"\n ></seam-signature-input-img>\n }\n }\n </form>\n\n <div class=\"seam-signature-input-panel__footer mt-1\">\n <div>\n @if (_resetType() === 'delete') {\n <button\n seamButton\n theme=\"danger\"\n [class.btn-sm]=\"_isSm()\"\n [disabled]=\"_valueEmpty()\"\n (click)=\"_onClearBtnClick($event)\"\n >\n Delete\n </button>\n } @else {\n <button\n seamButton\n theme=\"lightgray\"\n [class.btn-sm]=\"_isSm()\"\n [disabled]=\"_valueEmpty()\"\n (click)=\"_onClearBtnClick($event)\"\n >\n Clear\n </button>\n }\n </div>\n <div>\n <button\n seamButton\n theme=\"lightgray\"\n [class.btn-sm]=\"_isSm()\"\n seamAutoFocus\n (click)=\"_onCancelBtnClick($event)\"\n >\n Cancel\n </button>\n <button\n seamButton\n theme=\"primary\"\n [class.btn-sm]=\"_isSm()\"\n [disabled]=\"!_canSubmit()\"\n (click)=\"_onSubmitBtnClick($event)\"\n >\n Apply Signature\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block}.seam-signature-input-panel{background-color:#fff;border-radius:4px}.seam-signature-input-panel__options{padding:8px 8px 0;border-bottom:1px solid black;display:flex;flex-direction:row;justify-content:flex-end}.seam-signature-input-panel__footer{padding:0 8px 8px;display:flex;flex-direction:row;justify-content:space-between;margin-top:2px}.seam-signature-input-panel__footer>div>*:not(:last-child){margin-right:6px}.seam-signature-input-panel__footer button{height:36px;margin-top:6px}\n"], dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i1$1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TheSeamButtonsModule }, { kind: "component", type: i3.TheSeamButtonComponent, selector: "button[seamButton]", inputs: ["disabled", "theme", "size", "type"], exportAs: ["seamButton"] }, { kind: "ngmodule", type: TheSeamIconModule }, { kind: "component", type: i4.IconComponent, selector: "seam-icon", inputs: ["grayscaleOnDisable", "disabled", "iconClass", "icon", "size", "showDefaultOnError", "defaultIcon", "iconType"] }, { kind: "directive", type: TheSeamAutoFocusDirective, selector: "[seamAutoFocus]", inputs: ["seamAutoFocus"], exportAs: ["seamAutoFocus"] }, { kind: "component", type: TheSeamSignatureInputPenComponent, selector: "seam-signature-input-pen", inputs: ["options"], outputs: ["beginDrawing", "endDrawing"] }, { kind: "component", type: TheSeamSignatureInputTextComponent, selector: "seam-signature-input-text" }, { kind: "component", type: TheSeamSignatureInputImgComponent, selector: "seam-signature-input-img" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
587
549
  }
588
550
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamSignatureInputPanelComponent, decorators: [{
589
551
  type: Component,
@@ -747,7 +709,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
747
709
 
748
710
  class TheSeamSignatureInputImgHarness extends ComponentHarness {
749
711
  static hostSelector = 'seam-signature-input-img';
750
- _fileDrop = this.locatorFor('ngx-file-drop');
712
+ _fileDrop = this.locatorFor('.seam-signature-input-img__upload-box');
751
713
  _sizeError = this.locatorForOptional('.seam-signature-input-img__size-error');
752
714
  _preview = this.locatorForOptional('.seam-signature-input-img__preview');
753
715
  async getSizeError() {