@ethlete/components 1.0.0-next.28 → 1.0.0-next.29

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-next.29
4
+
5
+ ### Patch Changes
6
+
7
+ - [`90dad92`](https://github.com/ethlete-io/ethdk/commit/90dad922e8985b76e5f5ad67727333de6f5b9431) Thanks [@TomTomB](https://github.com/TomTomB)! - Overlays now elevate one level above the surface their **trigger** actually sits on, resolved from the trigger's nearest surface ancestor in the DOM rather than from dependency injection. The overlay container previously read its parent surface from the injector context (`SURFACE_PROVIDER`), which is wrong across the portal boundary: an overlay's trigger keeps the injector of where it was _declared_, and the anchored panel overlays (select, cascader, date-picker, menu) mount with no DI link to the trigger at all — so they always landed at elevation 1.
8
+
9
+ This fixes two cases:
10
+
11
+ - A `select` (or any anchored panel) opened from **inside a dialog** now mounts at elevation 2 instead of matching the dialog's elevation 1.
12
+ - A picker anchored to a field inside an **elevated card** (e.g. a date input in a card at elevation 1) now elevates above the card instead of staying at elevation 1.
13
+
14
+ Nested content (submenus elevating above their parent menu) and the plain non-nested case (an overlay opened from the base page mounts at elevation 1) are unchanged. Modal dialogs still always mount at elevation 1 — a backdrop resets the visual context.
15
+
3
16
  ## 1.0.0-next.28
4
17
 
5
18
  ### Minor Changes
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { ViewEncapsulation, Component, Directive, input, booleanAttribute, signal, effect, numberAttribute, computed, inject, ElementRef, afterNextRender, DestroyRef, InjectionToken, model, linkedSignal, output, Injector, runInInjectionContext, untracked, ApplicationRef, viewChild, ViewContainerRef, createEnvironmentInjector, DOCUMENT, EnvironmentInjector, inputBinding, createComponent, TemplateRef, afterRenderEffect, contentChild, viewChildren, outputBinding, afterEveryRender, isSignal, assertInInjectionContext, isDevMode, provideEnvironmentInitializer, PLATFORM_ID, contentChildren } from '@angular/core';
3
3
  import * as i1 from '@ethlete/core';
4
- import { injectStyleManager, SurfaceInteractiveDirective, RuntimeError, createCanAnimateSignal, ColorInteractiveDirective, ProvideColorDirective, injectErrorTheme, signalElementDimensions, createProvider, ProvideSurfaceDirective, COLOR_PROVIDER, SURFACE_PROVIDER, injectSurfaceThemes, injectSurfaceContextTracker, injectRenderer, injectBoundaryElement, ANIMATED_LIFECYCLE_TOKEN, resolveAppRootColorProvider, resolveSurfaceByElevation, provideBoundaryElement, AnimatedLifecycleDirective, injectBreakpointObserver, isHTMLElement, equal, animationDebugLog, createRootProvider, injectOverlayRuntime, injectAnimatedBlockSize, randomId, createStaticRootProvider, forceReflow, nextFrame, elementCanScroll, createSwipeTracker, createComponentId, injectObserveBreakpoint, AutoSurfaceDirective, AnimatableDirective, ColorInteractiveContainerDirective, ColorInteractiveExcludeDirective, createFlipAnimation, injectPrefersReducedMotion, createFlipAnimationGroup, ColorInteractiveHasFocusDirective, htmlToMarkdown, markdownToHtml, injectHasTouchInput, injectLocale, clamp, signalHostElementDimensions, DragHandleDirective, ResizeHandlesComponent, copyToClipboard, ScrollObserverDirective, signalElementScrollState, ScrollObserverStartDirective, ScrollObserverEndDirective, applyHostListener, injectQueryParam, createUnsavedChangesTracker, createDocumentElementSignal, fromNextFrame, injectRoute, AnimatedIfDirective, applyInitialFocus, syncSignal, typedBreakpointTransform, signalElementChildren, signalElementIntersection, signalHostAttributes, signalHostClasses, signalHostStyles, getScrollContainerTarget, getScrollItemTarget, getElementScrollCoordinates, provideBreakpointInstance, signalClasses, useCursorDragScroll, signalStyles, getScrollSnapTarget, injectViewportSize, signalHostElementIntersection, setInputSignal, injectHostElement, injectRouterEvent, canUseSessionMemory, createAutoSessionMemoryKey, createSessionMemory, injectFocusVisibleTracker } from '@ethlete/core';
4
+ import { injectStyleManager, SurfaceInteractiveDirective, RuntimeError, createCanAnimateSignal, ColorInteractiveDirective, ProvideColorDirective, injectErrorTheme, signalElementDimensions, createProvider, ProvideSurfaceDirective, COLOR_PROVIDER, SURFACE_PROVIDER, injectSurfaceThemes, injectSurfaceThemesPrefix, injectSurfaceContextTracker, injectRenderer, injectBoundaryElement, ANIMATED_LIFECYCLE_TOKEN, resolveAppRootColorProvider, resolveSurfaceByElevation, createCssSurfaceName, provideBoundaryElement, AnimatedLifecycleDirective, injectBreakpointObserver, isHTMLElement, equal, animationDebugLog, createRootProvider, injectOverlayRuntime, injectAnimatedBlockSize, randomId, createStaticRootProvider, forceReflow, nextFrame, elementCanScroll, createSwipeTracker, createComponentId, injectObserveBreakpoint, AutoSurfaceDirective, AnimatableDirective, ColorInteractiveContainerDirective, ColorInteractiveExcludeDirective, createFlipAnimation, injectPrefersReducedMotion, createFlipAnimationGroup, ColorInteractiveHasFocusDirective, htmlToMarkdown, markdownToHtml, injectHasTouchInput, injectLocale, clamp, signalHostElementDimensions, DragHandleDirective, ResizeHandlesComponent, copyToClipboard, ScrollObserverDirective, signalElementScrollState, ScrollObserverStartDirective, ScrollObserverEndDirective, applyHostListener, injectQueryParam, createUnsavedChangesTracker, createDocumentElementSignal, fromNextFrame, injectRoute, AnimatedIfDirective, applyInitialFocus, syncSignal, typedBreakpointTransform, signalElementChildren, signalElementIntersection, signalHostAttributes, signalHostClasses, signalHostStyles, getScrollContainerTarget, getScrollItemTarget, getElementScrollCoordinates, provideBreakpointInstance, signalClasses, useCursorDragScroll, signalStyles, getScrollSnapTarget, injectViewportSize, signalHostElementIntersection, setInputSignal, injectHostElement, injectRouterEvent, canUseSessionMemory, createAutoSessionMemoryKey, createSessionMemory, injectFocusVisibleTracker } from '@ethlete/core';
5
5
  import { NgTemplateOutlet, DOCUMENT as DOCUMENT$1, NgComponentOutlet, Location, isPlatformBrowser } from '@angular/common';
6
6
  import { DomSanitizer } from '@angular/platform-browser';
7
7
  import { endOfMonth, startOfWeek, startOfMonth, addDays, addYears, addMonths, startOfDay, isSameMonth, getDate, setDate, lastDayOfMonth, isBefore, format, isSameDay, isAfter, parse, isValid, setMilliseconds, setSeconds, setMinutes, setHours } from 'date-fns';
@@ -3110,6 +3110,7 @@ class OverlayContainerComponent {
3110
3110
  this.appRef = inject(ApplicationRef);
3111
3111
  this.overlayRef = inject(OVERLAY_REF);
3112
3112
  this.surfaceThemes = injectSurfaceThemes({ optional: true });
3113
+ this.surfacePrefix = injectSurfaceThemesPrefix({ optional: true });
3113
3114
  this.surfaceContextTracker = injectSurfaceContextTracker();
3114
3115
  this.renderer = injectRenderer();
3115
3116
  this.rootBoundary = injectBoundaryElement();
@@ -3133,9 +3134,18 @@ class OverlayContainerComponent {
3133
3134
  this.ownColorProvider.syncWithProvider(contextColorProvider);
3134
3135
  }
3135
3136
  if (this.surfaceThemes) {
3136
- const parentType = this.parentSurfaceProvider?.surfaceType() ?? 'dark';
3137
+ // Resolve the surface the overlay's trigger visually sits on, so the overlay elevates one
3138
+ // level above it. The trigger keeps its *declaration* injector even when projected/portaled
3139
+ // into another overlay, so DI (`parentSurfaceProvider`) reports the wrong surface for a
3140
+ // trigger rendered inside a dialog/menu pane, and reports nothing for the anchored panel
3141
+ // overlays (select/menu/date) which mount with no DI link to the trigger at all. The
3142
+ // trigger's nearest painted surface ancestor *in the DOM* is authoritative in every case —
3143
+ // an overlay pane and a plain elevated card both carry the surface class — so read that,
3144
+ // and fall back to DI (openers that pass a viewContainerRef but no origin).
3145
+ const parentSurface = this.resolveOriginSurface() ?? this.parentDiSurface();
3146
+ const parentType = parentSurface?.type ?? 'dark';
3137
3147
  const hasBackdrop = this.overlayRef.config.hasBackdrop ?? this.overlayRef.config.mode !== 'non-modal';
3138
- const elevation = hasBackdrop || !this.parentSurfaceProvider ? 1 : this.parentSurfaceProvider.elevation() + 1;
3148
+ const elevation = hasBackdrop || !parentSurface ? 1 : parentSurface.elevation + 1;
3139
3149
  const resolved = resolveSurfaceByElevation(this.surfaceThemes, parentType, elevation);
3140
3150
  if (resolved) {
3141
3151
  this.ownSurfaceProvider.forceSurface(resolved.name);
@@ -3216,6 +3226,49 @@ class OverlayContainerComponent {
3216
3226
  * dialog), otherwise the first painted element in the rendered content (menu/tooltip/date-picker
3217
3227
  * paint a nested element, potentially at a higher elevation than the host's forced surface).
3218
3228
  */
3229
+ parentDiSurface() {
3230
+ const provider = this.parentSurfaceProvider;
3231
+ if (!provider)
3232
+ return null;
3233
+ return { elevation: provider.elevation(), type: provider.surfaceType() ?? 'dark' };
3234
+ }
3235
+ /**
3236
+ * The surface of the nearest ancestor of the overlay's origin (trigger) that paints a resolved
3237
+ * surface — walking the real DOM so it sees *through* the portal/projection boundary that hides
3238
+ * the true parent surface from DI. Returns null when there is no origin element or no surfaced
3239
+ * ancestor (the overlay then falls back to DI, and ultimately to elevation 1).
3240
+ */
3241
+ resolveOriginSurface() {
3242
+ const themes = this.surfaceThemes;
3243
+ if (!themes)
3244
+ return null;
3245
+ const origin = this.resolveOriginElement();
3246
+ if (!origin)
3247
+ return null;
3248
+ const prefix = this.surfacePrefix || 'et';
3249
+ const themeByClass = new Map();
3250
+ for (const theme of themes) {
3251
+ themeByClass.set(`${prefix}-surface--${createCssSurfaceName(theme.name)}`, theme);
3252
+ }
3253
+ for (let el = origin; el; el = el.parentElement) {
3254
+ for (const cls of Array.from(el.classList)) {
3255
+ const theme = themeByClass.get(cls);
3256
+ if (theme)
3257
+ return { elevation: theme.elevation, type: theme.type };
3258
+ }
3259
+ }
3260
+ return null;
3261
+ }
3262
+ resolveOriginElement() {
3263
+ const origin = this.overlayRef.config.origin;
3264
+ if (origin instanceof HTMLElement)
3265
+ return origin;
3266
+ if (origin instanceof Event) {
3267
+ const target = origin.target ?? origin.currentTarget;
3268
+ return target instanceof HTMLElement ? target : null;
3269
+ }
3270
+ return null;
3271
+ }
3219
3272
  isSheetHost(host) {
3220
3273
  return (host.classList.contains('et-overlay--bottom-sheet') ||
3221
3274
  host.classList.contains('et-overlay--top-sheet') ||