ngwr 12.2.0 → 13.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/README.md +11 -1
- package/border-glow/border-glow.scss +1 -1
- package/border-glow/styles/_index.scss +15 -233
- package/border-glow/styles/_rules.scss +228 -0
- package/breadcrumbs/breadcrumbs.scss +1 -1
- package/breadcrumbs/styles/_index.scss +15 -78
- package/breadcrumbs/styles/_rules.scss +84 -0
- package/card/card.scss +1 -1
- package/card/styles/_index.scss +15 -96
- package/card/styles/_rules.scss +96 -0
- package/cascader/cascader.scss +1 -1
- package/cascader/styles/_index.scss +15 -244
- package/cascader/styles/_rules.scss +261 -0
- package/checkbox/styles/_index.scss +7 -0
- package/circular-text/circular-text.scss +1 -1
- package/circular-text/styles/_index.scss +15 -63
- package/circular-text/styles/_rules.scss +58 -0
- package/color-picker/styles/_index.scss +8 -0
- package/drag-drop/sortable-list.scss +1 -1
- package/drag-drop/styles/_index.scss +14 -143
- package/drag-drop/styles/_rules.scss +151 -0
- package/falling-text/falling-text.scss +1 -1
- package/falling-text/styles/_index.scss +15 -36
- package/falling-text/styles/_rules.scss +31 -0
- package/fesm2022/ngwr-border-glow.mjs +14 -5
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +15 -4
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +2 -2
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +27 -9
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +86 -8
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +2 -2
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +57 -7
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +6 -2
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +8 -0
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-dialog.mjs +22 -16
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-directives.mjs +5 -2
- package/fesm2022/ngwr-directives.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +2 -2
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +24 -18
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +6 -2
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text.mjs +2 -2
- package/fesm2022/ngwr-falling-text.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload.mjs +29 -8
- package/fesm2022/ngwr-file-upload.mjs.map +1 -1
- package/fesm2022/ngwr-form.mjs +48 -1
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +21 -4
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text.mjs +2 -2
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text.mjs +11 -4
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
- package/fesm2022/ngwr-icon-adapters-feather.mjs +11 -0
- package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
- package/fesm2022/ngwr-icon-adapters-lucide.mjs +22 -2
- package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +474 -37
- package/fesm2022/ngwr-icon.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +41 -3
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-knob.mjs +17 -2
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +2 -2
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +2 -2
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar.mjs +2 -2
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-markdown.mjs +63 -11
- package/fesm2022/ngwr-markdown.mjs.map +1 -1
- package/fesm2022/ngwr-marquee.mjs +2 -2
- package/fesm2022/ngwr-marquee.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +36 -7
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +1 -1
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-qr.mjs +38 -1
- package/fesm2022/ngwr-qr.mjs.map +1 -1
- package/fesm2022/ngwr-radio.mjs +63 -8
- package/fesm2022/ngwr-radio.mjs.map +1 -1
- package/fesm2022/ngwr-rating.mjs +17 -2
- package/fesm2022/ngwr-rating.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +22 -3
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +46 -12
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text.mjs +12 -5
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-slider.mjs +21 -6
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card.mjs +2 -2
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-star-border.mjs +2 -2
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-switch.mjs +44 -4
- package/fesm2022/ngwr-switch.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +1 -1
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +6 -2
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea.mjs +5 -2
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +41 -1
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card.mjs +2 -2
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +38 -6
- package/fesm2022/ngwr-transfer.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +33 -7
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter.mjs +2 -2
- package/fesm2022/ngwr-typewriter.mjs.map +1 -1
- package/fesm2022/ngwr-utils.mjs +107 -1
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +2 -2
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +2 -2
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/file-upload/styles/_index.scss +6 -0
- package/glitch-text/glitch-text.scss +1 -1
- package/glitch-text/styles/_index.scss +14 -164
- package/glitch-text/styles/_rules.scss +160 -0
- package/gradient-text/gradient-text.scss +1 -1
- package/gradient-text/styles/_index.scss +15 -118
- package/gradient-text/styles/_rules.scss +113 -0
- package/input/styles/_index.scss +13 -0
- package/input-otp/styles/_index.scss +8 -0
- package/lightbox/lightbox.scss +1 -1
- package/lightbox/styles/_index.scss +15 -191
- package/lightbox/styles/_rules.scss +198 -0
- package/list/list.scss +1 -1
- package/list/styles/_index.scss +15 -101
- package/list/styles/_rules.scss +108 -0
- package/llms-full.txt +4 -4
- package/loading-bar/loading-bar.scss +1 -1
- package/loading-bar/styles/_index.scss +15 -33
- package/loading-bar/styles/_rules.scss +28 -0
- package/marquee/marquee.scss +1 -1
- package/marquee/styles/_index.scss +15 -155
- package/marquee/styles/_rules.scss +150 -0
- package/package.json +3 -3
- package/segmented/styles/_index.scss +8 -0
- package/select/styles/_index.scss +17 -1
- package/shiny-text/shiny-text.scss +1 -1
- package/shiny-text/styles/_index.scss +14 -105
- package/shiny-text/styles/_rules.scss +101 -0
- package/slider/styles/_index.scss +8 -0
- package/spotlight-card/spotlight-card.scss +1 -1
- package/spotlight-card/styles/_index.scss +15 -52
- package/spotlight-card/styles/_rules.scss +47 -0
- package/star-border/star-border.scss +1 -1
- package/star-border/styles/_index.scss +15 -125
- package/star-border/styles/_rules.scss +120 -0
- package/switch/styles/_index.scss +7 -0
- package/theme/styles/_dark.scss +81 -4
- package/theme/styles/_index.scss +1 -0
- package/theme/styles/_motion.scss +94 -0
- package/tilt-card/styles/_index.scss +15 -45
- package/tilt-card/styles/_rules.scss +40 -0
- package/tilt-card/tilt-card.scss +1 -1
- package/tree/styles/_index.scss +15 -1
- package/types/ngwr-breadcrumbs.d.ts +1 -0
- package/types/ngwr-cascader.d.ts +17 -1
- package/types/ngwr-checkbox.d.ts +51 -3
- package/types/ngwr-color-picker.d.ts +19 -1
- package/types/ngwr-file-upload.d.ts +18 -1
- package/types/ngwr-form.d.ts +51 -2
- package/types/ngwr-fuzzy-text.d.ts +5 -0
- package/types/ngwr-icon-adapters-feather.d.ts +11 -0
- package/types/ngwr-icon.d.ts +42 -1
- package/types/ngwr-input-otp.d.ts +23 -1
- package/types/ngwr-knob.d.ts +14 -0
- package/types/ngwr-radio.d.ts +37 -2
- package/types/ngwr-rating.d.ts +14 -0
- package/types/ngwr-segmented.d.ts +18 -1
- package/types/ngwr-select.d.ts +17 -1
- package/types/ngwr-shiny-text.d.ts +1 -0
- package/types/ngwr-slider.d.ts +14 -1
- package/types/ngwr-switch.d.ts +26 -2
- package/types/ngwr-textarea.d.ts +3 -0
- package/types/ngwr-theme.d.ts +24 -0
- package/types/ngwr-transfer.d.ts +17 -1
- package/types/ngwr-tree.d.ts +21 -1
- package/types/ngwr-utils.d.ts +47 -1
- package/types/ngwr-version.d.ts +1 -1
- package/typewriter/styles/_index.scss +15 -38
- package/typewriter/styles/_rules.scss +33 -0
- package/typewriter/typewriter.scss +1 -1
- package/virtual-scroll/styles/_index.scss +15 -32
- package/virtual-scroll/styles/_rules.scss +27 -0
- package/virtual-scroll/virtual-scroll.scss +1 -1
- package/waves/styles/_index.scss +15 -50
- package/waves/styles/_rules.scss +45 -0
- package/waves/waves.scss +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-file-upload.mjs","sources":["../../../projects/lib/file-upload/file-upload.ts","../../../projects/lib/file-upload/file-upload.html","../../../projects/lib/file-upload/ngwr-file-upload.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n type ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n input,\n model,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useI18nText } from 'ngwr/i18n';\n\nimport type { WrFileUploadRejection, WrFileUploadRejectionReason } from './interfaces';\n\n/** Human-readable byte size (e.g. `1.2 MB`). */\nfunction formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n const v = bytes / 1024 ** i;\n return `${v.toFixed(v >= 100 || i === 0 ? 0 : 1)} ${units[i]}`;\n}\n\n/** Does `file` match the `accept` attribute (extensions or MIME globs)? */\nfunction matchesAccept(file: File, accept: string): boolean {\n if (!accept) return true;\n const tokens = accept\n .split(',')\n .map(t => t.trim().toLowerCase())\n .filter(Boolean);\n if (tokens.length === 0) return true;\n const name = file.name.toLowerCase();\n const mime = file.type.toLowerCase();\n return tokens.some(token => {\n if (token.startsWith('.')) return name.endsWith(token);\n if (token.endsWith('/*')) return mime.startsWith(token.slice(0, -1));\n return mime === token;\n });\n}\n\n/**\n * Drag-and-drop file upload zone. Click to browse, drag-and-drop to add,\n * with an optional file list + remove buttons.\n *\n * A signal-forms native control: it implements\n * `FormValueControl<File | readonly File[] | null>`, so `[formField]` binds\n * straight to its `value` model — no `ControlValueAccessor` in between.\n * `[(value)]` works standalone. The value shape follows `multiple`: a single\n * `File` (or `null`), or a `File[]` array.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-file-upload [formField]=\"form.files\" [multiple]=\"true\" accept=\"image/*\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-file-upload [(value)]=\"avatar\" accept=\".png,.jpg\" [maxSize]=\"2 * 1024 * 1024\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/file-upload\n */\n@Component({\n selector: 'wr-file-upload',\n templateUrl: './file-upload.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [],\n})\nexport class WrFileUpload implements FormValueControl<File | readonly File[] | null> {\n /** Allow multiple files. Single mode replaces on each add. @default false */\n readonly multiple = input(false, { transform: coerceBooleanProperty });\n\n /** `accept` attribute — comma-separated MIME types or extensions. @default '' */\n readonly accept = input<string>('');\n\n /** Max bytes per file. `0` disables the check. @default 0 */\n readonly maxSize = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /** Max files (multi mode only). `0` disables the check. @default 0 */\n readonly maxFiles = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /** Render the picked-files list below the zone. @default true */\n readonly showList = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Disable interaction. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Primary call-to-action label. Falls back to `fileUpload.browse`. */\n readonly pickLabel = input<string | null>(null);\n\n /** Secondary instruction below the CTA. Falls back to `fileUpload.dropZone`. */\n readonly dropLabel = input<string | null>(null);\n\n /** Drop-zone host aria-label. Falls back to `fileUpload.dropZoneLabel`. */\n readonly dropZoneLabel = input<string | null>(null);\n\n /** Remove-file button aria-label. Falls back to `fileUpload.removeFile`. */\n readonly removeFileLabel = input<string | null>(null);\n\n protected readonly resolvedPick = useI18nText(this.pickLabel, 'fileUpload.browse', 'Click to browse');\n protected readonly resolvedDrop = useI18nText(this.dropLabel, 'fileUpload.dropZone', 'or drop files here');\n protected readonly resolvedDropZone = useI18nText(\n this.dropZoneLabel,\n 'fileUpload.dropZoneLabel',\n 'File upload drop zone — click or drop files'\n );\n protected readonly resolvedRemove = useI18nText(this.removeFileLabel, 'fileUpload.removeFile', 'Remove file');\n\n /** Optional helper text shown below the labels (e.g. accepted formats). */\n readonly helperText = input<string>('');\n\n /**\n * Selected file(s). Bound by `[formField]`, or two-way via `[(value)]`. Shape\n * follows `multiple`: a single `File` (or `null`), or a `File[]` array.\n */\n readonly value = model<File | readonly File[] | null>(null);\n\n /** Emitted when files are rejected for type / size / count reasons. */\n readonly rejected = output<readonly WrFileUploadRejection[]>();\n\n /** Emitted on commit so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n protected readonly picker = viewChild.required<ElementRef<HTMLInputElement>>('picker');\n\n protected readonly files = signal<readonly File[]>([]);\n protected readonly dragging = signal(false);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-file-upload'];\n if (this.dragging()) parts.push('wr-file-upload--dragging');\n if (this.disabled()) parts.push('wr-file-upload--disabled');\n return parts.join(' ');\n });\n\n protected readonly formatBytes = formatBytes;\n\n constructor() {\n // Rebuild the internal file list from an external `value` write (mirrors the\n // old `writeValue`). Skipped when the value is merely the echo of an\n // in-component edit, so re-syncing can never clobber a live selection.\n effect(() => {\n const v = this.value();\n untracked(() => {\n // Coerce null/undefined to `[]` — a classic-forms binding can write\n // null, which the old `writeValue(v: … | null)` accepted too.\n const next: readonly File[] = v === null || v === undefined ? [] : Array.isArray(v) ? v : [v as File];\n const current = this.files();\n if (current.length === next.length && current.every((file, i) => file === next[i])) return;\n this.files.set(next);\n });\n });\n }\n\n // Template handlers\n\n protected openPicker(): void {\n if (this.disabled()) return;\n this.picker().nativeElement.click();\n }\n\n protected onZoneKey(event: KeyboardEvent): void {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this.openPicker();\n }\n }\n\n protected onPickerChange(event: Event): void {\n const target = event.target as HTMLInputElement;\n if (target.files) this.acceptFiles(Array.from(target.files));\n // Reset so picking the same file twice re-fires `change`.\n target.value = '';\n }\n\n protected onDragEnter(event: DragEvent): void {\n if (this.disabled()) return;\n event.preventDefault();\n this.dragging.set(true);\n }\n\n protected onDragOver(event: DragEvent): void {\n if (this.disabled()) return;\n event.preventDefault();\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy';\n }\n\n protected onDragLeave(event: DragEvent): void {\n // Only clear when leaving the zone itself, not internal children.\n if (event.currentTarget === event.target) this.dragging.set(false);\n }\n\n protected onDrop(event: DragEvent): void {\n event.preventDefault();\n this.dragging.set(false);\n if (this.disabled()) return;\n const dropped = event.dataTransfer?.files ? Array.from(event.dataTransfer.files) : [];\n if (dropped.length > 0) this.acceptFiles(dropped);\n }\n\n protected removeAt(index: number): void {\n if (this.disabled()) return;\n const next = this.files().filter((_, i) => i !== index);\n this.files.set(next);\n this.emit();\n }\n\n // Internals\n\n private acceptFiles(incoming: readonly File[]): void {\n const accept = this.accept();\n const maxSize = this.maxSize();\n const rejections: WrFileUploadRejection[] = [];\n const accepted: File[] = [];\n\n for (const file of incoming) {\n const reason = this.reject(file, accept, maxSize);\n if (reason) rejections.push({ file, reason });\n else accepted.push(file);\n }\n\n let next: File[];\n if (this.multiple()) {\n next = [...this.files(), ...accepted];\n const maxFiles = this.maxFiles();\n if (maxFiles > 0 && next.length > maxFiles) {\n const overflow = next.splice(maxFiles);\n for (const file of overflow) rejections.push({ file, reason: 'count' });\n }\n } else {\n // Single mode — last accepted wins, prior selection cleared.\n next = accepted.length > 0 ? [accepted[accepted.length - 1]] : [...this.files()];\n }\n\n this.files.set(next);\n this.touch.emit();\n this.emit();\n if (rejections.length > 0) this.rejected.emit(rejections);\n }\n\n private reject(file: File, accept: string, maxSize: number): WrFileUploadRejectionReason | null {\n if (accept && !matchesAccept(file, accept)) return 'type';\n if (maxSize > 0 && file.size > maxSize) return 'size';\n return null;\n }\n\n private emit(): void {\n const list = this.files();\n if (this.multiple()) {\n this.value.set(list);\n } else {\n this.value.set(list.length > 0 ? list[0] : null);\n }\n }\n}\n","<div\n class=\"wr-file-upload__zone\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedDropZone()\"\n [attr.aria-disabled]=\"disabled() || null\"\n (click)=\"openPicker()\"\n (keydown)=\"onZoneKey($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <svg class=\"wr-file-upload__icon\" width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 16V4M12 4l-4 4M12 4l4 4M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <div class=\"wr-file-upload__hint\">\n <strong>{{ resolvedPick() }}</strong>\n <span>{{ resolvedDrop() }}</span>\n @if (helperText()) {\n <small>{{ helperText() }}</small>\n }\n </div>\n</div>\n\n<!--\n The real <input type=\"file\"> is never operated directly — the zone above is\n the control and forwards clicks here, so the input is visually hidden and kept\n OUTSIDE the zone: inside, it nested one interactive element within another\n (the zone is role=\"button\") and put two tab stops where users see one. It is\n positioned absolutely and clipped, so being a sibling changes nothing visually.\n aria-hidden must be paired with tabindex=\"-1\", or hiding a focusable element is\n itself a violation.\n-->\n<input\n #picker\n class=\"wr-file-upload__picker\"\n type=\"file\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n [attr.accept]=\"accept() || null\"\n [attr.multiple]=\"multiple() ? '' : null\"\n [disabled]=\"disabled()\"\n (change)=\"onPickerChange($event)\"\n/>\n\n@if (showList() && files().length > 0) {\n <ul class=\"wr-file-upload__list\">\n @for (file of files(); track $index) {\n <li class=\"wr-file-upload__item\">\n <span class=\"wr-file-upload__name\" [title]=\"file.name\">{{ file.name }}</span>\n <span class=\"wr-file-upload__size\">{{ formatBytes(file.size) }}</span>\n <button\n type=\"button\"\n class=\"wr-file-upload__remove\"\n [attr.aria-label]=\"resolvedRemove()\"\n [disabled]=\"disabled()\"\n (click)=\"removeAt($index)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" aria-hidden=\"true\">\n <path\n d=\"M2 2 L10 10 M10 2 L2 10\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n </li>\n }\n </ul>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;AAAA;;;;;AAKG;AAsBH;AACA,SAAS,WAAW,CAAC,KAAa,EAAA;IAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AAAE,QAAA,OAAO,KAAK;AACvD,IAAA,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;AAC3C,IAAA,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AAClF,IAAA,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,IAAI,CAAC;IAC3B,OAAO,CAAA,EAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA,CAAA,EAAI,KAAK,CAAC,CAAC,CAAC,CAAA,CAAE;AAChE;AAEA;AACA,SAAS,aAAa,CAAC,IAAU,EAAE,MAAc,EAAA;AAC/C,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,IAAI;IACxB,MAAM,MAAM,GAAG;SACZ,KAAK,CAAC,GAAG;AACT,SAAA,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;SAC/B,MAAM,CAAC,OAAO,CAAC;AAClB,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;AACpC,IAAA,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,IAAG;AACzB,QAAA,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;AAAE,YAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AACtD,QAAA,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;AAAE,YAAA,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpE,OAAO,IAAI,KAAK,KAAK;AACvB,IAAA,CAAC,CAAC;AACJ;AAEA;;;;;;;;;;;;;;;;;;;;AAoBG;MAQU,YAAY,CAAA;;IAEd,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAG7D,MAAM,GAAG,KAAK,CAAS,EAAE;+EAAC;;IAG1B,OAAO,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,SAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,CAAC,CAAU,KAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAA,CAAG;;IAGlG,QAAQ,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,UAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,CAAC,CAAU,KAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAA,CAAG;;IAGnG,QAAQ,GAAG,KAAK,CAAC,IAAI,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;AAErE;;;;;AAKG;IACM,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAG7D,SAAS,GAAG,KAAK,CAAgB,IAAI;kFAAC;;IAGtC,SAAS,GAAG,KAAK,CAAgB,IAAI;kFAAC;;IAGtC,aAAa,GAAG,KAAK,CAAgB,IAAI;sFAAC;;IAG1C,eAAe,GAAG,KAAK,CAAgB,IAAI;wFAAC;IAElC,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,EAAE,iBAAiB,CAAC;IAClF,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAqB,EAAE,oBAAoB,CAAC;IACvF,gBAAgB,GAAG,WAAW,CAC/C,IAAI,CAAC,aAAa,EAClB,0BAA0B,EAC1B,6CAA6C,CAC9C;IACkB,cAAc,GAAG,WAAW,CAAC,IAAI,CAAC,eAAe,EAAE,uBAAuB,EAAE,aAAa,CAAC;;IAGpG,UAAU,GAAG,KAAK,CAAS,EAAE;mFAAC;AAEvC;;;AAGG;IACM,KAAK,GAAG,KAAK,CAAgC,IAAI;8EAAC;;IAGlD,QAAQ,GAAG,MAAM,EAAoC;;IAGrD,KAAK,GAAG,MAAM,EAAQ;AAEZ,IAAA,MAAM,GAAG,SAAS,CAAC,QAAQ,CAA+B,QAAQ;+EAAC;IAEnE,KAAK,GAAG,MAAM,CAAkB,EAAE;8EAAC;IACnC,QAAQ,GAAG,MAAM,CAAC,KAAK;iFAAC;AAExB,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AACzC,QAAA,MAAM,KAAK,GAAG,CAAC,gBAAgB,CAAC;QAChC,IAAI,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC;QAC3D,IAAI,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC;AAC3D,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IACxB,CAAC;gFAAC;IAEiB,WAAW,GAAG,WAAW;AAE5C,IAAA,WAAA,GAAA;;;;QAIE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;YACtB,SAAS,CAAC,MAAK;;;AAGb,gBAAA,MAAM,IAAI,GAAoB,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,GAAG,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAS,CAAC;AACrG,gBAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE;gBAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;oBAAE;AACpF,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;;IAIU,UAAU,GAAA;QAClB,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACrB,IAAI,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE;IACrC;AAEU,IAAA,SAAS,CAAC,KAAoB,EAAA;AACtC,QAAA,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE;YAC9C,KAAK,CAAC,cAAc,EAAE;YACtB,IAAI,CAAC,UAAU,EAAE;QACnB;IACF;AAEU,IAAA,cAAc,CAAC,KAAY,EAAA;AACnC,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;QAC/C,IAAI,MAAM,CAAC,KAAK;AAAE,YAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;;AAE5D,QAAA,MAAM,CAAC,KAAK,GAAG,EAAE;IACnB;AAEU,IAAA,WAAW,CAAC,KAAgB,EAAA;QACpC,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACrB,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;IACzB;AAEU,IAAA,UAAU,CAAC,KAAgB,EAAA;QACnC,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACrB,KAAK,CAAC,cAAc,EAAE;QACtB,IAAI,KAAK,CAAC,YAAY;AAAE,YAAA,KAAK,CAAC,YAAY,CAAC,UAAU,GAAG,MAAM;IAChE;AAEU,IAAA,WAAW,CAAC,KAAgB,EAAA;;AAEpC,QAAA,IAAI,KAAK,CAAC,aAAa,KAAK,KAAK,CAAC,MAAM;AAAE,YAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;IACpE;AAEU,IAAA,MAAM,CAAC,KAAgB,EAAA;QAC/B,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;QACxB,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACrB,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,EAAE,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE;AACrF,QAAA,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;AAAE,YAAA,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;IACnD;AAEU,IAAA,QAAQ,CAAC,KAAa,EAAA;QAC9B,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC;AACvD,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACpB,IAAI,CAAC,IAAI,EAAE;IACb;;AAIQ,IAAA,WAAW,CAAC,QAAyB,EAAA;AAC3C,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;AAC5B,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE;QAC9B,MAAM,UAAU,GAA4B,EAAE;QAC9C,MAAM,QAAQ,GAAW,EAAE;AAE3B,QAAA,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE;AAC3B,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AACjD,YAAA,IAAI,MAAM;gBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;AACxC,gBAAA,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1B;AAEA,QAAA,IAAI,IAAY;AAChB,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;YACnB,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC;AACrC,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;YAChC,IAAI,QAAQ,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ,EAAE;gBAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;gBACtC,KAAK,MAAM,IAAI,IAAI,QAAQ;oBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YACzE;QACF;aAAO;;AAEL,YAAA,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAClF;AAEA,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QACjB,IAAI,CAAC,IAAI,EAAE;AACX,QAAA,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;AAAE,YAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;IAC3D;AAEQ,IAAA,MAAM,CAAC,IAAU,EAAE,MAAc,EAAE,OAAe,EAAA;QACxD,IAAI,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC;AAAE,YAAA,OAAO,MAAM;QACzD,IAAI,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,OAAO;AAAE,YAAA,OAAO,MAAM;AACrD,QAAA,OAAO,IAAI;IACb;IAEQ,IAAI,GAAA;AACV,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE;AACzB,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;AACnB,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB;aAAO;YACL,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QAClD;IACF;uGA9LW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,22DCjFzB,wpFAgFA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDCa,YAAY,EAAA,UAAA,EAAA,CAAA;kBAPxB,SAAS;+BACE,gBAAgB,EAAA,aAAA,EAEX,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B,EAAE,SAAS,EAAE,WAAW,EAAE,EAAA,OAAA,EACvB,EAAE,EAAA,QAAA,EAAA,wpFAAA,EAAA;k4CA8DkE,QAAQ,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AE7IvF;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"ngwr-file-upload.mjs","sources":["../../../projects/lib/file-upload/file-upload.ts","../../../projects/lib/file-upload/file-upload.html","../../../projects/lib/file-upload/ngwr-file-upload.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n type ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n input,\n model,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useFormFieldAria } from 'ngwr/form';\nimport { useI18nText } from 'ngwr/i18n';\n\nimport type { WrFileUploadRejection, WrFileUploadRejectionReason } from './interfaces';\n\n/** Human-readable byte size (e.g. `1.2 MB`). */\nfunction formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n const v = bytes / 1024 ** i;\n return `${v.toFixed(v >= 100 || i === 0 ? 0 : 1)} ${units[i]}`;\n}\n\n/** Does `file` match the `accept` attribute (extensions or MIME globs)? */\nfunction matchesAccept(file: File, accept: string): boolean {\n if (!accept) return true;\n const tokens = accept\n .split(',')\n .map(t => t.trim().toLowerCase())\n .filter(Boolean);\n if (tokens.length === 0) return true;\n const name = file.name.toLowerCase();\n const mime = file.type.toLowerCase();\n return tokens.some(token => {\n if (token.startsWith('.')) return name.endsWith(token);\n if (token.endsWith('/*')) return mime.startsWith(token.slice(0, -1));\n return mime === token;\n });\n}\n\n/**\n * Drag-and-drop file upload zone. Click to browse, drag-and-drop to add,\n * with an optional file list + remove buttons.\n *\n * A signal-forms native control: it implements\n * `FormValueControl<File | readonly File[] | null>`, so `[formField]` binds\n * straight to its `value` model — no `ControlValueAccessor` in between.\n * `[(value)]` works standalone. The value shape follows `multiple`: a single\n * `File` (or `null`), or a `File[]` array.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-file-upload [formField]=\"form.files\" [multiple]=\"true\" accept=\"image/*\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-file-upload [(value)]=\"avatar\" accept=\".png,.jpg\" [maxSize]=\"2 * 1024 * 1024\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/file-upload\n */\n@Component({\n selector: 'wr-file-upload',\n templateUrl: './file-upload.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [],\n})\nexport class WrFileUpload implements FormValueControl<File | readonly File[] | null> {\n /** Allow multiple files. Single mode replaces on each add. @default false */\n readonly multiple = input(false, { transform: coerceBooleanProperty });\n\n /** `accept` attribute — comma-separated MIME types or extensions. @default '' */\n readonly accept = input<string>('');\n\n /** Max bytes per file. `0` disables the check. @default 0 */\n readonly maxSize = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /** Max files (multi mode only). `0` disables the check. @default 0 */\n readonly maxFiles = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /** Render the picked-files list below the zone. @default true */\n readonly showList = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Disable interaction. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Refuse changes to the selection while the zone stays focusable and the files\n * still submit. Bound automatically from the field's readonly state when used\n * with `[formField]`.\n *\n * The zone stops opening the picker, a drop is refused and the per-file remove\n * buttons go inert — but the list stays readable, which is the point. No\n * `aria-readonly`: the zone is a `role=\"button\"`, and ARIA does not define the\n * state for that role, so `aria-disabled` would be the only mirror available\n * and it would say the wrong thing.\n *\n * @default false\n */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** The surrounding `<wr-form-field>`'s error state. @internal */\n protected readonly fieldAria = useFormFieldAria();\n\n /** Primary call-to-action label. Falls back to `fileUpload.browse`. */\n readonly pickLabel = input<string | null>(null);\n\n /** Secondary instruction below the CTA. Falls back to `fileUpload.dropZone`. */\n readonly dropLabel = input<string | null>(null);\n\n /** Drop-zone host aria-label. Falls back to `fileUpload.dropZoneLabel`. */\n readonly dropZoneLabel = input<string | null>(null);\n\n /** Remove-file button aria-label. Falls back to `fileUpload.removeFile`. */\n readonly removeFileLabel = input<string | null>(null);\n\n protected readonly resolvedPick = useI18nText(this.pickLabel, 'fileUpload.browse', 'Click to browse');\n protected readonly resolvedDrop = useI18nText(this.dropLabel, 'fileUpload.dropZone', 'or drop files here');\n protected readonly resolvedDropZone = useI18nText(\n this.dropZoneLabel,\n 'fileUpload.dropZoneLabel',\n 'File upload drop zone — click or drop files'\n );\n protected readonly resolvedRemove = useI18nText(this.removeFileLabel, 'fileUpload.removeFile', 'Remove file');\n\n /** Optional helper text shown below the labels (e.g. accepted formats). */\n readonly helperText = input<string>('');\n\n /**\n * Selected file(s). Bound by `[formField]`, or two-way via `[(value)]`. Shape\n * follows `multiple`: a single `File` (or `null`), or a `File[]` array.\n */\n readonly value = model<File | readonly File[] | null>(null);\n\n /** Emitted when files are rejected for type / size / count reasons. */\n readonly rejected = output<readonly WrFileUploadRejection[]>();\n\n /** Emitted on commit so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n protected readonly picker = viewChild.required<ElementRef<HTMLInputElement>>('picker');\n\n protected readonly files = signal<readonly File[]>([]);\n protected readonly dragging = signal(false);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-file-upload'];\n if (this.dragging()) parts.push('wr-file-upload--dragging');\n if (this.disabled()) parts.push('wr-file-upload--disabled');\n else if (this.readonly()) parts.push('wr-file-upload--readonly');\n return parts.join(' ');\n });\n\n protected readonly formatBytes = formatBytes;\n\n constructor() {\n // Rebuild the internal file list from an external `value` write (mirrors the\n // old `writeValue`). Skipped when the value is merely the echo of an\n // in-component edit, so re-syncing can never clobber a live selection.\n effect(() => {\n const v = this.value();\n untracked(() => {\n // Coerce null/undefined to `[]` — a classic-forms binding can write\n // null, which the old `writeValue(v: … | null)` accepted too.\n const next: readonly File[] = v === null || v === undefined ? [] : Array.isArray(v) ? v : [v as File];\n const current = this.files();\n if (current.length === next.length && current.every((file, i) => file === next[i])) return;\n this.files.set(next);\n });\n });\n }\n\n // Template handlers\n\n protected openPicker(): void {\n if (this.disabled() || this.readonly()) return;\n this.picker().nativeElement.click();\n }\n\n protected onZoneKey(event: KeyboardEvent): void {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this.openPicker();\n }\n }\n\n protected onPickerChange(event: Event): void {\n const target = event.target as HTMLInputElement;\n if (target.files) this.acceptFiles(Array.from(target.files));\n // Reset so picking the same file twice re-fires `change`.\n target.value = '';\n }\n\n protected onDragEnter(event: DragEvent): void {\n if (this.disabled() || this.readonly()) return;\n event.preventDefault();\n this.dragging.set(true);\n }\n\n protected onDragOver(event: DragEvent): void {\n if (this.disabled() || this.readonly()) return;\n event.preventDefault();\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy';\n }\n\n protected onDragLeave(event: DragEvent): void {\n // Only clear when leaving the zone itself, not internal children.\n if (event.currentTarget === event.target) this.dragging.set(false);\n }\n\n protected onDrop(event: DragEvent): void {\n event.preventDefault();\n this.dragging.set(false);\n if (this.disabled() || this.readonly()) return;\n const dropped = event.dataTransfer?.files ? Array.from(event.dataTransfer.files) : [];\n if (dropped.length > 0) this.acceptFiles(dropped);\n }\n\n protected removeAt(index: number): void {\n if (this.disabled() || this.readonly()) return;\n const next = this.files().filter((_, i) => i !== index);\n this.files.set(next);\n this.emit();\n }\n\n // Internals\n\n private acceptFiles(incoming: readonly File[]): void {\n if (this.disabled() || this.readonly()) return;\n const accept = this.accept();\n const maxSize = this.maxSize();\n const rejections: WrFileUploadRejection[] = [];\n const accepted: File[] = [];\n\n for (const file of incoming) {\n const reason = this.reject(file, accept, maxSize);\n if (reason) rejections.push({ file, reason });\n else accepted.push(file);\n }\n\n let next: File[];\n if (this.multiple()) {\n next = [...this.files(), ...accepted];\n const maxFiles = this.maxFiles();\n if (maxFiles > 0 && next.length > maxFiles) {\n const overflow = next.splice(maxFiles);\n for (const file of overflow) rejections.push({ file, reason: 'count' });\n }\n } else {\n // Single mode — last accepted wins, prior selection cleared.\n next = accepted.length > 0 ? [accepted[accepted.length - 1]] : [...this.files()];\n }\n\n this.files.set(next);\n this.touch.emit();\n this.emit();\n if (rejections.length > 0) this.rejected.emit(rejections);\n }\n\n private reject(file: File, accept: string, maxSize: number): WrFileUploadRejectionReason | null {\n if (accept && !matchesAccept(file, accept)) return 'type';\n if (maxSize > 0 && file.size > maxSize) return 'size';\n return null;\n }\n\n private emit(): void {\n const list = this.files();\n if (this.multiple()) {\n this.value.set(list);\n } else {\n this.value.set(list.length > 0 ? list[0] : null);\n }\n }\n}\n","<div\n class=\"wr-file-upload__zone\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedDropZone()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n (click)=\"openPicker()\"\n (keydown)=\"onZoneKey($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <svg class=\"wr-file-upload__icon\" width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 16V4M12 4l-4 4M12 4l4 4M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <div class=\"wr-file-upload__hint\">\n <strong>{{ resolvedPick() }}</strong>\n <span>{{ resolvedDrop() }}</span>\n @if (helperText()) {\n <small>{{ helperText() }}</small>\n }\n </div>\n</div>\n\n<!--\n The real <input type=\"file\"> is never operated directly — the zone above is\n the control and forwards clicks here, so the input is visually hidden and kept\n OUTSIDE the zone: inside, it nested one interactive element within another\n (the zone is role=\"button\") and put two tab stops where users see one. It is\n positioned absolutely and clipped, so being a sibling changes nothing visually.\n aria-hidden must be paired with tabindex=\"-1\", or hiding a focusable element is\n itself a violation.\n-->\n<input\n #picker\n class=\"wr-file-upload__picker\"\n type=\"file\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n [attr.accept]=\"accept() || null\"\n [attr.multiple]=\"multiple() ? '' : null\"\n [disabled]=\"disabled() || readonly()\"\n (change)=\"onPickerChange($event)\"\n/>\n\n@if (showList() && files().length > 0) {\n <ul class=\"wr-file-upload__list\">\n @for (file of files(); track $index) {\n <li class=\"wr-file-upload__item\">\n <span class=\"wr-file-upload__name\" [title]=\"file.name\">{{ file.name }}</span>\n <span class=\"wr-file-upload__size\">{{ formatBytes(file.size) }}</span>\n <button\n type=\"button\"\n class=\"wr-file-upload__remove\"\n [attr.aria-label]=\"resolvedRemove()\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"removeAt($index)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" aria-hidden=\"true\">\n <path\n d=\"M2 2 L10 10 M10 2 L2 10\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n </li>\n }\n </ul>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;AAAA;;;;;AAKG;AAuBH;AACA,SAAS,WAAW,CAAC,KAAa,EAAA;IAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AAAE,QAAA,OAAO,KAAK;AACvD,IAAA,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;AAC3C,IAAA,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AAClF,IAAA,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,IAAI,CAAC;IAC3B,OAAO,CAAA,EAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA,CAAA,EAAI,KAAK,CAAC,CAAC,CAAC,CAAA,CAAE;AAChE;AAEA;AACA,SAAS,aAAa,CAAC,IAAU,EAAE,MAAc,EAAA;AAC/C,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,IAAI;IACxB,MAAM,MAAM,GAAG;SACZ,KAAK,CAAC,GAAG;AACT,SAAA,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;SAC/B,MAAM,CAAC,OAAO,CAAC;AAClB,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;AACpC,IAAA,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,IAAG;AACzB,QAAA,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;AAAE,YAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AACtD,QAAA,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;AAAE,YAAA,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpE,OAAO,IAAI,KAAK,KAAK;AACvB,IAAA,CAAC,CAAC;AACJ;AAEA;;;;;;;;;;;;;;;;;;;;AAoBG;MAQU,YAAY,CAAA;;IAEd,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAG7D,MAAM,GAAG,KAAK,CAAS,EAAE;+EAAC;;IAG1B,OAAO,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,SAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,CAAC,CAAU,KAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAA,CAAG;;IAGlG,QAAQ,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,UAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,CAAC,CAAU,KAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAA,CAAG;;IAGnG,QAAQ,GAAG,KAAK,CAAC,IAAI,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;AAErE;;;;;AAKG;IACM,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;AAEtE;;;;;;;;;;;;AAYG;IACM,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAGnD,SAAS,GAAG,gBAAgB,EAAE;;IAGxC,SAAS,GAAG,KAAK,CAAgB,IAAI;kFAAC;;IAGtC,SAAS,GAAG,KAAK,CAAgB,IAAI;kFAAC;;IAGtC,aAAa,GAAG,KAAK,CAAgB,IAAI;sFAAC;;IAG1C,eAAe,GAAG,KAAK,CAAgB,IAAI;wFAAC;IAElC,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,EAAE,iBAAiB,CAAC;IAClF,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAqB,EAAE,oBAAoB,CAAC;IACvF,gBAAgB,GAAG,WAAW,CAC/C,IAAI,CAAC,aAAa,EAClB,0BAA0B,EAC1B,6CAA6C,CAC9C;IACkB,cAAc,GAAG,WAAW,CAAC,IAAI,CAAC,eAAe,EAAE,uBAAuB,EAAE,aAAa,CAAC;;IAGpG,UAAU,GAAG,KAAK,CAAS,EAAE;mFAAC;AAEvC;;;AAGG;IACM,KAAK,GAAG,KAAK,CAAgC,IAAI;8EAAC;;IAGlD,QAAQ,GAAG,MAAM,EAAoC;;IAGrD,KAAK,GAAG,MAAM,EAAQ;AAEZ,IAAA,MAAM,GAAG,SAAS,CAAC,QAAQ,CAA+B,QAAQ;+EAAC;IAEnE,KAAK,GAAG,MAAM,CAAkB,EAAE;8EAAC;IACnC,QAAQ,GAAG,MAAM,CAAC,KAAK;iFAAC;AAExB,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AACzC,QAAA,MAAM,KAAK,GAAG,CAAC,gBAAgB,CAAC;QAChC,IAAI,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC;QAC3D,IAAI,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC;aACtD,IAAI,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC;AAChE,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IACxB,CAAC;gFAAC;IAEiB,WAAW,GAAG,WAAW;AAE5C,IAAA,WAAA,GAAA;;;;QAIE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;YACtB,SAAS,CAAC,MAAK;;;AAGb,gBAAA,MAAM,IAAI,GAAoB,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,GAAG,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAS,CAAC;AACrG,gBAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE;gBAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;oBAAE;AACpF,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;;IAIU,UAAU,GAAA;QAClB,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACxC,IAAI,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE;IACrC;AAEU,IAAA,SAAS,CAAC,KAAoB,EAAA;AACtC,QAAA,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE;YAC9C,KAAK,CAAC,cAAc,EAAE;YACtB,IAAI,CAAC,UAAU,EAAE;QACnB;IACF;AAEU,IAAA,cAAc,CAAC,KAAY,EAAA;AACnC,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;QAC/C,IAAI,MAAM,CAAC,KAAK;AAAE,YAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;;AAE5D,QAAA,MAAM,CAAC,KAAK,GAAG,EAAE;IACnB;AAEU,IAAA,WAAW,CAAC,KAAgB,EAAA;QACpC,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACxC,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;IACzB;AAEU,IAAA,UAAU,CAAC,KAAgB,EAAA;QACnC,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACxC,KAAK,CAAC,cAAc,EAAE;QACtB,IAAI,KAAK,CAAC,YAAY;AAAE,YAAA,KAAK,CAAC,YAAY,CAAC,UAAU,GAAG,MAAM;IAChE;AAEU,IAAA,WAAW,CAAC,KAAgB,EAAA;;AAEpC,QAAA,IAAI,KAAK,CAAC,aAAa,KAAK,KAAK,CAAC,MAAM;AAAE,YAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;IACpE;AAEU,IAAA,MAAM,CAAC,KAAgB,EAAA;QAC/B,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;QACxB,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACxC,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,EAAE,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE;AACrF,QAAA,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;AAAE,YAAA,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;IACnD;AAEU,IAAA,QAAQ,CAAC,KAAa,EAAA;QAC9B,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC;AACvD,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACpB,IAAI,CAAC,IAAI,EAAE;IACb;;AAIQ,IAAA,WAAW,CAAC,QAAyB,EAAA;QAC3C,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;AACxC,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;AAC5B,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE;QAC9B,MAAM,UAAU,GAA4B,EAAE;QAC9C,MAAM,QAAQ,GAAW,EAAE;AAE3B,QAAA,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE;AAC3B,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AACjD,YAAA,IAAI,MAAM;gBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;AACxC,gBAAA,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1B;AAEA,QAAA,IAAI,IAAY;AAChB,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;YACnB,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC;AACrC,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;YAChC,IAAI,QAAQ,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ,EAAE;gBAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;gBACtC,KAAK,MAAM,IAAI,IAAI,QAAQ;oBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YACzE;QACF;aAAO;;AAEL,YAAA,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAClF;AAEA,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QACjB,IAAI,CAAC,IAAI,EAAE;AACX,QAAA,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;AAAE,YAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;IAC3D;AAEQ,IAAA,MAAM,CAAC,IAAU,EAAE,MAAc,EAAE,OAAe,EAAA;QACxD,IAAI,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC;AAAE,YAAA,OAAO,MAAM;QACzD,IAAI,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,OAAO;AAAE,YAAA,OAAO,MAAM;AACrD,QAAA,OAAO,IAAI;IACb;IAEQ,IAAI,GAAA;AACV,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE;AACzB,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;AACnB,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB;aAAO;YACL,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QAClD;IACF;uGAlNW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,4+DClFzB,8xFAkFA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDAa,YAAY,EAAA,UAAA,EAAA,CAAA;kBAPxB,SAAS;+BACE,gBAAgB,EAAA,aAAA,EAEX,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B,EAAE,SAAS,EAAE,WAAW,EAAE,EAAA,OAAA,EACvB,EAAE,EAAA,QAAA,EAAA,8xFAAA,EAAA;k+CAgFkE,QAAQ,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AEhKvF;;AAEG;;;;"}
|
package/fesm2022/ngwr-form.mjs
CHANGED
|
@@ -412,9 +412,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
|
|
|
412
412
|
], template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">({{ resolvedOptional() }})</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n" }]
|
|
413
413
|
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], optionalLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionalLabel", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }], autoErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoErrors", required: false }] }], projectedErrors: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WrFormError), { isSignal: true }] }] } });
|
|
414
414
|
|
|
415
|
+
/**
|
|
416
|
+
* @license
|
|
417
|
+
*
|
|
418
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
419
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
420
|
+
*/
|
|
421
|
+
/**
|
|
422
|
+
* The two attributes that make a `<wr-form-field>`'s visible error reach a
|
|
423
|
+
* screen reader — bind both on whichever element carries the control's role.
|
|
424
|
+
*
|
|
425
|
+
* Without them the messages the field renders are visible and nothing else: the
|
|
426
|
+
* control announces itself as valid, and the message is associated with nothing.
|
|
427
|
+
* No automated gate catches that — axe has no rule that a visible error must be
|
|
428
|
+
* programmatically associated with its control — so this helper exists to make
|
|
429
|
+
* the wiring one line per control rather than three, and the specs are the gate.
|
|
430
|
+
*
|
|
431
|
+
* `aria-invalid` is keyed on the message EXISTING rather than on `errorKeys()`,
|
|
432
|
+
* because the field only publishes an id once it is showing something —
|
|
433
|
+
* announcing "invalid" while pointing at nothing is worse than staying quiet.
|
|
434
|
+
*
|
|
435
|
+
* Must be called from an injection context (a field initialiser or a
|
|
436
|
+
* constructor). Returns inert signals outside a `<wr-form-field>`.
|
|
437
|
+
*
|
|
438
|
+
* Pass `skipSelf` from a COMPOSITE control — one whose own template renders
|
|
439
|
+
* other ngwr controls. Such a component shields its subtree by re-providing
|
|
440
|
+
* `WR_FORM_FIELD` as `null`, or the inner parts each announce the outer field's
|
|
441
|
+
* error as their own; a component sees its own providers, so it has to read the
|
|
442
|
+
* real field from one level up.
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* ```ts
|
|
446
|
+
* protected readonly fieldAria = useFormFieldAria();
|
|
447
|
+
* ```
|
|
448
|
+
* ```html
|
|
449
|
+
* <input [attr.aria-invalid]="fieldAria.ariaInvalid()" [attr.aria-describedby]="fieldAria.describedBy()" />
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
function useFormFieldAria(options) {
|
|
453
|
+
const field = inject(WR_FORM_FIELD, { optional: true, skipSelf: options?.skipSelf ?? false });
|
|
454
|
+
const describedBy = computed(() => field?.describedBy() ?? null, /* @ts-ignore */
|
|
455
|
+
...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
|
|
456
|
+
return {
|
|
457
|
+
describedBy,
|
|
458
|
+
ariaInvalid: computed(() => (describedBy() ? 'true' : null)),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
415
462
|
/**
|
|
416
463
|
* Generated bundle index. Do not edit.
|
|
417
464
|
*/
|
|
418
465
|
|
|
419
|
-
export { WR_FORM_ERRORS, WR_FORM_ERROR_FALLBACKS, WR_FORM_FIELD, WrFormError, WrFormField, WrFormItem, provideWrFormErrors };
|
|
466
|
+
export { WR_FORM_ERRORS, WR_FORM_ERROR_FALLBACKS, WR_FORM_FIELD, WrFormError, WrFormField, WrFormItem, provideWrFormErrors, useFormFieldAria };
|
|
420
467
|
//# sourceMappingURL=ngwr-form.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-form.mjs","sources":["../../../projects/lib/form/form-item.ts","../../../projects/lib/form/form-errors.ts","../../../projects/lib/form/tokens.ts","../../../projects/lib/form/form-field.ts","../../../projects/lib/form/form-field.html","../../../projects/lib/form/ngwr-form.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\n/**\n * Wraps a label + control + error message into a single field row.\n *\n * Toggle `hasError` to apply error styling to the projected label and\n * any descendant input / textarea / checkbox.\n *\n * @example\n * ```html\n * <wr-form-item [hasError]=\"form.controls.email.invalid\">\n * <label>Email</label>\n * <input wrInput formControlName=\"email\" />\n * <wr-form-error>Invalid email</wr-form-error>\n * </wr-form-item>\n * ```\n *\n * @see https://ngwr.dev/reference/components/form\n */\n@Component({\n selector: 'wr-form-item',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrFormItem {\n /**\n * When `true`, applies error coloring to the projected label and inputs.\n *\n * @default false\n */\n readonly hasError = input(false, { transform: coerceBooleanProperty });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-form-item'];\n if (this.hasError()) parts.push('wr-form-item--error');\n return parts.join(' ');\n });\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken, type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\n/** Pull a number out of a validator payload without trusting its shape. */\nfunction num(error: unknown, field: string): string {\n if (error && typeof error === 'object' && field in error) {\n const value = (error as Record<string, unknown>)[field];\n if (typeof value === 'number' || typeof value === 'string') return String(value);\n }\n return '';\n}\n\n/** What a message function is handed to build its sentence. */\nexport interface WrFormErrorContext {\n /** Validator key (classic forms) or Signal Forms `kind` that produced the error. */\n readonly key: string;\n /**\n * The validator's own payload — `{ requiredLength, actualLength }` for\n * `minlength`, `{ min }` for `wrMinDate`, and so on. `true` for the\n * flag-shaped validators, which carry nothing.\n */\n readonly error: unknown;\n /** The surrounding field's `label`, so a message can name the field. */\n readonly label: string;\n}\n\n/** A message, or a function that builds one from the validator's payload. */\nexport type WrFormErrorMessage = string | ((ctx: WrFormErrorContext) => string);\n\n/** Validator key → message. Keys are matched exactly against `control.errors`. */\nexport type WrFormErrorMessages = Readonly<Record<string, WrFormErrorMessage>>;\n\n/**\n * App-wide validation copy. Empty by default; `<wr-form-field>` falls through\n * to the `ngwr/i18n` catalog and then to a built-in English sentence, so a\n * form shows a usable message with no configuration at all.\n */\nexport const WR_FORM_ERRORS = new InjectionToken<WrFormErrorMessages>('WR_FORM_ERRORS', {\n providedIn: 'root',\n factory: () => ({}),\n});\n\n/**\n * Register validation messages for the whole app.\n *\n * Only the keys you name are overridden — everything else keeps resolving\n * through the i18n catalog, so this composes with `provideWrI18n()` rather than\n * replacing it. A projected `<wr-form-error key=\"…\">` still wins over both:\n * per-field copy beats app-wide copy.\n *\n * @example\n * ```ts\n * bootstrapApplication(App, {\n * providers: [\n * provideWrFormErrors({\n * required: 'Please fill this in.',\n * minlength: ({ error }) => `At least ${(error as { requiredLength: number }).requiredLength} characters.`,\n * }),\n * ],\n * });\n * ```\n */\nexport function provideWrFormErrors(messages: WrFormErrorMessages = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: WR_FORM_ERRORS, useValue: messages }]);\n}\n\n/**\n * Last-resort English, used when no app catalog and no i18n catalog answer.\n *\n * These exist so that a form with zero configuration still says something true\n * rather than rendering an empty error block. Every key here also ships as a\n * `validation.*` entry in `ngwr/i18n/{en,ru}`, which is what a localized app\n * actually reads.\n */\nexport const WR_FORM_ERROR_FALLBACKS: WrFormErrorMessages = {\n // Angular built-ins.\n required: 'This field is required.',\n requiredTrue: 'This field must be checked.',\n email: 'Enter a valid email address.',\n minlength: ({ error }) => `Enter at least ${num(error, 'requiredLength')} characters.`,\n maxlength: ({ error }) => `Enter at most ${num(error, 'requiredLength')} characters.`,\n min: ({ error }) => `Enter ${num(error, 'min')} or more.`,\n max: ({ error }) => `Enter ${num(error, 'max')} or less.`,\n pattern: 'This value is not in the expected format.',\n\n // WrValidators.\n // Not \"cannot be blank\": the validator PASSES on an empty value and fails on\n // any value that holds whitespace, so the blank wording described the one case\n // that never reaches this message — over a field reading `Ada Lovelace`.\n noWhitespace: 'This value cannot contain spaces.',\n hexColor: 'Enter a hex colour, e.g. #1a2b3c.',\n url: 'Enter a valid URL.',\n cardNumber: 'Enter a valid card number.',\n cvc: ({ error }) => `Enter the ${num(error, 'length')}-digit security code.`,\n iban: 'Enter a valid IBAN.',\n match: 'The two values do not match.',\n matchFields: 'These fields do not match.',\n oneOf: 'Choose one of the allowed values.',\n minDate: 'Choose a later date.',\n maxDate: 'Choose an earlier date.',\n};\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * What a projected control can read from its surrounding `<wr-form-field>`.\n *\n * The field renders `<label [for]>` before it can see what was projected into\n * it, so the id has to travel the other way: the control adopts the id the\n * label already points at. Doing it through DI keeps it a render-time binding,\n * which is what SSR needs — stamping the id from `afterNextRender` would leave\n * every prerendered page with a label pointing at nothing.\n */\nexport interface WrFormFieldContext {\n /** Id the field's `<label for>` points at. */\n readonly controlId: Signal<string>;\n\n /**\n * The reverse direction, for the one case the paragraph above does not cover: a\n * projected control that already carries its own `id` keeps it, and the label has\n * to follow, or `for` names an element that does not exist and the field ends up\n * with no label at all. Called during the control's construction, so the label's\n * binding still resolves in the same render pass.\n */\n adoptControlId(id: string): void;\n\n /**\n * Validator keys currently in error, once the control is touched or dirty.\n * Empty when the field is valid or has not been interacted with.\n */\n readonly errorKeys: Signal<readonly string[]>;\n\n /** Id of the element listing the visible messages, or `null` when there are none. */\n readonly describedBy: Signal<string | null>;\n}\n\n/** Present only inside a `<wr-form-field>`; inject with `{ optional: true }`. */\nexport const WR_FORM_FIELD = new InjectionToken<WrFormFieldContext>('WR_FORM_FIELD');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n DestroyRef,\n ViewEncapsulation,\n computed,\n contentChild,\n contentChildren,\n effect,\n forwardRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\n\nimport { EMPTY, type Subscription } from 'rxjs';\n\nimport { WrI18n, useI18nText } from 'ngwr/i18n';\n\nimport { WR_FORM_ERRORS, WR_FORM_ERROR_FALLBACKS, type WrFormErrorMessage } from './form-errors';\nimport { WR_FORM_FIELD, type WrFormFieldContext } from './tokens';\n\nlet uid = 0;\n\n/** A message is either literal text or a function over the validator's payload. */\nfunction render(message: WrFormErrorMessage, key: string, error: unknown, label: string): string {\n return typeof message === 'function' ? message({ key, error, label }) : message;\n}\n\n/** Validator payloads are plain objects; anything else interpolates nothing. */\nfunction toParams(error: unknown): Record<string, unknown> | undefined {\n return error && typeof error === 'object' ? (error as Record<string, unknown>) : undefined;\n}\n\n/**\n * One error message tied to a validator key. Renders only when the parent\n * form-field has a matching error in `control.errors`, and only once the\n * control is touched or dirty.\n *\n * @example\n * ```html\n * <wr-form-error key=\"required\">This field is required.</wr-form-error>\n * <wr-form-error key=\"email\">That doesn't look right.</wr-form-error>\n * ```\n */\n@Component({\n selector: 'wr-form-error',\n template: `<ng-content />`,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-form-error wr-form-field__error',\n role: 'alert',\n '[attr.data-key]': 'key() ?? null',\n '[class.wr-form-field__error--hidden]': '!visible()',\n },\n})\nexport class WrFormError {\n /**\n * Validator key this message corresponds to (e.g. `'required'`).\n * Optional — without a key the message always renders, which is the\n * plain inline-error usage inside `<wr-form-item>`.\n */\n readonly key = input<string>();\n\n private readonly field = inject(WR_FORM_FIELD, { optional: true });\n\n /**\n * Whether this message is the one the current error calls for.\n *\n * The parent projects every `<wr-form-error>` through a single\n * `<ng-content select>`, which cannot filter — so each message decides for\n * itself. Without this, one invalid control rendered \"Email is required.\"\n * and \"That isn't a valid email.\" side by side, which is what the docs have\n * always described as gated behaviour.\n *\n * A keyless message always shows: that is the plain inline-error usage inside\n * `<wr-form-item>`, which has no error discovery at all.\n */\n protected readonly visible = computed(() => {\n const key = this.key();\n if (!key || !this.field) return true;\n return this.field.errorKeys().includes(key);\n });\n}\n\n/**\n * Wraps a label + control + hint + error messages into a single block —\n * the layout primitive every form ends up reinventing.\n *\n * - Auto-binds `label[for]` to the projected control via a generated id\n * (or use the form-control's `[id]` if you set one).\n * - Reads the projected control's `NgControl` to surface validation\n * errors only when the control has been touched / submitted.\n * - Optional `[required]` / `[optional]` markers next to the label.\n *\n * @example\n * ```html\n * <wr-form-field label=\"Email\" hint=\"We'll never share it.\">\n * <input wrInput [formControl]=\"email\" type=\"email\" />\n * <wr-form-error key=\"required\">Email is required.</wr-form-error>\n * <wr-form-error key=\"email\">Not a valid email.</wr-form-error>\n * </wr-form-field>\n * ```\n *\n * @see https://ngwr.dev/reference/components/form-field\n */\n@Component({\n selector: 'wr-form-field',\n templateUrl: './form-field.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n providers: [\n {\n provide: WR_FORM_FIELD,\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n useExisting: forwardRef(() => WrFormField),\n },\n ],\n})\nexport class WrFormField implements WrFormFieldContext {\n /** Label text shown above the control. */\n readonly label = input<string>('');\n\n /** Hint text shown below the control. Hidden when an error is visible. */\n readonly hint = input<string>('');\n\n /** Show a `*` next to the label. @default false */\n readonly required = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Show an \"optional\" marker next to the label — the word comes from\n * `optionalLabel`. Mutually exclusive with `required`. @default false\n */\n readonly optional = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Word inside the optional marker's parentheses. Falls back to\n * `form.optional`, then `'optional'`. The parentheses are the template's, so\n * no locale has to repeat punctuation.\n */\n readonly optionalLabel = input<string | null>(null);\n\n // Unlike the `*` marker beside it, this one is NOT `aria-hidden` — it sits\n // inside the `<label>` and is read as part of the control's accessible name,\n // so an English word here reached a Russian screen reader.\n protected readonly resolvedOptional = useI18nText(this.optionalLabel, 'form.optional', 'optional');\n\n /**\n * Force a specific id on the label's `for` attribute. Auto-generated\n * otherwise, and adopted by the projected `wrInput` unless that element\n * already carries an `id` of its own.\n */\n readonly controlId = input<string>(`wr-form-field-${++uid}`);\n\n // What a projected control reported it actually kept. `null` until one does — most\n // controls simply adopt `controlId`, and only a control with its own `id` overrides\n // it from the other direction.\n private readonly adoptedId = signal<string | null>(null);\n\n /**\n * Id the `<label for>` renders. A control that came with its own `id` keeps it, so\n * the label follows that instead of the generated one — otherwise `for` points at\n * an element that is not in the document and the field is unlabelled.\n */\n protected readonly labelFor = computed(() => this.adoptedId() ?? this.controlId());\n\n /** @internal `WrFormFieldContext` — see the token's docs. */\n adoptControlId(id: string): void {\n this.adoptedId.set(id);\n }\n\n /**\n * Projected `NgControl` — used to read touched / dirty / errors.\n *\n * This catches `[formField]` too: Angular's Signal Forms directive provides\n * `NgControl` via its interop shim, so both forms flavours land here.\n */\n protected readonly ngControl = contentChild(NgControl);\n\n /**\n * Bumped on every event the projected control emits.\n *\n * `AbstractControl`'s `touched` / `dirty` / `errors` accessors are NOT\n * reactive under classic forms — Angular reads them inside `untracked()`, and\n * `errors` is a plain assigned field. A `computed` over them therefore caches\n * its first value and never recomputes, which in a zoneless app means the\n * error block never appears at all. `control.events` is the reactive source\n * Angular does expose, so the computed depends on this counter instead.\n * Signal Forms needs none of this — its interop getters read live signals —\n * and harmlessly contributes no events.\n */\n private readonly revision = signal(0);\n\n constructor() {\n let sub: Subscription | null = null;\n effect(() => {\n const control = this.ngControl()?.control;\n sub?.unsubscribe();\n sub = (control?.events ?? EMPTY).subscribe(() => this.revision.update(n => n + 1));\n });\n inject(DestroyRef).onDestroy(() => sub?.unsubscribe());\n }\n\n protected readonly errors = computed(() => {\n this.revision();\n const c = this.ngControl();\n if (!c) return null;\n if (!c.touched && !c.dirty) return null;\n return c.errors;\n });\n\n protected readonly hasError = computed(() => {\n const errs = this.errors();\n return !!errs && Object.keys(errs).length > 0;\n });\n\n /** @internal Read by the projected `<wr-form-error>` children to decide which of them renders. */\n readonly errorKeys = computed<readonly string[]>(() => Object.keys(this.errors() ?? {}));\n\n /** @internal Read by the projected control so a screen reader is pointed at the message. */\n readonly describedBy = computed(() => (this.hasError() ? `${this.controlId()}-errors` : null));\n\n /**\n * Render a catalog message for any error the markup does not already answer.\n *\n * On by default: a field with no `<wr-form-error>` at all is the common case,\n * and an empty error block helps nobody. Turn it off for a field whose copy is\n * entirely hand-written. @default true\n */\n readonly autoErrors = input(true, { transform: coerceBooleanProperty });\n\n private readonly projectedErrors = contentChildren(WrFormError);\n private readonly appMessages = inject(WR_FORM_ERRORS);\n private readonly i18n = inject(WrI18n, { optional: true });\n\n /**\n * Errors with no projected `<wr-form-error key>` to answer them, resolved to\n * text: the app catalog first, then the `validation.*` i18n keys, then a\n * built-in English sentence. Per-field markup always wins — it is the most\n * specific thing the author wrote.\n */\n protected readonly autoMessages = computed<readonly { key: string; text: string }[]>(() => {\n if (!this.autoErrors()) return [];\n const errors = this.errors();\n if (!errors) return [];\n\n const claimed = new Set(\n this.projectedErrors()\n .map(e => e.key())\n .filter(Boolean)\n );\n\n return Object.entries(errors)\n .filter(([key]) => !claimed.has(key))\n .map(([key, error]) => ({ key, text: this.resolve(key, error) }))\n .filter(m => m.text.length > 0);\n });\n\n private resolve(key: string, error: unknown): string {\n const app = this.appMessages[key];\n if (app !== undefined) return render(app, key, error, this.label());\n\n // `t()` reads the service's revision signal, so this recomputes on a locale\n // switch; a miss returns the key itself, which is the documented signal.\n const translated = this.i18n?.t(`validation.${key}`, toParams(error));\n if (translated && translated !== `validation.${key}`) return translated;\n\n const fallback = WR_FORM_ERROR_FALLBACKS[key];\n return fallback === undefined ? '' : render(fallback, key, error, this.label());\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-form-field'];\n if (this.hasError()) parts.push('wr-form-field--invalid');\n if (this.required()) parts.push('wr-form-field--required');\n return parts.join(' ');\n });\n}\n","@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">({{ resolvedOptional() }})</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-form.mjs","sources":["../../../projects/lib/form/form-item.ts","../../../projects/lib/form/form-errors.ts","../../../projects/lib/form/tokens.ts","../../../projects/lib/form/form-field.ts","../../../projects/lib/form/form-field.html","../../../projects/lib/form/field-aria.ts","../../../projects/lib/form/ngwr-form.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\n/**\n * Wraps a label + control + error message into a single field row.\n *\n * Toggle `hasError` to apply error styling to the projected label and\n * any descendant input / textarea / checkbox.\n *\n * @example\n * ```html\n * <wr-form-item [hasError]=\"form.controls.email.invalid\">\n * <label>Email</label>\n * <input wrInput formControlName=\"email\" />\n * <wr-form-error>Invalid email</wr-form-error>\n * </wr-form-item>\n * ```\n *\n * @see https://ngwr.dev/reference/components/form\n */\n@Component({\n selector: 'wr-form-item',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrFormItem {\n /**\n * When `true`, applies error coloring to the projected label and inputs.\n *\n * @default false\n */\n readonly hasError = input(false, { transform: coerceBooleanProperty });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-form-item'];\n if (this.hasError()) parts.push('wr-form-item--error');\n return parts.join(' ');\n });\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken, type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\n/** Pull a number out of a validator payload without trusting its shape. */\nfunction num(error: unknown, field: string): string {\n if (error && typeof error === 'object' && field in error) {\n const value = (error as Record<string, unknown>)[field];\n if (typeof value === 'number' || typeof value === 'string') return String(value);\n }\n return '';\n}\n\n/** What a message function is handed to build its sentence. */\nexport interface WrFormErrorContext {\n /** Validator key (classic forms) or Signal Forms `kind` that produced the error. */\n readonly key: string;\n /**\n * The validator's own payload — `{ requiredLength, actualLength }` for\n * `minlength`, `{ min }` for `wrMinDate`, and so on. `true` for the\n * flag-shaped validators, which carry nothing.\n */\n readonly error: unknown;\n /** The surrounding field's `label`, so a message can name the field. */\n readonly label: string;\n}\n\n/** A message, or a function that builds one from the validator's payload. */\nexport type WrFormErrorMessage = string | ((ctx: WrFormErrorContext) => string);\n\n/** Validator key → message. Keys are matched exactly against `control.errors`. */\nexport type WrFormErrorMessages = Readonly<Record<string, WrFormErrorMessage>>;\n\n/**\n * App-wide validation copy. Empty by default; `<wr-form-field>` falls through\n * to the `ngwr/i18n` catalog and then to a built-in English sentence, so a\n * form shows a usable message with no configuration at all.\n */\nexport const WR_FORM_ERRORS = new InjectionToken<WrFormErrorMessages>('WR_FORM_ERRORS', {\n providedIn: 'root',\n factory: () => ({}),\n});\n\n/**\n * Register validation messages for the whole app.\n *\n * Only the keys you name are overridden — everything else keeps resolving\n * through the i18n catalog, so this composes with `provideWrI18n()` rather than\n * replacing it. A projected `<wr-form-error key=\"…\">` still wins over both:\n * per-field copy beats app-wide copy.\n *\n * @example\n * ```ts\n * bootstrapApplication(App, {\n * providers: [\n * provideWrFormErrors({\n * required: 'Please fill this in.',\n * minlength: ({ error }) => `At least ${(error as { requiredLength: number }).requiredLength} characters.`,\n * }),\n * ],\n * });\n * ```\n */\nexport function provideWrFormErrors(messages: WrFormErrorMessages = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: WR_FORM_ERRORS, useValue: messages }]);\n}\n\n/**\n * Last-resort English, used when no app catalog and no i18n catalog answer.\n *\n * These exist so that a form with zero configuration still says something true\n * rather than rendering an empty error block. Every key here also ships as a\n * `validation.*` entry in `ngwr/i18n/{en,ru}`, which is what a localized app\n * actually reads.\n */\nexport const WR_FORM_ERROR_FALLBACKS: WrFormErrorMessages = {\n // Angular built-ins.\n required: 'This field is required.',\n requiredTrue: 'This field must be checked.',\n email: 'Enter a valid email address.',\n minlength: ({ error }) => `Enter at least ${num(error, 'requiredLength')} characters.`,\n maxlength: ({ error }) => `Enter at most ${num(error, 'requiredLength')} characters.`,\n min: ({ error }) => `Enter ${num(error, 'min')} or more.`,\n max: ({ error }) => `Enter ${num(error, 'max')} or less.`,\n pattern: 'This value is not in the expected format.',\n\n // WrValidators.\n // Not \"cannot be blank\": the validator PASSES on an empty value and fails on\n // any value that holds whitespace, so the blank wording described the one case\n // that never reaches this message — over a field reading `Ada Lovelace`.\n noWhitespace: 'This value cannot contain spaces.',\n hexColor: 'Enter a hex colour, e.g. #1a2b3c.',\n url: 'Enter a valid URL.',\n cardNumber: 'Enter a valid card number.',\n cvc: ({ error }) => `Enter the ${num(error, 'length')}-digit security code.`,\n iban: 'Enter a valid IBAN.',\n match: 'The two values do not match.',\n matchFields: 'These fields do not match.',\n oneOf: 'Choose one of the allowed values.',\n minDate: 'Choose a later date.',\n maxDate: 'Choose an earlier date.',\n};\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * What a projected control can read from its surrounding `<wr-form-field>`.\n *\n * The field renders `<label [for]>` before it can see what was projected into\n * it, so the id has to travel the other way: the control adopts the id the\n * label already points at. Doing it through DI keeps it a render-time binding,\n * which is what SSR needs — stamping the id from `afterNextRender` would leave\n * every prerendered page with a label pointing at nothing.\n */\nexport interface WrFormFieldContext {\n /** Id the field's `<label for>` points at. */\n readonly controlId: Signal<string>;\n\n /**\n * The reverse direction, for the one case the paragraph above does not cover: a\n * projected control that already carries its own `id` keeps it, and the label has\n * to follow, or `for` names an element that does not exist and the field ends up\n * with no label at all. Called during the control's construction, so the label's\n * binding still resolves in the same render pass.\n */\n adoptControlId(id: string): void;\n\n /**\n * Validator keys currently in error, once the control is touched or dirty.\n * Empty when the field is valid or has not been interacted with.\n */\n readonly errorKeys: Signal<readonly string[]>;\n\n /** Id of the element listing the visible messages, or `null` when there are none. */\n readonly describedBy: Signal<string | null>;\n}\n\n/** Present only inside a `<wr-form-field>`; inject with `{ optional: true }`. */\nexport const WR_FORM_FIELD = new InjectionToken<WrFormFieldContext>('WR_FORM_FIELD');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n DestroyRef,\n ViewEncapsulation,\n computed,\n contentChild,\n contentChildren,\n effect,\n forwardRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\n\nimport { EMPTY, type Subscription } from 'rxjs';\n\nimport { WrI18n, useI18nText } from 'ngwr/i18n';\n\nimport { WR_FORM_ERRORS, WR_FORM_ERROR_FALLBACKS, type WrFormErrorMessage } from './form-errors';\nimport { WR_FORM_FIELD, type WrFormFieldContext } from './tokens';\n\nlet uid = 0;\n\n/** A message is either literal text or a function over the validator's payload. */\nfunction render(message: WrFormErrorMessage, key: string, error: unknown, label: string): string {\n return typeof message === 'function' ? message({ key, error, label }) : message;\n}\n\n/** Validator payloads are plain objects; anything else interpolates nothing. */\nfunction toParams(error: unknown): Record<string, unknown> | undefined {\n return error && typeof error === 'object' ? (error as Record<string, unknown>) : undefined;\n}\n\n/**\n * One error message tied to a validator key. Renders only when the parent\n * form-field has a matching error in `control.errors`, and only once the\n * control is touched or dirty.\n *\n * @example\n * ```html\n * <wr-form-error key=\"required\">This field is required.</wr-form-error>\n * <wr-form-error key=\"email\">That doesn't look right.</wr-form-error>\n * ```\n */\n@Component({\n selector: 'wr-form-error',\n template: `<ng-content />`,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-form-error wr-form-field__error',\n role: 'alert',\n '[attr.data-key]': 'key() ?? null',\n '[class.wr-form-field__error--hidden]': '!visible()',\n },\n})\nexport class WrFormError {\n /**\n * Validator key this message corresponds to (e.g. `'required'`).\n * Optional — without a key the message always renders, which is the\n * plain inline-error usage inside `<wr-form-item>`.\n */\n readonly key = input<string>();\n\n private readonly field = inject(WR_FORM_FIELD, { optional: true });\n\n /**\n * Whether this message is the one the current error calls for.\n *\n * The parent projects every `<wr-form-error>` through a single\n * `<ng-content select>`, which cannot filter — so each message decides for\n * itself. Without this, one invalid control rendered \"Email is required.\"\n * and \"That isn't a valid email.\" side by side, which is what the docs have\n * always described as gated behaviour.\n *\n * A keyless message always shows: that is the plain inline-error usage inside\n * `<wr-form-item>`, which has no error discovery at all.\n */\n protected readonly visible = computed(() => {\n const key = this.key();\n if (!key || !this.field) return true;\n return this.field.errorKeys().includes(key);\n });\n}\n\n/**\n * Wraps a label + control + hint + error messages into a single block —\n * the layout primitive every form ends up reinventing.\n *\n * - Auto-binds `label[for]` to the projected control via a generated id\n * (or use the form-control's `[id]` if you set one).\n * - Reads the projected control's `NgControl` to surface validation\n * errors only when the control has been touched / submitted.\n * - Optional `[required]` / `[optional]` markers next to the label.\n *\n * @example\n * ```html\n * <wr-form-field label=\"Email\" hint=\"We'll never share it.\">\n * <input wrInput [formControl]=\"email\" type=\"email\" />\n * <wr-form-error key=\"required\">Email is required.</wr-form-error>\n * <wr-form-error key=\"email\">Not a valid email.</wr-form-error>\n * </wr-form-field>\n * ```\n *\n * @see https://ngwr.dev/reference/components/form-field\n */\n@Component({\n selector: 'wr-form-field',\n templateUrl: './form-field.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n providers: [\n {\n provide: WR_FORM_FIELD,\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n useExisting: forwardRef(() => WrFormField),\n },\n ],\n})\nexport class WrFormField implements WrFormFieldContext {\n /** Label text shown above the control. */\n readonly label = input<string>('');\n\n /** Hint text shown below the control. Hidden when an error is visible. */\n readonly hint = input<string>('');\n\n /** Show a `*` next to the label. @default false */\n readonly required = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Show an \"optional\" marker next to the label — the word comes from\n * `optionalLabel`. Mutually exclusive with `required`. @default false\n */\n readonly optional = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Word inside the optional marker's parentheses. Falls back to\n * `form.optional`, then `'optional'`. The parentheses are the template's, so\n * no locale has to repeat punctuation.\n */\n readonly optionalLabel = input<string | null>(null);\n\n // Unlike the `*` marker beside it, this one is NOT `aria-hidden` — it sits\n // inside the `<label>` and is read as part of the control's accessible name,\n // so an English word here reached a Russian screen reader.\n protected readonly resolvedOptional = useI18nText(this.optionalLabel, 'form.optional', 'optional');\n\n /**\n * Force a specific id on the label's `for` attribute. Auto-generated\n * otherwise, and adopted by the projected `wrInput` unless that element\n * already carries an `id` of its own.\n */\n readonly controlId = input<string>(`wr-form-field-${++uid}`);\n\n // What a projected control reported it actually kept. `null` until one does — most\n // controls simply adopt `controlId`, and only a control with its own `id` overrides\n // it from the other direction.\n private readonly adoptedId = signal<string | null>(null);\n\n /**\n * Id the `<label for>` renders. A control that came with its own `id` keeps it, so\n * the label follows that instead of the generated one — otherwise `for` points at\n * an element that is not in the document and the field is unlabelled.\n */\n protected readonly labelFor = computed(() => this.adoptedId() ?? this.controlId());\n\n /** @internal `WrFormFieldContext` — see the token's docs. */\n adoptControlId(id: string): void {\n this.adoptedId.set(id);\n }\n\n /**\n * Projected `NgControl` — used to read touched / dirty / errors.\n *\n * This catches `[formField]` too: Angular's Signal Forms directive provides\n * `NgControl` via its interop shim, so both forms flavours land here.\n */\n protected readonly ngControl = contentChild(NgControl);\n\n /**\n * Bumped on every event the projected control emits.\n *\n * `AbstractControl`'s `touched` / `dirty` / `errors` accessors are NOT\n * reactive under classic forms — Angular reads them inside `untracked()`, and\n * `errors` is a plain assigned field. A `computed` over them therefore caches\n * its first value and never recomputes, which in a zoneless app means the\n * error block never appears at all. `control.events` is the reactive source\n * Angular does expose, so the computed depends on this counter instead.\n * Signal Forms needs none of this — its interop getters read live signals —\n * and harmlessly contributes no events.\n */\n private readonly revision = signal(0);\n\n constructor() {\n let sub: Subscription | null = null;\n effect(() => {\n const control = this.ngControl()?.control;\n sub?.unsubscribe();\n sub = (control?.events ?? EMPTY).subscribe(() => this.revision.update(n => n + 1));\n });\n inject(DestroyRef).onDestroy(() => sub?.unsubscribe());\n }\n\n protected readonly errors = computed(() => {\n this.revision();\n const c = this.ngControl();\n if (!c) return null;\n if (!c.touched && !c.dirty) return null;\n return c.errors;\n });\n\n protected readonly hasError = computed(() => {\n const errs = this.errors();\n return !!errs && Object.keys(errs).length > 0;\n });\n\n /** @internal Read by the projected `<wr-form-error>` children to decide which of them renders. */\n readonly errorKeys = computed<readonly string[]>(() => Object.keys(this.errors() ?? {}));\n\n /** @internal Read by the projected control so a screen reader is pointed at the message. */\n readonly describedBy = computed(() => (this.hasError() ? `${this.controlId()}-errors` : null));\n\n /**\n * Render a catalog message for any error the markup does not already answer.\n *\n * On by default: a field with no `<wr-form-error>` at all is the common case,\n * and an empty error block helps nobody. Turn it off for a field whose copy is\n * entirely hand-written. @default true\n */\n readonly autoErrors = input(true, { transform: coerceBooleanProperty });\n\n private readonly projectedErrors = contentChildren(WrFormError);\n private readonly appMessages = inject(WR_FORM_ERRORS);\n private readonly i18n = inject(WrI18n, { optional: true });\n\n /**\n * Errors with no projected `<wr-form-error key>` to answer them, resolved to\n * text: the app catalog first, then the `validation.*` i18n keys, then a\n * built-in English sentence. Per-field markup always wins — it is the most\n * specific thing the author wrote.\n */\n protected readonly autoMessages = computed<readonly { key: string; text: string }[]>(() => {\n if (!this.autoErrors()) return [];\n const errors = this.errors();\n if (!errors) return [];\n\n const claimed = new Set(\n this.projectedErrors()\n .map(e => e.key())\n .filter(Boolean)\n );\n\n return Object.entries(errors)\n .filter(([key]) => !claimed.has(key))\n .map(([key, error]) => ({ key, text: this.resolve(key, error) }))\n .filter(m => m.text.length > 0);\n });\n\n private resolve(key: string, error: unknown): string {\n const app = this.appMessages[key];\n if (app !== undefined) return render(app, key, error, this.label());\n\n // `t()` reads the service's revision signal, so this recomputes on a locale\n // switch; a miss returns the key itself, which is the documented signal.\n const translated = this.i18n?.t(`validation.${key}`, toParams(error));\n if (translated && translated !== `validation.${key}`) return translated;\n\n const fallback = WR_FORM_ERROR_FALLBACKS[key];\n return fallback === undefined ? '' : render(fallback, key, error, this.label());\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-form-field'];\n if (this.hasError()) parts.push('wr-form-field--invalid');\n if (this.required()) parts.push('wr-form-field--required');\n return parts.join(' ');\n });\n}\n","@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">({{ resolvedOptional() }})</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type Signal, computed, inject } from '@angular/core';\n\nimport { WR_FORM_FIELD } from './tokens';\n\n/** What {@link useFormFieldAria} hands a control to bind on its own element. */\nexport interface WrFormFieldAria {\n /** Id of the field's message block, or `null` when it is showing none. */\n readonly describedBy: Signal<string | null>;\n /** `'true'` while the field is showing a message, `null` otherwise. */\n readonly ariaInvalid: Signal<'true' | null>;\n}\n\n/**\n * The two attributes that make a `<wr-form-field>`'s visible error reach a\n * screen reader — bind both on whichever element carries the control's role.\n *\n * Without them the messages the field renders are visible and nothing else: the\n * control announces itself as valid, and the message is associated with nothing.\n * No automated gate catches that — axe has no rule that a visible error must be\n * programmatically associated with its control — so this helper exists to make\n * the wiring one line per control rather than three, and the specs are the gate.\n *\n * `aria-invalid` is keyed on the message EXISTING rather than on `errorKeys()`,\n * because the field only publishes an id once it is showing something —\n * announcing \"invalid\" while pointing at nothing is worse than staying quiet.\n *\n * Must be called from an injection context (a field initialiser or a\n * constructor). Returns inert signals outside a `<wr-form-field>`.\n *\n * Pass `skipSelf` from a COMPOSITE control — one whose own template renders\n * other ngwr controls. Such a component shields its subtree by re-providing\n * `WR_FORM_FIELD` as `null`, or the inner parts each announce the outer field's\n * error as their own; a component sees its own providers, so it has to read the\n * real field from one level up.\n *\n * @example\n * ```ts\n * protected readonly fieldAria = useFormFieldAria();\n * ```\n * ```html\n * <input [attr.aria-invalid]=\"fieldAria.ariaInvalid()\" [attr.aria-describedby]=\"fieldAria.describedBy()\" />\n * ```\n */\nexport function useFormFieldAria(options?: { readonly skipSelf?: boolean }): WrFormFieldAria {\n const field = inject(WR_FORM_FIELD, { optional: true, skipSelf: options?.skipSelf ?? false });\n const describedBy = computed(() => field?.describedBy() ?? null);\n return {\n describedBy,\n ariaInvalid: computed<'true' | null>(() => (describedBy() ? 'true' : null)),\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -39,6 +39,18 @@ import { numAttr } from 'ngwr/utils';
|
|
|
39
39
|
* @see https://www.reactbits.dev/text-animations/fuzzy-text
|
|
40
40
|
*/
|
|
41
41
|
class WrFuzzyText {
|
|
42
|
+
/**
|
|
43
|
+
* Visual-hiding declarations for the screen-reader copy of the text. A stable
|
|
44
|
+
* object literal, so the binding is written once rather than on every pass.
|
|
45
|
+
*/
|
|
46
|
+
srOnly = {
|
|
47
|
+
position: 'absolute',
|
|
48
|
+
width: '1px',
|
|
49
|
+
height: '1px',
|
|
50
|
+
overflow: 'hidden',
|
|
51
|
+
'clip-path': 'inset(50%)',
|
|
52
|
+
'white-space': 'nowrap',
|
|
53
|
+
};
|
|
42
54
|
/** Text to render. */
|
|
43
55
|
text = input.required(/* @ts-ignore */
|
|
44
56
|
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
@@ -379,7 +391,7 @@ class WrFuzzyText {
|
|
|
379
391
|
};
|
|
380
392
|
}
|
|
381
393
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrFuzzyText, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
382
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.4", type: WrFuzzyText, isStandalone: true, selector: "wr-fuzzy-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, fontSize: { classPropertyName: "fontSize", publicName: "fontSize", isSignal: true, isRequired: false, transformFunction: null }, fontWeight: { classPropertyName: "fontWeight", publicName: "fontWeight", isSignal: true, isRequired: false, transformFunction: null }, fontFamily: { classPropertyName: "fontFamily", publicName: "fontFamily", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, gradient: { classPropertyName: "gradient", publicName: "gradient", isSignal: true, isRequired: false, transformFunction: null }, enableHover: { classPropertyName: "enableHover", publicName: "enableHover", isSignal: true, isRequired: false, transformFunction: null }, baseIntensity: { classPropertyName: "baseIntensity", publicName: "baseIntensity", isSignal: true, isRequired: false, transformFunction: null }, hoverIntensity: { classPropertyName: "hoverIntensity", publicName: "hoverIntensity", isSignal: true, isRequired: false, transformFunction: null }, fuzzRange: { classPropertyName: "fuzzRange", publicName: "fuzzRange", isSignal: true, isRequired: false, transformFunction: null }, fps: { classPropertyName: "fps", publicName: "fps", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, transitionDuration: { classPropertyName: "transitionDuration", publicName: "transitionDuration", isSignal: true, isRequired: false, transformFunction: null }, clickEffect: { classPropertyName: "clickEffect", publicName: "clickEffect", isSignal: true, isRequired: false, transformFunction: null }, glitchMode: { classPropertyName: "glitchMode", publicName: "glitchMode", isSignal: true, isRequired: false, transformFunction: null }, glitchInterval: { classPropertyName: "glitchInterval", publicName: "glitchInterval", isSignal: true, isRequired: false, transformFunction: null }, glitchDuration: { classPropertyName: "glitchDuration", publicName: "glitchDuration", isSignal: true, isRequired: false, transformFunction: null }, letterSpacing: { classPropertyName: "letterSpacing", publicName: "letterSpacing", isSignal: true, isRequired: false, transformFunction: null } }, host: {
|
|
394
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.4", type: WrFuzzyText, isStandalone: true, selector: "wr-fuzzy-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, fontSize: { classPropertyName: "fontSize", publicName: "fontSize", isSignal: true, isRequired: false, transformFunction: null }, fontWeight: { classPropertyName: "fontWeight", publicName: "fontWeight", isSignal: true, isRequired: false, transformFunction: null }, fontFamily: { classPropertyName: "fontFamily", publicName: "fontFamily", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, gradient: { classPropertyName: "gradient", publicName: "gradient", isSignal: true, isRequired: false, transformFunction: null }, enableHover: { classPropertyName: "enableHover", publicName: "enableHover", isSignal: true, isRequired: false, transformFunction: null }, baseIntensity: { classPropertyName: "baseIntensity", publicName: "baseIntensity", isSignal: true, isRequired: false, transformFunction: null }, hoverIntensity: { classPropertyName: "hoverIntensity", publicName: "hoverIntensity", isSignal: true, isRequired: false, transformFunction: null }, fuzzRange: { classPropertyName: "fuzzRange", publicName: "fuzzRange", isSignal: true, isRequired: false, transformFunction: null }, fps: { classPropertyName: "fps", publicName: "fps", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, transitionDuration: { classPropertyName: "transitionDuration", publicName: "transitionDuration", isSignal: true, isRequired: false, transformFunction: null }, clickEffect: { classPropertyName: "clickEffect", publicName: "clickEffect", isSignal: true, isRequired: false, transformFunction: null }, glitchMode: { classPropertyName: "glitchMode", publicName: "glitchMode", isSignal: true, isRequired: false, transformFunction: null }, glitchInterval: { classPropertyName: "glitchInterval", publicName: "glitchInterval", isSignal: true, isRequired: false, transformFunction: null }, glitchDuration: { classPropertyName: "glitchDuration", publicName: "glitchDuration", isSignal: true, isRequired: false, transformFunction: null }, letterSpacing: { classPropertyName: "letterSpacing", publicName: "letterSpacing", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.color": "'var(--wr-color-dark)'" }, classAttribute: "wr-fuzzy-text" }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, isSignal: true }], ngImport: i0, template: "<span class=\"wr-fuzzy-text__sr-only\" [style]=\"srOnly\">{{ text() }}</span><canvas #canvas aria-hidden=\"true\"></canvas>", isInline: true, encapsulation: i0.ViewEncapsulation.None });
|
|
383
395
|
}
|
|
384
396
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrFuzzyText, decorators: [{
|
|
385
397
|
type: Component,
|
|
@@ -390,11 +402,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
|
|
|
390
402
|
// and `wr-decrypt-text` carry. Hidden inline rather than through a class: this
|
|
391
403
|
// entry point ships no stylesheet, and a consumer who imported none would
|
|
392
404
|
// otherwise see the text twice.
|
|
393
|
-
|
|
394
|
-
|
|
405
|
+
//
|
|
406
|
+
// BOUND, not a static `style="…"` attribute. A real attribute is refused under
|
|
407
|
+
// `style-src 'self'`, and what is lost there is not a colour: the span drops
|
|
408
|
+
// every visual-hiding declaration and the word renders on screen beside the
|
|
409
|
+
// canvas that already draws it. A binding goes through `style.setProperty`,
|
|
410
|
+
// which is CSSOM and outside CSP's reach.
|
|
411
|
+
template: '<span class="wr-fuzzy-text__sr-only" [style]="srOnly">{{ text() }}</span>' +
|
|
395
412
|
'<canvas #canvas aria-hidden="true"></canvas>',
|
|
396
413
|
encapsulation: ViewEncapsulation.None,
|
|
397
|
-
host: { class: 'wr-fuzzy-text', style: '
|
|
414
|
+
host: { class: 'wr-fuzzy-text', '[style.color]': "'var(--wr-color-dark)'" },
|
|
398
415
|
}]
|
|
399
416
|
}], ctorParameters: () => [], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], fontSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "fontSize", required: false }] }], fontWeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "fontWeight", required: false }] }], fontFamily: [{ type: i0.Input, args: [{ isSignal: true, alias: "fontFamily", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], gradient: [{ type: i0.Input, args: [{ isSignal: true, alias: "gradient", required: false }] }], enableHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableHover", required: false }] }], baseIntensity: [{ type: i0.Input, args: [{ isSignal: true, alias: "baseIntensity", required: false }] }], hoverIntensity: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverIntensity", required: false }] }], fuzzRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "fuzzRange", required: false }] }], fps: [{ type: i0.Input, args: [{ isSignal: true, alias: "fps", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], transitionDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "transitionDuration", required: false }] }], clickEffect: [{ type: i0.Input, args: [{ isSignal: true, alias: "clickEffect", required: false }] }], glitchMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "glitchMode", required: false }] }], glitchInterval: [{ type: i0.Input, args: [{ isSignal: true, alias: "glitchInterval", required: false }] }], glitchDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "glitchDuration", required: false }] }], letterSpacing: [{ type: i0.Input, args: [{ isSignal: true, alias: "letterSpacing", required: false }] }], canvasRef: [{ type: i0.ViewChild, args: ['canvas', { isSignal: true }] }] } });
|
|
400
417
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-fuzzy-text.mjs","sources":["../../../projects/lib/fuzzy-text/fuzzy-text.ts","../../../projects/lib/fuzzy-text/ngwr-fuzzy-text.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the FuzzyText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/fuzzy-text\n *\n * Renders text to an offscreen 2D canvas, then redraws it onto the\n * visible canvas one row (or column) at a time with random horizontal /\n * vertical offsets. Intensity reacts to hover / click / glitch state.\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { isPlatformBrowser } from '@angular/common';\nimport type { ElementRef } from '@angular/core';\nimport {\n Component,\n DestroyRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n effect,\n inject,\n input,\n viewChild,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrFuzzyTextDirection } from './interfaces';\n\n/**\n * Canvas-rendered \"fuzzy\" text — each row/column of glyphs is offset by\n * a random amount per frame, giving the text a TV-static or motion-blur\n * appearance. Intensity ramps up on hover, click, and during periodic\n * glitch bursts (configurable).\n *\n * @example\n * ```html\n * <wr-fuzzy-text text=\"404\" />\n * <wr-fuzzy-text\n * text=\"HELLO\"\n * color=\"#0ea5e9\"\n * [fuzzRange]=\"50\"\n * [hoverIntensity]=\"0.8\"\n * [glitchMode]=\"true\"\n * />\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/fuzzy-text\n */\n@Component({\n selector: 'wr-fuzzy-text',\n // The text is drawn into the canvas as PIXELS, so without the first span there is\n // nothing for a screen reader to read at all — the same layer `wr-rotating-text`\n // and `wr-decrypt-text` carry. Hidden inline rather than through a class: this\n // entry point ships no stylesheet, and a consumer who imported none would\n // otherwise see the text twice.\n template:\n '<span class=\"wr-fuzzy-text__sr-only\" style=\"position:absolute;width:1px;height:1px;overflow:hidden;' +\n 'clip-path:inset(50%);white-space:nowrap\">{{ text() }}</span>' +\n '<canvas #canvas aria-hidden=\"true\"></canvas>',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-fuzzy-text', style: 'color: var(--wr-color-dark)' },\n})\nexport class WrFuzzyText {\n /** Text to render. */\n readonly text = input.required<string>();\n\n /** Font size as CSS length (`'4rem'`, `'72px'`, …) or px number. @default 'clamp(2rem, 10vw, 10rem)' */\n readonly fontSize = input<string | number>('clamp(2rem, 10vw, 10rem)');\n\n /** Font weight. @default 900 */\n readonly fontWeight = input<number | string>(900);\n\n /** Font family. `'inherit'` reads the canvas's computed style. @default 'inherit' */\n readonly fontFamily = input('inherit');\n\n /** Text colour. `'inherit'` reads the canvas's computed style. Ignored if `[gradient]` is set. @default 'inherit' */\n readonly color = input('inherit');\n\n /** Optional horizontal gradient stops. */\n readonly gradient = input<readonly string[] | null>(null);\n\n /** Enable hover / touch reactivity. @default true */\n readonly enableHover = input(true, { transform: coerceBooleanProperty });\n\n /** Base intensity (0..1) when idle. @default 0.18 */\n readonly baseIntensity = input(0.18, { transform: numAttr(0.18) });\n\n /** Intensity (0..1) while the pointer hovers the text. @default 0.5 */\n readonly hoverIntensity = input(0.5, { transform: numAttr(0.5) });\n\n /** Max pixel displacement per row/column at intensity 1. @default 30 */\n readonly fuzzRange = input(30, { transform: numAttr(30) });\n\n /** Frame rate cap. @default 60 */\n readonly fps = input(60, { transform: numAttr(60) });\n\n /** Fuzz direction. @default 'horizontal' */\n readonly direction = input<WrFuzzyTextDirection>('horizontal');\n\n /** Ms to ease intensity toward target. `0` snaps. @default 0 */\n readonly transitionDuration = input(0, { transform: numAttr(0) });\n\n /** Spike to full intensity briefly on click. @default false */\n readonly clickEffect = input(false, { transform: coerceBooleanProperty });\n\n /** Periodic \"glitch\" bursts at full intensity. @default false */\n readonly glitchMode = input(false, { transform: coerceBooleanProperty });\n\n /** Ms between glitch bursts. @default 2000 */\n readonly glitchInterval = input(2000, { transform: numAttr(2000) });\n\n /** Ms a glitch burst lasts. @default 200 */\n readonly glitchDuration = input(200, { transform: numAttr(200) });\n\n /** Extra pixels between glyphs. @default 0 */\n readonly letterSpacing = input(0, { transform: numAttr(0) });\n\n private readonly canvasRef = viewChild.required<ElementRef<HTMLCanvasElement>>('canvas');\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly platform = inject(WrPlatform);\n\n private teardown: (() => void) | undefined;\n\n /**\n * `init()` suspends on font loading, so the component can be destroyed while\n * it is awaiting. At that moment `teardown` is still undefined, so the\n * destroy hook has nothing to call — and everything the rest of `init()`\n * starts (the rAF loop, the glitch timers, canvas listeners) would run for\n * the life of the page. This flag lets the continuation bail out.\n */\n private destroyed = false;\n\n constructor() {\n if (!this.isBrowser) return;\n\n afterNextRender(() => {\n void this.init();\n });\n\n // Re-init whenever any input that affects the offscreen rendering changes.\n effect(() => {\n this.text();\n this.fontSize();\n this.fontWeight();\n this.fontFamily();\n this.color();\n this.gradient();\n this.letterSpacing();\n this.direction();\n this.fuzzRange();\n this.fps();\n this.glitchMode();\n this.glitchInterval();\n this.glitchDuration();\n // Skip if we haven't booted yet.\n if (!this.teardown) return;\n this.teardown();\n this.teardown = undefined;\n queueMicrotask(() => {\n void this.init();\n });\n });\n\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.teardown?.();\n });\n }\n\n // Init / loop\n\n private async init(): Promise<void> {\n const canvas = this.canvasRef().nativeElement;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const fontFamily =\n this.fontFamily() === 'inherit' ? window.getComputedStyle(canvas).fontFamily || 'sans-serif' : this.fontFamily();\n const color = this.color() === 'inherit' ? window.getComputedStyle(canvas).color || '#fff' : this.color();\n const fontSize = this.fontSize();\n const fontSizeStr = typeof fontSize === 'number' ? `${fontSize}px` : fontSize;\n const fontWeight = this.fontWeight();\n const fontString = `${fontWeight} ${fontSizeStr} ${fontFamily}`;\n\n try {\n await document.fonts.load(fontString);\n } catch {\n await document.fonts.ready;\n }\n\n // Destroyed while the fonts were loading — nothing below may start.\n if (this.destroyed) return;\n\n // Numeric font size for buffer maths.\n let numericFontSize: number;\n if (typeof fontSize === 'number') {\n numericFontSize = fontSize;\n } else {\n const tmp = document.createElement('span');\n tmp.style.fontSize = fontSize;\n document.body.appendChild(tmp);\n numericFontSize = Number.parseFloat(window.getComputedStyle(tmp).fontSize);\n document.body.removeChild(tmp);\n }\n\n const text = this.text();\n const letterSpacing = this.letterSpacing();\n const fuzzRange = this.fuzzRange();\n const direction = this.direction();\n\n // Offscreen canvas with the laid-out text.\n const offscreen = document.createElement('canvas');\n const offCtx = offscreen.getContext('2d');\n if (!offCtx) return;\n offCtx.font = fontString;\n offCtx.textBaseline = 'alphabetic';\n\n let totalWidth = 0;\n if (letterSpacing !== 0) {\n for (const ch of text) totalWidth += offCtx.measureText(ch).width + letterSpacing;\n totalWidth -= letterSpacing;\n } else {\n totalWidth = offCtx.measureText(text).width;\n }\n const m = offCtx.measureText(text);\n const actualLeft = m.actualBoundingBoxLeft ?? 0;\n const actualRight = letterSpacing !== 0 ? totalWidth : (m.actualBoundingBoxRight ?? m.width);\n const actualAscent = m.actualBoundingBoxAscent ?? numericFontSize;\n const actualDescent = m.actualBoundingBoxDescent ?? numericFontSize * 0.2;\n const textBoundingWidth = Math.ceil(letterSpacing !== 0 ? totalWidth : actualLeft + actualRight);\n const tightHeight = Math.ceil(actualAscent + actualDescent);\n\n const extraWidthBuffer = 10;\n const offscreenWidth = textBoundingWidth + extraWidthBuffer;\n offscreen.width = offscreenWidth;\n offscreen.height = tightHeight;\n const xOffset = extraWidthBuffer / 2;\n\n offCtx.font = fontString;\n offCtx.textBaseline = 'alphabetic';\n\n const gradient = this.gradient();\n if (gradient && gradient.length >= 2) {\n const g = offCtx.createLinearGradient(0, 0, offscreenWidth, 0);\n gradient.forEach((c, i) => g.addColorStop(i / (gradient.length - 1), c));\n offCtx.fillStyle = g;\n } else {\n offCtx.fillStyle = color;\n }\n\n if (letterSpacing !== 0) {\n let xPos = xOffset;\n for (const ch of text) {\n offCtx.fillText(ch, xPos, actualAscent);\n xPos += offCtx.measureText(ch).width + letterSpacing;\n }\n } else {\n offCtx.fillText(text, xOffset - actualLeft, actualAscent);\n }\n\n // Size the visible canvas, leaving room for the max horizontal fuzz.\n const horizontalMargin = fuzzRange + 20;\n const verticalMargin = 0;\n canvas.width = offscreenWidth + horizontalMargin * 2;\n canvas.height = tightHeight + verticalMargin * 2;\n ctx.translate(horizontalMargin, verticalMargin);\n\n const interactiveLeft = horizontalMargin + xOffset;\n const interactiveTop = verticalMargin;\n const interactiveRight = interactiveLeft + textBoundingWidth;\n const interactiveBottom = interactiveTop + tightHeight;\n\n // Reduced motion: paint the text once, clean — no fuzz loop, no\n // glitch bursts, no pointer reactivity. Teardown must still be set\n // so the re-init effect keeps redrawing on input changes.\n if (this.platform.prefersReducedMotion()) {\n ctx.drawImage(offscreen, 0, 0);\n this.teardown = (): void => undefined;\n return;\n }\n\n let isHovering = false;\n let isClicking = false;\n let isGlitching = false;\n let currentIntensity = this.baseIntensity();\n let targetIntensity = currentIntensity;\n let lastFrame = 0;\n const frameDuration = 1000 / this.fps();\n\n let rafId = 0;\n let glitchOnTimer: ReturnType<typeof setTimeout> | undefined;\n let glitchOffTimer: ReturnType<typeof setTimeout> | undefined;\n let clickTimer: ReturnType<typeof setTimeout> | undefined;\n\n const startGlitchLoop = (): void => {\n if (!this.glitchMode()) return;\n glitchOnTimer = setTimeout(() => {\n isGlitching = true;\n glitchOffTimer = setTimeout(() => {\n isGlitching = false;\n startGlitchLoop();\n }, this.glitchDuration());\n }, this.glitchInterval());\n };\n if (this.glitchMode()) startGlitchLoop();\n\n const run = (ts: number): void => {\n if (ts - lastFrame < frameDuration) {\n rafId = requestAnimationFrame(run);\n return;\n }\n lastFrame = ts;\n\n ctx.clearRect(\n -fuzzRange - 20,\n -fuzzRange - 10,\n offscreenWidth + 2 * (fuzzRange + 20),\n tightHeight + 2 * (fuzzRange + 10)\n );\n\n if (isClicking) targetIntensity = 1;\n else if (isGlitching) targetIntensity = 1;\n else if (isHovering) targetIntensity = this.hoverIntensity();\n else targetIntensity = this.baseIntensity();\n\n const td = this.transitionDuration();\n if (td > 0) {\n const step = 1 / (td / frameDuration);\n if (currentIntensity < targetIntensity) currentIntensity = Math.min(currentIntensity + step, targetIntensity);\n else if (currentIntensity > targetIntensity)\n currentIntensity = Math.max(currentIntensity - step, targetIntensity);\n } else {\n currentIntensity = targetIntensity;\n }\n\n if (direction === 'horizontal') {\n for (let j = 0; j < tightHeight; j++) {\n const dx = Math.floor(currentIntensity * (Math.random() - 0.5) * fuzzRange);\n ctx.drawImage(offscreen, 0, j, offscreenWidth, 1, dx, j, offscreenWidth, 1);\n }\n } else if (direction === 'vertical') {\n for (let i = 0; i < offscreenWidth; i++) {\n const dy = Math.floor(currentIntensity * (Math.random() - 0.5) * fuzzRange);\n ctx.drawImage(offscreen, i, 0, 1, tightHeight, i, dy, 1, tightHeight);\n }\n } else {\n // both: horizontal pass then vertical pass on the result\n for (let j = 0; j < tightHeight; j++) {\n const dx = Math.floor(currentIntensity * (Math.random() - 0.5) * fuzzRange);\n ctx.drawImage(offscreen, 0, j, offscreenWidth, 1, dx, j, offscreenWidth, 1);\n }\n const w = offscreenWidth + fuzzRange;\n const h = tightHeight + fuzzRange;\n const data = ctx.getImageData(0, 0, w, h);\n ctx.clearRect(\n -fuzzRange - 20,\n -fuzzRange - 10,\n offscreenWidth + 2 * (fuzzRange + 20),\n tightHeight + 2 * (fuzzRange + 10)\n );\n ctx.putImageData(data, 0, 0);\n for (let i = 0; i < w; i++) {\n const dy = Math.floor(currentIntensity * (Math.random() - 0.5) * fuzzRange * 0.5);\n const col = ctx.getImageData(i, 0, 1, h);\n ctx.clearRect(i, -fuzzRange, 1, tightHeight + 2 * fuzzRange);\n ctx.putImageData(col, i, dy);\n }\n }\n\n rafId = requestAnimationFrame(run);\n };\n rafId = requestAnimationFrame(run);\n\n const isInsideText = (x: number, y: number): boolean =>\n x >= interactiveLeft && x <= interactiveRight && y >= interactiveTop && y <= interactiveBottom;\n\n const onMouseMove = (e: MouseEvent): void => {\n if (!this.enableHover()) return;\n const rect = canvas.getBoundingClientRect();\n isHovering = isInsideText(e.clientX - rect.left, e.clientY - rect.top);\n };\n const onMouseLeave = (): void => {\n isHovering = false;\n };\n const onTouchMove = (e: TouchEvent): void => {\n if (!this.enableHover()) return;\n e.preventDefault();\n const rect = canvas.getBoundingClientRect();\n const t = e.touches[0];\n isHovering = isInsideText(t.clientX - rect.left, t.clientY - rect.top);\n };\n const onTouchEnd = (): void => {\n isHovering = false;\n };\n const onClick = (): void => {\n if (!this.clickEffect()) return;\n isClicking = true;\n clearTimeout(clickTimer);\n clickTimer = setTimeout(() => {\n isClicking = false;\n }, 150);\n };\n\n // Attached unconditionally, and gated inside the three handlers that can SET a\n // reactive state instead. `enableHover` and `clickEffect` are not in the re-init\n // effect's dependency list, so an attach conditional on them read the value at\n // boot and never again: a `[clickEffect]` bound to a signal that starts `false` —\n // the default, so the ordinary case — stayed dead after it flipped true. Adding\n // them to that list would restart the rAF loop and the glitch timers just to\n // toggle a listener, which is visible; the guards are not. `onTouchMove` calls\n // `preventDefault()` only past its own guard, so a hover-disabled instance still\n // swallows nothing.\n //\n // `onMouseLeave` / `onTouchEnd` stay ungated deliberately: they only ever CLEAR\n // toward the idle state, and gating them would strand `isHovering` on for good\n // when hover is switched off mid-hover.\n canvas.addEventListener('mousemove', onMouseMove);\n canvas.addEventListener('mouseleave', onMouseLeave);\n canvas.addEventListener('touchmove', onTouchMove, { passive: false });\n canvas.addEventListener('touchend', onTouchEnd);\n canvas.addEventListener('click', onClick);\n\n this.teardown = (): void => {\n cancelAnimationFrame(rafId);\n clearTimeout(glitchOnTimer);\n clearTimeout(glitchOffTimer);\n clearTimeout(clickTimer);\n canvas.removeEventListener('mousemove', onMouseMove);\n canvas.removeEventListener('mouseleave', onMouseLeave);\n canvas.removeEventListener('touchmove', onTouchMove);\n canvas.removeEventListener('touchend', onTouchEnd);\n canvas.removeEventListener('click', onClick);\n };\n }\n}\n\nexport type { WrFuzzyTextDirection } from './interfaces';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-fuzzy-text.mjs","sources":["../../../projects/lib/fuzzy-text/fuzzy-text.ts","../../../projects/lib/fuzzy-text/ngwr-fuzzy-text.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the FuzzyText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/fuzzy-text\n *\n * Renders text to an offscreen 2D canvas, then redraws it onto the\n * visible canvas one row (or column) at a time with random horizontal /\n * vertical offsets. Intensity reacts to hover / click / glitch state.\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { isPlatformBrowser } from '@angular/common';\nimport type { ElementRef } from '@angular/core';\nimport {\n Component,\n DestroyRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n effect,\n inject,\n input,\n viewChild,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrFuzzyTextDirection } from './interfaces';\n\n/**\n * Canvas-rendered \"fuzzy\" text — each row/column of glyphs is offset by\n * a random amount per frame, giving the text a TV-static or motion-blur\n * appearance. Intensity ramps up on hover, click, and during periodic\n * glitch bursts (configurable).\n *\n * @example\n * ```html\n * <wr-fuzzy-text text=\"404\" />\n * <wr-fuzzy-text\n * text=\"HELLO\"\n * color=\"#0ea5e9\"\n * [fuzzRange]=\"50\"\n * [hoverIntensity]=\"0.8\"\n * [glitchMode]=\"true\"\n * />\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/fuzzy-text\n */\n@Component({\n selector: 'wr-fuzzy-text',\n // The text is drawn into the canvas as PIXELS, so without the first span there is\n // nothing for a screen reader to read at all — the same layer `wr-rotating-text`\n // and `wr-decrypt-text` carry. Hidden inline rather than through a class: this\n // entry point ships no stylesheet, and a consumer who imported none would\n // otherwise see the text twice.\n //\n // BOUND, not a static `style=\"…\"` attribute. A real attribute is refused under\n // `style-src 'self'`, and what is lost there is not a colour: the span drops\n // every visual-hiding declaration and the word renders on screen beside the\n // canvas that already draws it. A binding goes through `style.setProperty`,\n // which is CSSOM and outside CSP's reach.\n template:\n '<span class=\"wr-fuzzy-text__sr-only\" [style]=\"srOnly\">{{ text() }}</span>' +\n '<canvas #canvas aria-hidden=\"true\"></canvas>',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-fuzzy-text', '[style.color]': \"'var(--wr-color-dark)'\" },\n})\nexport class WrFuzzyText {\n /**\n * Visual-hiding declarations for the screen-reader copy of the text. A stable\n * object literal, so the binding is written once rather than on every pass.\n */\n protected readonly srOnly: Readonly<Record<string, string>> = {\n position: 'absolute',\n width: '1px',\n height: '1px',\n overflow: 'hidden',\n 'clip-path': 'inset(50%)',\n 'white-space': 'nowrap',\n };\n\n /** Text to render. */\n readonly text = input.required<string>();\n\n /** Font size as CSS length (`'4rem'`, `'72px'`, …) or px number. @default 'clamp(2rem, 10vw, 10rem)' */\n readonly fontSize = input<string | number>('clamp(2rem, 10vw, 10rem)');\n\n /** Font weight. @default 900 */\n readonly fontWeight = input<number | string>(900);\n\n /** Font family. `'inherit'` reads the canvas's computed style. @default 'inherit' */\n readonly fontFamily = input('inherit');\n\n /** Text colour. `'inherit'` reads the canvas's computed style. Ignored if `[gradient]` is set. @default 'inherit' */\n readonly color = input('inherit');\n\n /** Optional horizontal gradient stops. */\n readonly gradient = input<readonly string[] | null>(null);\n\n /** Enable hover / touch reactivity. @default true */\n readonly enableHover = input(true, { transform: coerceBooleanProperty });\n\n /** Base intensity (0..1) when idle. @default 0.18 */\n readonly baseIntensity = input(0.18, { transform: numAttr(0.18) });\n\n /** Intensity (0..1) while the pointer hovers the text. @default 0.5 */\n readonly hoverIntensity = input(0.5, { transform: numAttr(0.5) });\n\n /** Max pixel displacement per row/column at intensity 1. @default 30 */\n readonly fuzzRange = input(30, { transform: numAttr(30) });\n\n /** Frame rate cap. @default 60 */\n readonly fps = input(60, { transform: numAttr(60) });\n\n /** Fuzz direction. @default 'horizontal' */\n readonly direction = input<WrFuzzyTextDirection>('horizontal');\n\n /** Ms to ease intensity toward target. `0` snaps. @default 0 */\n readonly transitionDuration = input(0, { transform: numAttr(0) });\n\n /** Spike to full intensity briefly on click. @default false */\n readonly clickEffect = input(false, { transform: coerceBooleanProperty });\n\n /** Periodic \"glitch\" bursts at full intensity. @default false */\n readonly glitchMode = input(false, { transform: coerceBooleanProperty });\n\n /** Ms between glitch bursts. @default 2000 */\n readonly glitchInterval = input(2000, { transform: numAttr(2000) });\n\n /** Ms a glitch burst lasts. @default 200 */\n readonly glitchDuration = input(200, { transform: numAttr(200) });\n\n /** Extra pixels between glyphs. @default 0 */\n readonly letterSpacing = input(0, { transform: numAttr(0) });\n\n private readonly canvasRef = viewChild.required<ElementRef<HTMLCanvasElement>>('canvas');\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly platform = inject(WrPlatform);\n\n private teardown: (() => void) | undefined;\n\n /**\n * `init()` suspends on font loading, so the component can be destroyed while\n * it is awaiting. At that moment `teardown` is still undefined, so the\n * destroy hook has nothing to call — and everything the rest of `init()`\n * starts (the rAF loop, the glitch timers, canvas listeners) would run for\n * the life of the page. This flag lets the continuation bail out.\n */\n private destroyed = false;\n\n constructor() {\n if (!this.isBrowser) return;\n\n afterNextRender(() => {\n void this.init();\n });\n\n // Re-init whenever any input that affects the offscreen rendering changes.\n effect(() => {\n this.text();\n this.fontSize();\n this.fontWeight();\n this.fontFamily();\n this.color();\n this.gradient();\n this.letterSpacing();\n this.direction();\n this.fuzzRange();\n this.fps();\n this.glitchMode();\n this.glitchInterval();\n this.glitchDuration();\n // Skip if we haven't booted yet.\n if (!this.teardown) return;\n this.teardown();\n this.teardown = undefined;\n queueMicrotask(() => {\n void this.init();\n });\n });\n\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.teardown?.();\n });\n }\n\n // Init / loop\n\n private async init(): Promise<void> {\n const canvas = this.canvasRef().nativeElement;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const fontFamily =\n this.fontFamily() === 'inherit' ? window.getComputedStyle(canvas).fontFamily || 'sans-serif' : this.fontFamily();\n const color = this.color() === 'inherit' ? window.getComputedStyle(canvas).color || '#fff' : this.color();\n const fontSize = this.fontSize();\n const fontSizeStr = typeof fontSize === 'number' ? `${fontSize}px` : fontSize;\n const fontWeight = this.fontWeight();\n const fontString = `${fontWeight} ${fontSizeStr} ${fontFamily}`;\n\n try {\n await document.fonts.load(fontString);\n } catch {\n await document.fonts.ready;\n }\n\n // Destroyed while the fonts were loading — nothing below may start.\n if (this.destroyed) return;\n\n // Numeric font size for buffer maths.\n let numericFontSize: number;\n if (typeof fontSize === 'number') {\n numericFontSize = fontSize;\n } else {\n const tmp = document.createElement('span');\n tmp.style.fontSize = fontSize;\n document.body.appendChild(tmp);\n numericFontSize = Number.parseFloat(window.getComputedStyle(tmp).fontSize);\n document.body.removeChild(tmp);\n }\n\n const text = this.text();\n const letterSpacing = this.letterSpacing();\n const fuzzRange = this.fuzzRange();\n const direction = this.direction();\n\n // Offscreen canvas with the laid-out text.\n const offscreen = document.createElement('canvas');\n const offCtx = offscreen.getContext('2d');\n if (!offCtx) return;\n offCtx.font = fontString;\n offCtx.textBaseline = 'alphabetic';\n\n let totalWidth = 0;\n if (letterSpacing !== 0) {\n for (const ch of text) totalWidth += offCtx.measureText(ch).width + letterSpacing;\n totalWidth -= letterSpacing;\n } else {\n totalWidth = offCtx.measureText(text).width;\n }\n const m = offCtx.measureText(text);\n const actualLeft = m.actualBoundingBoxLeft ?? 0;\n const actualRight = letterSpacing !== 0 ? totalWidth : (m.actualBoundingBoxRight ?? m.width);\n const actualAscent = m.actualBoundingBoxAscent ?? numericFontSize;\n const actualDescent = m.actualBoundingBoxDescent ?? numericFontSize * 0.2;\n const textBoundingWidth = Math.ceil(letterSpacing !== 0 ? totalWidth : actualLeft + actualRight);\n const tightHeight = Math.ceil(actualAscent + actualDescent);\n\n const extraWidthBuffer = 10;\n const offscreenWidth = textBoundingWidth + extraWidthBuffer;\n offscreen.width = offscreenWidth;\n offscreen.height = tightHeight;\n const xOffset = extraWidthBuffer / 2;\n\n offCtx.font = fontString;\n offCtx.textBaseline = 'alphabetic';\n\n const gradient = this.gradient();\n if (gradient && gradient.length >= 2) {\n const g = offCtx.createLinearGradient(0, 0, offscreenWidth, 0);\n gradient.forEach((c, i) => g.addColorStop(i / (gradient.length - 1), c));\n offCtx.fillStyle = g;\n } else {\n offCtx.fillStyle = color;\n }\n\n if (letterSpacing !== 0) {\n let xPos = xOffset;\n for (const ch of text) {\n offCtx.fillText(ch, xPos, actualAscent);\n xPos += offCtx.measureText(ch).width + letterSpacing;\n }\n } else {\n offCtx.fillText(text, xOffset - actualLeft, actualAscent);\n }\n\n // Size the visible canvas, leaving room for the max horizontal fuzz.\n const horizontalMargin = fuzzRange + 20;\n const verticalMargin = 0;\n canvas.width = offscreenWidth + horizontalMargin * 2;\n canvas.height = tightHeight + verticalMargin * 2;\n ctx.translate(horizontalMargin, verticalMargin);\n\n const interactiveLeft = horizontalMargin + xOffset;\n const interactiveTop = verticalMargin;\n const interactiveRight = interactiveLeft + textBoundingWidth;\n const interactiveBottom = interactiveTop + tightHeight;\n\n // Reduced motion: paint the text once, clean — no fuzz loop, no\n // glitch bursts, no pointer reactivity. Teardown must still be set\n // so the re-init effect keeps redrawing on input changes.\n if (this.platform.prefersReducedMotion()) {\n ctx.drawImage(offscreen, 0, 0);\n this.teardown = (): void => undefined;\n return;\n }\n\n let isHovering = false;\n let isClicking = false;\n let isGlitching = false;\n let currentIntensity = this.baseIntensity();\n let targetIntensity = currentIntensity;\n let lastFrame = 0;\n const frameDuration = 1000 / this.fps();\n\n let rafId = 0;\n let glitchOnTimer: ReturnType<typeof setTimeout> | undefined;\n let glitchOffTimer: ReturnType<typeof setTimeout> | undefined;\n let clickTimer: ReturnType<typeof setTimeout> | undefined;\n\n const startGlitchLoop = (): void => {\n if (!this.glitchMode()) return;\n glitchOnTimer = setTimeout(() => {\n isGlitching = true;\n glitchOffTimer = setTimeout(() => {\n isGlitching = false;\n startGlitchLoop();\n }, this.glitchDuration());\n }, this.glitchInterval());\n };\n if (this.glitchMode()) startGlitchLoop();\n\n const run = (ts: number): void => {\n if (ts - lastFrame < frameDuration) {\n rafId = requestAnimationFrame(run);\n return;\n }\n lastFrame = ts;\n\n ctx.clearRect(\n -fuzzRange - 20,\n -fuzzRange - 10,\n offscreenWidth + 2 * (fuzzRange + 20),\n tightHeight + 2 * (fuzzRange + 10)\n );\n\n if (isClicking) targetIntensity = 1;\n else if (isGlitching) targetIntensity = 1;\n else if (isHovering) targetIntensity = this.hoverIntensity();\n else targetIntensity = this.baseIntensity();\n\n const td = this.transitionDuration();\n if (td > 0) {\n const step = 1 / (td / frameDuration);\n if (currentIntensity < targetIntensity) currentIntensity = Math.min(currentIntensity + step, targetIntensity);\n else if (currentIntensity > targetIntensity)\n currentIntensity = Math.max(currentIntensity - step, targetIntensity);\n } else {\n currentIntensity = targetIntensity;\n }\n\n if (direction === 'horizontal') {\n for (let j = 0; j < tightHeight; j++) {\n const dx = Math.floor(currentIntensity * (Math.random() - 0.5) * fuzzRange);\n ctx.drawImage(offscreen, 0, j, offscreenWidth, 1, dx, j, offscreenWidth, 1);\n }\n } else if (direction === 'vertical') {\n for (let i = 0; i < offscreenWidth; i++) {\n const dy = Math.floor(currentIntensity * (Math.random() - 0.5) * fuzzRange);\n ctx.drawImage(offscreen, i, 0, 1, tightHeight, i, dy, 1, tightHeight);\n }\n } else {\n // both: horizontal pass then vertical pass on the result\n for (let j = 0; j < tightHeight; j++) {\n const dx = Math.floor(currentIntensity * (Math.random() - 0.5) * fuzzRange);\n ctx.drawImage(offscreen, 0, j, offscreenWidth, 1, dx, j, offscreenWidth, 1);\n }\n const w = offscreenWidth + fuzzRange;\n const h = tightHeight + fuzzRange;\n const data = ctx.getImageData(0, 0, w, h);\n ctx.clearRect(\n -fuzzRange - 20,\n -fuzzRange - 10,\n offscreenWidth + 2 * (fuzzRange + 20),\n tightHeight + 2 * (fuzzRange + 10)\n );\n ctx.putImageData(data, 0, 0);\n for (let i = 0; i < w; i++) {\n const dy = Math.floor(currentIntensity * (Math.random() - 0.5) * fuzzRange * 0.5);\n const col = ctx.getImageData(i, 0, 1, h);\n ctx.clearRect(i, -fuzzRange, 1, tightHeight + 2 * fuzzRange);\n ctx.putImageData(col, i, dy);\n }\n }\n\n rafId = requestAnimationFrame(run);\n };\n rafId = requestAnimationFrame(run);\n\n const isInsideText = (x: number, y: number): boolean =>\n x >= interactiveLeft && x <= interactiveRight && y >= interactiveTop && y <= interactiveBottom;\n\n const onMouseMove = (e: MouseEvent): void => {\n if (!this.enableHover()) return;\n const rect = canvas.getBoundingClientRect();\n isHovering = isInsideText(e.clientX - rect.left, e.clientY - rect.top);\n };\n const onMouseLeave = (): void => {\n isHovering = false;\n };\n const onTouchMove = (e: TouchEvent): void => {\n if (!this.enableHover()) return;\n e.preventDefault();\n const rect = canvas.getBoundingClientRect();\n const t = e.touches[0];\n isHovering = isInsideText(t.clientX - rect.left, t.clientY - rect.top);\n };\n const onTouchEnd = (): void => {\n isHovering = false;\n };\n const onClick = (): void => {\n if (!this.clickEffect()) return;\n isClicking = true;\n clearTimeout(clickTimer);\n clickTimer = setTimeout(() => {\n isClicking = false;\n }, 150);\n };\n\n // Attached unconditionally, and gated inside the three handlers that can SET a\n // reactive state instead. `enableHover` and `clickEffect` are not in the re-init\n // effect's dependency list, so an attach conditional on them read the value at\n // boot and never again: a `[clickEffect]` bound to a signal that starts `false` —\n // the default, so the ordinary case — stayed dead after it flipped true. Adding\n // them to that list would restart the rAF loop and the glitch timers just to\n // toggle a listener, which is visible; the guards are not. `onTouchMove` calls\n // `preventDefault()` only past its own guard, so a hover-disabled instance still\n // swallows nothing.\n //\n // `onMouseLeave` / `onTouchEnd` stay ungated deliberately: they only ever CLEAR\n // toward the idle state, and gating them would strand `isHovering` on for good\n // when hover is switched off mid-hover.\n canvas.addEventListener('mousemove', onMouseMove);\n canvas.addEventListener('mouseleave', onMouseLeave);\n canvas.addEventListener('touchmove', onTouchMove, { passive: false });\n canvas.addEventListener('touchend', onTouchEnd);\n canvas.addEventListener('click', onClick);\n\n this.teardown = (): void => {\n cancelAnimationFrame(rafId);\n clearTimeout(glitchOnTimer);\n clearTimeout(glitchOffTimer);\n clearTimeout(clickTimer);\n canvas.removeEventListener('mousemove', onMouseMove);\n canvas.removeEventListener('mouseleave', onMouseLeave);\n canvas.removeEventListener('touchmove', onTouchMove);\n canvas.removeEventListener('touchend', onTouchEnd);\n canvas.removeEventListener('click', onClick);\n };\n }\n}\n\nexport type { WrFuzzyTextDirection } from './interfaces';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|