@nanahoshi/mona-ui 0.18.0 → 0.18.2

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.
@@ -132,6 +132,7 @@ class SegmentedComponent {
132
132
  #lastAnimatedIndex;
133
133
  #lastOptions;
134
134
  #lastSize;
135
+ #observedOptionElements;
135
136
  #resizeObserver;
136
137
  constructor() {
137
138
  this.#destroyRef = inject(DestroyRef);
@@ -139,6 +140,7 @@ class SegmentedComponent {
139
140
  this.#lastAnimatedIndex = null;
140
141
  this.#lastOptions = null;
141
142
  this.#lastSize = null;
143
+ this.#observedOptionElements = [];
142
144
  this.#resizeObserver = null;
143
145
  this.containerClasses = computed(() => {
144
146
  const classes = segmentedContainerThemeVariants({ alignment: this.alignment(), rounded: this.rounded() });
@@ -265,7 +267,7 @@ class SegmentedComponent {
265
267
  afterRenderEffect({
266
268
  read: () => {
267
269
  this.selectedIndex();
268
- this.optionElements();
270
+ const optionElements = this.optionElements();
269
271
  this.alignment();
270
272
  this.rounded();
271
273
  this.animate();
@@ -276,6 +278,7 @@ class SegmentedComponent {
276
278
  const suppressTransition = isSizeChange || isOptionsChange;
277
279
  this.#lastSize = size;
278
280
  this.#lastOptions = options;
281
+ this.observeOptionElements(optionElements);
279
282
  this.updateIndicatorGeometry({ suppressTransition });
280
283
  }
281
284
  });
@@ -307,6 +310,23 @@ class SegmentedComponent {
307
310
  this.value.set(option.value);
308
311
  this.touch.emit();
309
312
  }
313
+ observeOptionElements(optionElements) {
314
+ if (!this.#resizeObserver) {
315
+ return;
316
+ }
317
+ const elements = optionElements.map(optionElement => optionElement.nativeElement);
318
+ for (const element of this.#observedOptionElements) {
319
+ if (!elements.includes(element)) {
320
+ this.#resizeObserver.unobserve(element);
321
+ }
322
+ }
323
+ for (const element of elements) {
324
+ if (!this.#observedOptionElements.includes(element)) {
325
+ this.#resizeObserver.observe(element);
326
+ }
327
+ }
328
+ this.#observedOptionElements = elements;
329
+ }
310
330
  setupResizeObserver() {
311
331
  if (typeof ResizeObserver === "undefined") {
312
332
  return;
@@ -315,6 +335,7 @@ class SegmentedComponent {
315
335
  this.updateIndicatorGeometry({ suppressTransition: true });
316
336
  });
317
337
  this.#resizeObserver.observe(this.#hostElementRef.nativeElement);
338
+ this.observeOptionElements(this.optionElements());
318
339
  }
319
340
  updateIndicatorGeometry(options) {
320
341
  const selectedIndex = this.selectedIndex();
@@ -332,14 +353,18 @@ class SegmentedComponent {
332
353
  this.indicatorTransitionEnabled.set(false);
333
354
  return;
334
355
  }
335
- const host = this.#hostElementRef.nativeElement;
336
356
  const option = selectedElementRef.nativeElement;
337
- const hostRect = host.getBoundingClientRect();
338
- const optionRect = option.getBoundingClientRect();
339
- const x = optionRect.left - hostRect.left - (host.clientLeft || 0);
340
- const y = optionRect.top - hostRect.top - (host.clientTop || 0);
341
- const width = optionRect.width;
342
- const height = optionRect.height;
357
+ // `offsetLeft`/`offsetTop`/`offsetWidth`/`offsetHeight` are pure layout values, relative to
358
+ // the nearest positioned ancestor (the host, which is `position: relative` and the option's
359
+ // direct parent). Unlike `getBoundingClientRect()`, they are unaffected by any CSS transform
360
+ // an ancestor applies — e.g. the scale animation `mona-popup` plays while opening. Measuring
361
+ // with `getBoundingClientRect()` there could capture a mid-transition (e.g. 95%-scaled)
362
+ // snapshot that never gets corrected once the animation settles, undersizing/misplacing the
363
+ // indicator relative to the option's real, final box.
364
+ const x = option.offsetLeft;
365
+ const y = option.offsetTop;
366
+ const width = option.offsetWidth;
367
+ const height = option.offsetHeight;
343
368
  const shouldAnimate = !options?.suppressTransition &&
344
369
  this.animate() &&
345
370
  this.indicatorInitialized() &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanahoshi/mona-ui",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "A very light Angular UI framework",
5
5
  "author": "Nanahoshi",
6
6
  "repository": {
@@ -148,6 +148,7 @@ declare class SegmentedComponent<T extends SegmentedValue = SegmentedValue> impl
148
148
  protected createOptionContext(option: SegmentedOption<T>, index: number): SegmentedItemTemplateContext<T>;
149
149
  protected onOptionBlur(): void;
150
150
  protected onOptionChange(option: SegmentedOption<T>): void;
151
+ private observeOptionElements;
151
152
  private setupResizeObserver;
152
153
  private updateIndicatorGeometry;
153
154
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SegmentedComponent<any>, never>;