ngwr 14.5.0 → 14.6.0

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.
Files changed (48) hide show
  1. package/README.md +9 -3
  2. package/avatar/styles/_index.scss +46 -0
  3. package/fesm2022/ngwr-avatar.mjs +58 -7
  4. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  5. package/fesm2022/ngwr-i18n-en.mjs +16 -5
  6. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  7. package/fesm2022/ngwr-i18n.mjs +13 -0
  8. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  9. package/fesm2022/ngwr-image-cropper-testing.mjs +34 -3
  10. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
  11. package/fesm2022/ngwr-image-cropper.mjs +98 -5
  12. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  13. package/fesm2022/ngwr-pagination.mjs +1 -1
  14. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  15. package/fesm2022/ngwr-schema-form.mjs +1 -1
  16. package/fesm2022/ngwr-schema-form.mjs.map +1 -1
  17. package/fesm2022/ngwr-select-testing.mjs +21 -2
  18. package/fesm2022/ngwr-select-testing.mjs.map +1 -1
  19. package/fesm2022/ngwr-select.mjs +233 -35
  20. package/fesm2022/ngwr-select.mjs.map +1 -1
  21. package/fesm2022/ngwr-table.mjs +10 -2
  22. package/fesm2022/ngwr-table.mjs.map +1 -1
  23. package/fesm2022/ngwr-version.mjs +1 -1
  24. package/fesm2022/ngwr-version.mjs.map +1 -1
  25. package/image-cropper/styles/_index.scss +7 -0
  26. package/llms-full.txt +5 -5
  27. package/llms.txt +12 -3
  28. package/mcp/providers.js +8 -2
  29. package/package.json +1 -1
  30. package/schematics/migrations/v14/index.js +90 -34
  31. package/schematics/migrations/v7/index.js +15 -4
  32. package/schematics/migrations/v8/index.js +13 -2
  33. package/schematics/migrations/v9/index.js +19 -10
  34. package/schematics/use/symbol-map.json +1 -0
  35. package/select/styles/_index.scss +34 -0
  36. package/skills/ngwr/SKILL.md +9 -4
  37. package/skills/ngwr/references/catalog.md +1 -1
  38. package/skills/ngwr/references/setup.md +2 -2
  39. package/table/styles/_index.scss +39 -12
  40. package/types/ngwr-avatar.d.ts +36 -3
  41. package/types/ngwr-i18n-en.d.ts +16 -5
  42. package/types/ngwr-i18n.d.ts +13 -0
  43. package/types/ngwr-image-cropper-testing.d.ts +25 -3
  44. package/types/ngwr-image-cropper.d.ts +88 -2
  45. package/types/ngwr-select-testing.d.ts +18 -1
  46. package/types/ngwr-select.d.ts +181 -10
  47. package/types/ngwr-table.d.ts +8 -0
  48. package/types/ngwr-version.d.ts +1 -1
@@ -389,6 +389,14 @@
389
389
  padding: 0;
390
390
  cursor: pointer;
391
391
  color: inherit;
392
+ // The indicator inside is a bare `.wr-icon__svg`, which is `1em`, and a
393
+ // `<button>` does not inherit its font-size: it takes the browser's control
394
+ // font (about 13.33px) unless the app resets it. So the same glyph measured
395
+ // 13.33px on a page with no reset and 12px under `ngwr/reset`, which sets
396
+ // `font: inherit`. Inheriting the size makes `1em` the header's own text in
397
+ // every app. `--wr-icon-size` cannot do this job — only a `<wr-icon>` host
398
+ // reads it, and the rule here used to set it for nothing.
399
+ font-size: inherit;
392
400
  line-height: 0;
393
401
  }
394
402
 
@@ -741,21 +749,35 @@
741
749
 
742
750
  .wr-table-sort {
743
751
  display: inline-flex;
744
- --wr-icon-size: 0.875rem;
752
+ // Unsorted is the flat line the template draws, at full strength in the muted
753
+ // role — the same ink as the filter funnel beside it. It used to be the sort
754
+ // ARROW at half opacity, which is how an unsorted column came to read as
755
+ // "sorted ascending, dimmed". The glyph carries the state now, so the opacity
756
+ // has no job left, and dropping it is what lifts the indicator over the 3:1 a
757
+ // graphic that identifies a control is held to. Measured on the header's own
758
+ // background in Chromium, after the colour transition settled: from 1.98:1 to
759
+ // 4.78:1 in light (`#5f6c7d` on `#eff2f6`) and from 2.74:1 to 7.04:1 in dark
760
+ // (`#9aa6b8` on `#131a2b`). An SVG stroke is invisible to axe, so re-measure
761
+ // by hand if this ink or the header fill ever moves.
745
762
  color: var(--wr-color-on-surface-muted);
746
- transition:
747
- transform var(--wr-transition-base),
748
- color var(--wr-transition-base);
749
- opacity: 0.5;
750
-
751
- &--asc {
752
- opacity: 1;
763
+ // Only the colour eases back to unsorted. A transition is taken from the
764
+ // state being ENTERED, so leaving `--desc` for the base would otherwise spin
765
+ // the flat line from 180deg to 0 — motion with no direction to report. The
766
+ // turn between the two arrows still animates, since both modifiers carry it.
767
+ transition: color var(--wr-transition-base);
768
+
769
+ // The template swaps the glyph — a flat line while unsorted, an arrow here
770
+ // (table-sort.ts) — so the state reads from the shape, and the primary ink
771
+ // only says which column is the sorted one.
772
+ &--asc,
773
+ &--desc {
753
774
  color: var(--wr-color-primary);
775
+ transition:
776
+ transform var(--wr-transition-base),
777
+ color var(--wr-transition-base);
754
778
  }
755
779
 
756
780
  &--desc {
757
- opacity: 1;
758
- color: var(--wr-color-primary);
759
781
  transform: rotate(180deg);
760
782
  }
761
783
  }
@@ -781,7 +803,13 @@
781
803
  padding: 0;
782
804
  cursor: pointer;
783
805
  color: inherit;
784
- --wr-icon-size: 0.875rem;
806
+ // The funnel is the same bare `1em` svg as the sort indicator beside it, in
807
+ // the same kind of `<button>`, so it inherits the header's font-size for the
808
+ // same reason (see `.wr-table__sort-btn`). The size only, not the `font`
809
+ // shorthand: that would also bring the header's 1.25rem line-height, and the
810
+ // count pill beside the funnel — 10px type with no line-height of its own —
811
+ // grows from 12px to 20px tall under it.
812
+ font-size: inherit;
785
813
  }
786
814
 
787
815
  &__count {
@@ -855,7 +883,6 @@
855
883
  border-radius: var(--wr-border-radius-sm);
856
884
  border: 1px solid var(--wr-color-outline);
857
885
  color: transparent;
858
- --wr-icon-size: 0.75rem;
859
886
  transition: var(--wr-transition-base);
860
887
  }
861
888
 
@@ -40,19 +40,25 @@ type WrAvatarSize = number | `${number}` | `${number}px` | `${number}rem`;
40
40
  /**
41
41
  * Image avatar with configurable corner treatment.
42
42
  *
43
- * A spinner is shown while the image loads, then crossfades in.
43
+ * Projected content (typically initials) is the FALLBACK: it shows with no
44
+ * `url`, while the image loads and after it fails, and leaves the paint once the
45
+ * image has loaded. It is projected into a `.wr-avatar__content` wrapper, so a
46
+ * `wr-avatar > …` child selector no longer reaches it. While it is there, no
47
+ * spinner is drawn over it; with nothing projected, the spinner marks the
48
+ * loading state instead. See `alt` for what a screen reader hears.
44
49
  *
45
50
  * @example
46
51
  * ```html
47
52
  * <wr-avatar url="/me.png" alt="Roman" size="3rem" shape="circle" />
48
- * <wr-avatar url="/me.png" alt="Roman" [size]="48" />
53
+ * <wr-avatar [url]="photo" alt="Roman Khegay" [size]="48">RK</wr-avatar>
49
54
  * ```
50
55
  *
51
56
  * @see https://ngwr.dev/reference/components/avatar
52
57
  */
53
58
  declare class WrAvatar {
54
59
  /**
55
- * Image URL. When unset, only projected content (e.g. initials) renders.
60
+ * Image URL. Projected content (e.g. initials) shows while it is unset,
61
+ * loading or broken, and gives way once the image has loaded.
56
62
  *
57
63
  * @default null
58
64
  */
@@ -61,6 +67,11 @@ declare class WrAvatar {
61
67
  * Alt text for the image. Falls back to `avatar.alt`, then `'Avatar'` — pass
62
68
  * the person's name where you have it, since that is what a reader wants.
63
69
  *
70
+ * With an `alt`, projected initials are hidden from assistive technology while
71
+ * an image is in play, so the name is announced once. Without one, the image
72
+ * carries only the generic name, so the initials stay announced beside it —
73
+ * visually hidden once it has loaded, never dropped.
74
+ *
64
75
  * @default null
65
76
  */
66
77
  readonly alt: _angular_core.InputSignal<string | null>;
@@ -85,8 +96,30 @@ declare class WrAvatar {
85
96
  * worth showing instead.
86
97
  */
87
98
  protected readonly failed: _angular_core.WritableSignal<boolean>;
99
+ private readonly img;
88
100
  constructor();
101
+ /**
102
+ * A prerendered `<img>` starts loading when the browser parses the HTML, and
103
+ * `(load)` / `(error)` are only attached at hydration — so on a slow bundle the
104
+ * image settles first, both events go nowhere, and the avatar sits on its
105
+ * fallback over a photo it already has (or names a broken image it never
106
+ * learns about). Only the FIRST render can race: every later `<img>` is
107
+ * created on the client with its listeners in place before `src` is set.
108
+ */
109
+ private adoptSettledImage;
89
110
  protected readonly resolved: _angular_core.Signal<ResolvedCssSize>;
111
+ /**
112
+ * Whether the projected fallback is `aria-hidden`: an `<img>` is in the DOM
113
+ * (loading or loaded) AND the consumer named it. Then the image's `alt` owns
114
+ * the name, announced once and unchanged at the moment the photo arrives.
115
+ * Without a real `alt` the image is only "Avatar", and hiding the initials
116
+ * would drop the one thing that says who this is.
117
+ *
118
+ * The stylesheet keys on the same attribute once `--loaded`: a hidden fallback
119
+ * leaves the paint through `visibility`, a named-by-initials one is clipped and
120
+ * stays in the accessibility tree. While loading it is painted either way.
121
+ */
122
+ protected readonly fallbackHidden: _angular_core.Signal<boolean>;
90
123
  protected readonly cssSize: _angular_core.Signal<string>;
91
124
  protected readonly pxSize: _angular_core.Signal<number | null>;
92
125
  protected readonly classes: _angular_core.Signal<string>;
@@ -1,16 +1,27 @@
1
1
  import { WrI18nCatalog } from 'ngwr/i18n';
2
2
 
3
3
  /**
4
- * Base English catalog for ngwr built-in component strings. Spread this
5
- * into your root catalog or pass it to `provideWrI18nStaticLoader`.
4
+ * Base English catalog for ngwr built-in component strings. Register it with
5
+ * `provideWrI18nBaseCatalogs` and keep only your own keys in the loader.
6
+ *
7
+ * Do not spread it into your own catalog. A spread is shallow, so any namespace
8
+ * you share with ngwr, e.g. `common`, `validation`, `table`, keeps only the keys
9
+ * of the side spread last, and the other side's keys stop resolving with nothing
10
+ * logged: ngwr's components fall back to their English defaults, and a `wrT`
11
+ * read of a lost key renders the raw key under the default missing handler. A
12
+ * base catalog is looked up key by key underneath yours, so both halves of a
13
+ * shared namespace resolve.
6
14
  *
7
15
  * @example
8
16
  * ```ts
17
+ * import { provideWrI18n, provideWrI18nBaseCatalogs, provideWrI18nStaticLoader } from 'ngwr/i18n';
9
18
  * import { wrEn } from 'ngwr/i18n/en';
10
19
  *
11
- * provideWrI18nStaticLoader({
12
- * en: { ...wrEn, app: { title: 'My app' } },
13
- * });
20
+ * providers: [
21
+ * provideWrI18n(),
22
+ * provideWrI18nBaseCatalogs({ en: wrEn }),
23
+ * provideWrI18nStaticLoader({ en: { app: { title: 'My app' } } }),
24
+ * ]
14
25
  * ```
15
26
  */
16
27
  declare const wrEn: WrI18nCatalog;
@@ -403,6 +403,15 @@ declare function provideWrI18n(options?: ProvideWrI18nOptions): EnvironmentProvi
403
403
  /**
404
404
  * Provide a static loader — catalogs already in memory.
405
405
  *
406
+ * A loader serves only the catalog you pass it, and ngwr registers none of its
407
+ * own. Pass only your own keys here and register ngwr's catalogs with
408
+ * {@link provideWrI18nBaseCatalogs} — not `{ ...wrRu, ...yours }`. A spread is
409
+ * shallow, so any namespace you share with ngwr, e.g. `common`, `validation`,
410
+ * `table`, keeps only the keys of the side spread last, and the other side's
411
+ * keys stop resolving with nothing logged: ngwr's components fall back to their
412
+ * English defaults, and a `wrT` read of a lost key renders the raw key under the
413
+ * default missing handler.
414
+ *
406
415
  * @param catalogs Root catalogs keyed by locale.
407
416
  * @param scopes Optional per-scope catalogs served to `registerScope(name)`.
408
417
  */
@@ -428,6 +437,10 @@ declare function provideWrI18nStaticLoader(catalogs: WrI18nStaticCatalogs, scope
428
437
  *
429
438
  * Pass only the locales you ship — the ones you leave out stay out of the
430
439
  * bundle. Your own keys always win; this is a floor, not an override.
440
+ *
441
+ * Use it instead of spreading `wrRu` into your own catalog, too: the lookup
442
+ * walks these key by key after the loader's catalog misses, so a namespace both
443
+ * define keeps both halves, where a shallow spread keeps only one.
431
444
  */
432
445
  declare function provideWrI18nBaseCatalogs(catalogs: WrI18nBaseCatalogs): Provider;
433
446
  /** Provide an HTTP loader — catalogs fetched from URLs. Requires `provideHttpClient`. */
@@ -72,9 +72,10 @@ interface WrCropWindowBox {
72
72
  * arrives on the `(cropped)` output, which emits a `Blob` after each drag end or
73
73
  * each run of arrow keys, and a consumer who wants a one-off read calls `toBlob()` /
74
74
  * `toDataUrl()` — or `cropRect()` for the geometry — on the component through a
75
- * `viewChild`. An output and three instance members: component API rather than DOM,
76
- * so all of it is outside what a harness can reach at all, which is worth knowing
77
- * rather than discovering.
75
+ * `viewChild`. The same goes for `(loadError)` and `status()`, which is why
76
+ * {@link isFailed} reads the failure off the host class instead. Outputs and instance
77
+ * members are component API rather than DOM, so all of it is outside what a harness
78
+ * can reach at all, which is worth knowing rather than discovering.
78
79
  *
79
80
  * @example
80
81
  * ```ts
@@ -127,6 +128,27 @@ declare class WrImageCropperHarness extends ComponentHarness {
127
128
  * a zero-sized image.
128
129
  */
129
130
  dispatchImageLoad(): Promise<void>;
131
+ /**
132
+ * Fire the image's `error` event — what a browser does with a source it cannot
133
+ * decode, and the only way to get one in a unit test, where nothing is decoded.
134
+ *
135
+ * Afterwards {@link isFailed} is `true` and the component has emitted
136
+ * `(loadError)`; a spec that wants the payload binds that output on its host.
137
+ */
138
+ dispatchImageError(): Promise<void>;
139
+ /**
140
+ * Whether the browser failed to decode the current source.
141
+ *
142
+ * Read off the `wr-image-cropper--failed` host modifier — the part of the failure
143
+ * the DOM shows. It is a third answer beside {@link isEmpty} and {@link isReady},
144
+ * not a combination of them: a failed cropper has a `src` (so it is not empty) and
145
+ * no crop window (so it is not ready), which is also exactly what a cropper still
146
+ * LOADING looks like. Asking those two would call a broken image a slow one.
147
+ *
148
+ * The component renders no message for it, so there is no text to read here; the
149
+ * host's own message is the host's to assert.
150
+ */
151
+ isFailed(): Promise<boolean>;
130
152
  /**
131
153
  * Where the crop window sits over the displayed image, in CSS pixels.
132
154
  *
@@ -18,6 +18,45 @@ interface WrCropRect {
18
18
  type WrCropHandle = 'move' | 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw';
19
19
  /** Supported output MIME types for {@link WrImageCropper.toBlob}. */
20
20
  type WrImageOutputType = 'image/png' | 'image/jpeg' | 'image/webp';
21
+ /**
22
+ * Where the cropper's source stands.
23
+ *
24
+ * - `empty` — no `src`, so the placeholder is showing.
25
+ * - `loading` — a `src` has been handed to the `<img>` and the browser has not
26
+ * answered yet.
27
+ * - `loaded` — the image decoded. The crop window opens once it also has a
28
+ * measured size, which in a browser is the same moment.
29
+ * - `failed` — the browser could not decode it (a renamed `.heic`, a truncated
30
+ * file, a URL that 404s). `(loadError)` fired with the details.
31
+ */
32
+ type WrImageCropperStatus = 'empty' | 'loading' | 'loaded' | 'failed';
33
+ /**
34
+ * The payload of `(loadError)` — plain strings and numbers, so it can be logged,
35
+ * sent to an error tracker or put into a store as it is.
36
+ *
37
+ * The browser's `error` event carries no reason at all, so none is invented here:
38
+ * what the source DECLARED is the most a host has to work with, and for the common
39
+ * case (a phone photo in a format this browser cannot decode) `type` and `name`
40
+ * are what tell the two apart from a broken upload.
41
+ */
42
+ interface WrImageLoadError {
43
+ /**
44
+ * The URL the `<img>` was given — the `src` string itself, or the object URL
45
+ * made for a `File` / `Blob`. An object URL is revoked as soon as `src` changes,
46
+ * so it identifies the attempt rather than being something to fetch later.
47
+ */
48
+ readonly url: string;
49
+ /** `File.name` for a `File` source; `null` for a `Blob` or a string. */
50
+ readonly name: string | null;
51
+ /**
52
+ * The MIME type a `File` / `Blob` declares — `''` when it declares none, and
53
+ * `null` for a string source. A `File`'s type comes from its extension, so a
54
+ * renamed file declares the type of the name it was given.
55
+ */
56
+ readonly type: string | null;
57
+ /** Size in bytes for a `File` / `Blob`; `null` for a string source. */
58
+ readonly size: number | null;
59
+ }
21
60
 
22
61
  interface RectPx {
23
62
  x: number;
@@ -37,12 +76,18 @@ interface RectPx {
37
76
  * freshly-rendered `Blob` of the cropped region. For one-off reads use
38
77
  * `toBlob()` / `toDataUrl()`.
39
78
  *
79
+ * A source the browser cannot decode fires `(loadError)` once, sets `status()` to
80
+ * `'failed'` and puts `wr-image-cropper--failed` on the host. The component renders
81
+ * no message of its own for it — what went wrong and what to do next is the host's
82
+ * to say.
83
+ *
40
84
  * @example
41
85
  * ```html
42
86
  * <wr-image-cropper
43
87
  * [src]="file"
44
88
  * [aspectRatio]="1"
45
89
  * (cropped)="onBlob($event)"
90
+ * (loadError)="onUnreadable($event)"
46
91
  * />
47
92
  * ```
48
93
  *
@@ -61,8 +106,23 @@ declare class WrImageCropper {
61
106
  readonly outputType: _angular_core.InputSignal<WrImageOutputType>;
62
107
  /** JPEG / WebP quality for `(cropped)` in [0, 1]. @default 0.92 */
63
108
  readonly outputQuality: _angular_core.InputSignal<number>;
109
+ /**
110
+ * Longest side of the exported image, in pixels — for `(cropped)`, `toBlob()` and
111
+ * `toDataUrl()` alike. A crop larger than this is scaled down to fit, keeping its
112
+ * aspect ratio; a smaller one is exported as it is, never upscaled. The crop itself
113
+ * — `cropRect()`, in source pixels — is not affected. `null` (or anything that is
114
+ * not a positive number) exports at the source image's own resolution.
115
+ * @default null
116
+ */
117
+ readonly maxOutputSize: _angular_core.InputSignalWithTransform<number | null, unknown>;
64
118
  /** Emits a Blob after each drag end. */
65
119
  readonly cropped: _angular_core.OutputEmitterRef<Blob>;
120
+ /**
121
+ * Fires once when the browser cannot decode the current `src` — a format it does
122
+ * not support, a broken file, a URL that fails. Nothing is rendered for it; show
123
+ * your own message. A new `src` clears the failure.
124
+ */
125
+ readonly loadError: _angular_core.OutputEmitterRef<WrImageLoadError>;
66
126
  protected readonly imgEl: _angular_core.Signal<ElementRef<HTMLImageElement>>;
67
127
  /** Resolved object URL for `src` (so File / Blob render in `<img>`). */
68
128
  protected readonly emptyText: _angular_core.Signal<string>;
@@ -98,6 +158,13 @@ declare class WrImageCropper {
98
158
  private startPointer;
99
159
  private startRect;
100
160
  private readonly destroyRef;
161
+ private readonly statusState;
162
+ /**
163
+ * Where the source stands: `'empty'`, `'loading'`, `'loaded'` or `'failed'`. Reset
164
+ * to `'loading'` (or `'empty'`) whenever `src` changes, so a failure never outlives
165
+ * the source that caused it.
166
+ */
167
+ readonly status: _angular_core.Signal<WrImageCropperStatus>;
101
168
  /** Resolved crop rect in natural (source) pixel coordinates. */
102
169
  readonly cropRect: _angular_core.Signal<WrCropRect>;
103
170
  constructor();
@@ -113,6 +180,18 @@ declare class WrImageCropper {
113
180
  */
114
181
  private resetGeometry;
115
182
  protected onImageLoad(): void;
183
+ /**
184
+ * The `<img>` could not decode its source.
185
+ *
186
+ * Before this handler existed a failed decode left the component exactly as it
187
+ * looks while loading — an image with no size, no crop window, nothing emitted —
188
+ * so a host had no way to tell "still decoding" from "never going to", and a user
189
+ * who picked a `.heic` the browser cannot read saw an empty box and no reason.
190
+ *
191
+ * Once per source: `status` is reset by a new `src` and by nothing else, so a
192
+ * second `error` for the same source says nothing new and is not re-emitted.
193
+ */
194
+ protected onImageError(): void;
116
195
  /**
117
196
  * Keep `display` on the box the image is actually rendered at.
118
197
  *
@@ -176,11 +255,18 @@ declare class WrImageCropper {
176
255
  toDataUrl(type?: WrImageOutputType, quality?: number): string;
177
256
  /** Re-emit the crop without waiting for a drag (e.g. after a programmatic change). */
178
257
  refresh(): Promise<void>;
258
+ /**
259
+ * The exported image's pixel size: the crop in source pixels, scaled down so its
260
+ * longer side fits `maxOutputSize`. `Math.min(1, …)` is the never-upscale rule — a
261
+ * crop already inside the cap keeps its own size, which is also the whole of the
262
+ * uncapped path.
263
+ */
264
+ private outputSize;
179
265
  private toCanvas;
180
266
  private emitCropped;
181
267
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrImageCropper, never>;
182
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrImageCropper, "wr-image-cropper", never, { "src": { "alias": "src"; "required": false; "isSignal": true; }; "aspectRatio": { "alias": "aspectRatio"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "outputType": { "alias": "outputType"; "required": false; "isSignal": true; }; "outputQuality": { "alias": "outputQuality"; "required": false; "isSignal": true; }; }, { "cropped": "cropped"; }, never, never, true, never>;
268
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrImageCropper, "wr-image-cropper", never, { "src": { "alias": "src"; "required": false; "isSignal": true; }; "aspectRatio": { "alias": "aspectRatio"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "outputType": { "alias": "outputType"; "required": false; "isSignal": true; }; "outputQuality": { "alias": "outputQuality"; "required": false; "isSignal": true; }; "maxOutputSize": { "alias": "maxOutputSize"; "required": false; "isSignal": true; }; }, { "cropped": "cropped"; "loadError": "loadError"; }, never, never, true, never>;
183
269
  }
184
270
 
185
271
  export { WrImageCropper };
186
- export type { WrCropHandle, WrCropRect, WrImageOutputType };
272
+ export type { WrCropHandle, WrCropRect, WrImageCropperStatus, WrImageLoadError, WrImageOutputType };
@@ -51,8 +51,25 @@ declare class WrOptionHarness extends ComponentHarness {
51
51
  static hostSelector: string;
52
52
  /** Build a predicate that narrows the query. */
53
53
  static with(options?: WrOptionHarnessFilters): HarnessPredicate<WrOptionHarness>;
54
- /** The option's label, trimmed. */
54
+ /**
55
+ * The option's text as drawn, trimmed — without its leading visual.
56
+ *
57
+ * A `wrOptionLeading` template renders INSIDE the row, so reading the whole
58
+ * host would hand back an avatar's initials as the first word of the name. It
59
+ * is excluded here for the same reason the select excludes it from the label.
60
+ *
61
+ * This is the drawn text, not the `label` input: an option whose `label` is
62
+ * shorter than what it draws still answers with what it draws. What the select
63
+ * REPORTS for it is on the select harness, as `getValueText()` or
64
+ * `getChipLabels()`.
65
+ */
55
66
  getText(): Promise<string>;
67
+ /**
68
+ * Whether the row is drawing a leading visual right now. Reads the slot the
69
+ * select renders the template into, so it answers `false` for an option that
70
+ * declares a template while the panel is closed — which is the contract.
71
+ */
72
+ hasLeading(): Promise<boolean>;
56
73
  /** Whether the option is currently selected. */
57
74
  isSelected(): Promise<boolean>;
58
75
  /** Whether the option refuses selection. */