@lukfel/ng-scaffold 22.1.3 → 22.1.4

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.
@@ -1,6 +1,6 @@
1
+ import { MediaMatcher, BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
1
2
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeEnvironmentProviders, inject, Injectable, PLATFORM_ID, DOCUMENT, signal, input, output, ChangeDetectionStrategy, Component, effect, DestroyRef, viewChild, computed, model, afterNextRender, linkedSignal, TemplateRef, Directive, contentChild } from '@angular/core';
3
- import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
3
+ import { inject, PLATFORM_ID, REQUEST, Injectable, InjectionToken, makeEnvironmentProviders, DOCUMENT, signal, input, output, ChangeDetectionStrategy, Component, effect, DestroyRef, viewChild, computed, model, afterNextRender, linkedSignal, TemplateRef, Directive, contentChild } from '@angular/core';
4
4
  import { isPlatformBrowser, NgClass, NgTemplateOutlet, KeyValuePipe } from '@angular/common';
5
5
  import { toObservable, toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
6
  import * as i1$3 from '@angular/router';
@@ -45,9 +45,89 @@ import { MatRippleModule } from '@angular/material/core';
45
45
  import * as i8 from '@angular/material/divider';
46
46
  import { MatDividerModule } from '@angular/material/divider';
47
47
 
48
+ /**
49
+ * Viewports assumed during SSR. A request reveals a device class, never an actual viewport, and
50
+ * CDK exposes only query strings, so these are plausible device sizes rather than derived values.
51
+ * They have to keep landing in `Breakpoints.XSmall` and `Breakpoints.Large`, which the spec asserts.
52
+ */
53
+ const SSR_MOBILE_VIEWPORT = { width: 390, height: 844 };
54
+ const SSR_DESKTOP_VIEWPORT = { width: 1280, height: 800 };
55
+ const MOBILE_USER_AGENT_REGEX = /Android|iPhone|iPod|Windows Phone|IEMobile|BlackBerry|Opera Mini/i;
56
+ /**
57
+ * Answers media queries during SSR from the incoming request rather than matching nothing, so
58
+ * breakpoint driven markup is server rendered for the requesting device class instead of always
59
+ * falling back to desktop. On the browser it defers to the platform implementation.
60
+ *
61
+ * Opt in with `provideScaffold({ ssrBreakpoints: true })`. Doing so makes the SSR response depend
62
+ * on the request, so whatever serves it must `Vary` on `Sec-CH-UA-Mobile` and `User-Agent` or a
63
+ * cached mobile render will be handed to a desktop client.
64
+ */
65
+ class ScaffoldMediaMatcher extends MediaMatcher {
66
+ constructor() {
67
+ super(...arguments);
68
+ this.platformId = inject(PLATFORM_ID);
69
+ this.request = inject(REQUEST, { optional: true });
70
+ }
71
+ matchMedia(query) {
72
+ if (isPlatformBrowser(this.platformId))
73
+ return super.matchMedia(query);
74
+ const { width, height } = this.isMobileRequest() ? SSR_MOBILE_VIEWPORT : SSR_DESKTOP_VIEWPORT;
75
+ return {
76
+ matches: matchesMediaQuery(query, width, height),
77
+ media: query,
78
+ onchange: null,
79
+ addListener: () => { },
80
+ removeListener: () => { },
81
+ addEventListener: () => { },
82
+ removeEventListener: () => { },
83
+ dispatchEvent: () => false,
84
+ };
85
+ }
86
+ // Sec-CH-UA-Mobile needs no Accept-CH opt in on Chromium; other browsers only give a User-Agent
87
+ isMobileRequest() {
88
+ const headers = this.request?.headers;
89
+ if (!headers)
90
+ return false;
91
+ const hint = headers.get('sec-ch-ua-mobile');
92
+ if (hint)
93
+ return hint.includes('?1');
94
+ return MOBILE_USER_AGENT_REGEX.test(headers.get('user-agent') ?? '');
95
+ }
96
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: ScaffoldMediaMatcher, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
97
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: ScaffoldMediaMatcher }); }
98
+ }
99
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: ScaffoldMediaMatcher, decorators: [{
100
+ type: Injectable
101
+ }] });
102
+ /**
103
+ * Evaluates the width and orientation parts of a media query against an assumed viewport.
104
+ * Conditions a request cannot answer, such as `prefers-color-scheme`, never match.
105
+ */
106
+ function matchesMediaQuery(query, width, height) {
107
+ // A comma separated query matches when any of its branches matches
108
+ return query.split(',').some((branch) => branch.split(' and ').every((condition) => {
109
+ const min = condition.match(/min-width:\s*([\d.]+)px/);
110
+ if (min)
111
+ return width >= parseFloat(min[1]);
112
+ const max = condition.match(/max-width:\s*([\d.]+)px/);
113
+ if (max)
114
+ return width <= parseFloat(max[1]);
115
+ if (/orientation:\s*portrait/.test(condition))
116
+ return height >= width;
117
+ if (/orientation:\s*landscape/.test(condition))
118
+ return width > height;
119
+ return false;
120
+ }));
121
+ }
122
+
48
123
  const CONFIG = new InjectionToken('config');
49
124
  function provideScaffold(config = {}) {
50
- return makeEnvironmentProviders([{ provide: CONFIG, useValue: config }]);
125
+ const providers = [{ provide: CONFIG, useValue: config }];
126
+ // Opt in, because it makes the SSR response depend on the request and therefore on Vary
127
+ if (config.ssrBreakpoints) {
128
+ providers.push({ provide: MediaMatcher, useClass: ScaffoldMediaMatcher });
129
+ }
130
+ return makeEnvironmentProviders(providers);
51
131
  }
52
132
 
53
133
  class BreakpointService {
@@ -1845,5 +1925,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1845
1925
  * Generated bundle index. Do not edit.
1846
1926
  */
1847
1927
 
1848
- export { BreakpointService, CONFIG, ColorPickerComponent, DialogService, FileUploadComponent, ListComponent, ListItemAvatarDirective, ListItemButtonsDirective, ListItemSubtitleDirective, ListItemTitleDirective, LocalStorageService, Logger, NotificationComponent, OverlayService, PlaceholderComponent, RouterService, ScaffoldComponent, ScaffoldLoadingInterceptor, ScaffoldService, SeoService, SnackbarService, ThemeService, TranslationService, provideScaffold };
1928
+ export { BreakpointService, CONFIG, ColorPickerComponent, DialogService, FileUploadComponent, ListComponent, ListItemAvatarDirective, ListItemButtonsDirective, ListItemSubtitleDirective, ListItemTitleDirective, LocalStorageService, Logger, NotificationComponent, OverlayService, PlaceholderComponent, RouterService, ScaffoldComponent, ScaffoldLoadingInterceptor, ScaffoldMediaMatcher, ScaffoldService, SeoService, SnackbarService, ThemeService, TranslationService, matchesMediaQuery, provideScaffold };
1849
1929
  //# sourceMappingURL=lukfel-ng-scaffold.mjs.map