@ssv/ngx.ux 3.0.0-dev.36 → 3.0.0-dev.40

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 (71) hide show
  1. package/esm2022/index.mjs +4 -0
  2. package/esm2022/internal/internal.model.mjs +2 -0
  3. package/esm2022/platform/window.mjs +31 -0
  4. package/esm2022/ssv-ngx.ux.mjs +5 -0
  5. package/esm2022/ux.module.mjs +19 -0
  6. package/esm2022/version.mjs +2 -0
  7. package/esm2022/viewport/index.mjs +10 -0
  8. package/esm2022/viewport/viewport-data/index.mjs +4 -0
  9. package/esm2022/viewport/viewport-data/viewport-data-matcher.mjs +107 -0
  10. package/esm2022/viewport/viewport-data/viewport-data.pipe.mjs +48 -0
  11. package/esm2022/viewport/viewport-data/viewport-data.service.mjs +32 -0
  12. package/esm2022/viewport/viewport-data/viewport-data.utils.mjs +100 -0
  13. package/esm2022/viewport/viewport-matcher-var.directive.mjs +69 -0
  14. package/esm2022/viewport/viewport-matcher.directive.mjs +136 -0
  15. package/esm2022/viewport/viewport-server-size.service.mjs +37 -0
  16. package/esm2022/viewport/viewport.model.mjs +30 -0
  17. package/esm2022/viewport/viewport.module.mjs +27 -0
  18. package/esm2022/viewport/viewport.options.mjs +51 -0
  19. package/esm2022/viewport/viewport.service.mjs +82 -0
  20. package/esm2022/viewport/viewport.util.mjs +117 -0
  21. package/fesm2022/ssv-ngx.ux.mjs +846 -0
  22. package/fesm2022/ssv-ngx.ux.mjs.map +1 -0
  23. package/{src/index.ts → index.d.ts} +0 -1
  24. package/{src/internal/internal.model.ts → internal/internal.model.d.ts} +1 -1
  25. package/package.json +18 -3
  26. package/platform/window.d.ts +13 -0
  27. package/ux.module.d.ts +7 -0
  28. package/version.d.ts +1 -0
  29. package/{src/viewport/index.ts → viewport/index.d.ts} +1 -12
  30. package/viewport/viewport-data/viewport-data-matcher.d.ts +32 -0
  31. package/viewport/viewport-data/viewport-data.pipe.d.ts +18 -0
  32. package/viewport/viewport-data/viewport-data.service.d.ts +17 -0
  33. package/viewport/viewport-data/viewport-data.utils.d.ts +21 -0
  34. package/viewport/viewport-matcher-var.directive.d.ts +25 -0
  35. package/viewport/viewport-matcher.directive.d.ts +33 -0
  36. package/viewport/viewport-server-size.service.d.ts +10 -0
  37. package/viewport/viewport.model.d.ts +47 -0
  38. package/viewport/viewport.module.d.ts +9 -0
  39. package/viewport/viewport.options.d.ts +19 -0
  40. package/viewport/viewport.service.d.ts +36 -0
  41. package/viewport/viewport.util.d.ts +25 -0
  42. package/eslint.config.js +0 -43
  43. package/index.ts +0 -1
  44. package/jest.config.ts +0 -21
  45. package/ng-package.json +0 -7
  46. package/project.json +0 -36
  47. package/src/platform/window.ts +0 -31
  48. package/src/test-setup.ts +0 -8
  49. package/src/ux.module.ts +0 -15
  50. package/src/version.ts +0 -1
  51. package/src/viewport/viewport-data/README.md +0 -47
  52. package/src/viewport/viewport-data/viewport-data-matcher.spec.ts +0 -227
  53. package/src/viewport/viewport-data/viewport-data-matcher.ts +0 -175
  54. package/src/viewport/viewport-data/viewport-data.pipe.ts +0 -51
  55. package/src/viewport/viewport-data/viewport-data.service.ts +0 -48
  56. package/src/viewport/viewport-data/viewport-data.utils.spec.ts +0 -228
  57. package/src/viewport/viewport-data/viewport-data.utils.ts +0 -137
  58. package/src/viewport/viewport-matcher-var.directive.ts +0 -85
  59. package/src/viewport/viewport-matcher.directive.ts +0 -170
  60. package/src/viewport/viewport-server-size.service.ts +0 -37
  61. package/src/viewport/viewport.model.ts +0 -54
  62. package/src/viewport/viewport.module.ts +0 -19
  63. package/src/viewport/viewport.options.ts +0 -74
  64. package/src/viewport/viewport.service.ts +0 -123
  65. package/src/viewport/viewport.util.spec.ts +0 -254
  66. package/src/viewport/viewport.util.ts +0 -152
  67. package/tsconfig.json +0 -28
  68. package/tsconfig.lib.json +0 -12
  69. package/tsconfig.lib.prod.json +0 -9
  70. package/tsconfig.spec.json +0 -11
  71. /package/{src/viewport/viewport-data/index.ts → viewport/viewport-data/index.d.ts} +0 -0
@@ -1,228 +0,0 @@
1
- import { generateViewportSizeTypeInfoList, generateViewportSizeTypeInfoRefs, } from "../viewport.util";
2
- import { ViewportSizeTypeInfo } from "../viewport.model";
3
- import {
4
- generateViewportRulesRangeFromDataMatcher as generateViewportRulesRangeFromDataMatcher_,
5
- } from "./viewport-data.utils";
6
- import { TestViewportSizeType } from "../viewport.util.spec";
7
- import { ViewportDataConfig, ViewportDataMatchStrategy } from "./viewport-data-matcher";
8
-
9
- type TestViewportDataConfig<T> = ViewportDataConfig<T, Partial<Record<keyof typeof TestViewportSizeType, T>>>;
10
-
11
- const breakpoints: Record<keyof typeof TestViewportSizeType, number> = {
12
- xsmall: 450,
13
- small: 767,
14
- medium: 992,
15
- large: 1200,
16
- hd: 1280,
17
- fullHd: 1920,
18
- };
19
-
20
- const sizeTypes = generateViewportSizeTypeInfoList(breakpoints);
21
- const sizeRefs = generateViewportSizeTypeInfoRefs(sizeTypes) as Record<keyof typeof TestViewportSizeType, ViewportSizeTypeInfo>;
22
-
23
- const generateViewportRulesRangeFromDataMatcher = <T>(
24
- dataConfig: ViewportDataConfig<T>,
25
- strategy: ViewportDataMatchStrategy,
26
- ) => generateViewportRulesRangeFromDataMatcher_<T>(dataConfig, strategy, sizeTypes, sizeRefs);
27
-
28
- describe("viewportDataUtils", () => {
29
-
30
- describe("generateViewportRulesRangeFromDataMatcher", () => {
31
-
32
- describe("given strategy is exact", () => {
33
- const strategy = ViewportDataMatchStrategy.exact;
34
- const dataConfig: TestViewportDataConfig<number> = {
35
- default: 12,
36
- xsmall: undefined,
37
- small: 10,
38
- medium: undefined,
39
- large: 20,
40
- hd: 25,
41
- fullHd: undefined,
42
- };
43
- /**
44
- * default: 12
45
- * [451-767]=10
46
- * [993-1200]=20
47
- * [1201-1280]=25
48
- * */
49
-
50
- it("should return matching rules", () => {
51
- const result = generateViewportRulesRangeFromDataMatcher(dataConfig, strategy);
52
- expect(result).toEqual([
53
- { min: undefined, max: undefined, value: dataConfig.default },
54
- { min: breakpoints.xsmall + 1, max: breakpoints.small, value: dataConfig.small },
55
- { min: breakpoints.medium + 1, max: breakpoints.large, value: dataConfig.large },
56
- { min: breakpoints.large + 1, max: breakpoints.hd, value: dataConfig.hd },
57
- ]);
58
- });
59
-
60
- });
61
-
62
- describe("given strategy is smaller", () => {
63
- const strategy = ViewportDataMatchStrategy.smaller;
64
- const dataConfig: TestViewportDataConfig<number> = {
65
- default: 12,
66
- medium: 15,
67
- large: 20,
68
- hd: 30,
69
- };
70
- /**
71
- * default: 12
72
- * [768-992]=15
73
- * [993-1200]=20
74
- * [1201-*]=30
75
- * */
76
-
77
- it("should return matching rules", () => {
78
- const result = generateViewportRulesRangeFromDataMatcher(dataConfig, strategy);
79
- expect(result).toEqual([
80
- { min: undefined, max: undefined, value: dataConfig.default }, // default
81
- { min: breakpoints.small + 1, max: breakpoints.medium, value: dataConfig.medium }, // medium
82
- { min: breakpoints.medium + 1, max: breakpoints.large, value: dataConfig.large }, // large
83
- { min: breakpoints.large + 1, max: undefined, value: dataConfig.hd }, // hd
84
- ]);
85
- });
86
- });
87
-
88
- describe("given strategy is larger", () => {
89
- const strategy = ViewportDataMatchStrategy.larger;
90
- const dataConfig: TestViewportDataConfig<number> = {
91
- default: 12,
92
- medium: 15,
93
- large: 20,
94
- hd: 30,
95
- };
96
- /**
97
- * default: 12
98
- * [*-992]=15
99
- * [993-1200]=20
100
- * [1201-1280]=30
101
- * */
102
- it("should return matching rules", () => {
103
- const result = generateViewportRulesRangeFromDataMatcher(dataConfig, strategy);
104
- expect(result).toEqual([
105
- { min: undefined, max: undefined, value: dataConfig.default }, // default
106
- { min: undefined, max: breakpoints.medium, value: dataConfig.medium }, // medium
107
- { min: breakpoints.medium + 1, max: breakpoints.large, value: dataConfig.large }, // large
108
- { min: breakpoints.large + 1, max: breakpoints.hd, value: dataConfig.hd }, // hd
109
- ]);
110
- });
111
- });
112
-
113
- describe("given strategy is closestSmallFirst", () => {
114
- const strategy = ViewportDataMatchStrategy.closestSmallerFirst;
115
-
116
- // todo: name
117
- describe("when dataset A", () => {
118
- const dataConfig: TestViewportDataConfig<number> = {
119
- default: 12,
120
- xsmall: 5,
121
- small: undefined,
122
- medium: 15,
123
- large: undefined,
124
- hd: undefined,
125
- fullHd: 50,
126
- };
127
-
128
- it("should return matching rules", () => {
129
- const result = generateViewportRulesRangeFromDataMatcher(dataConfig, strategy);
130
- expect(result).toEqual([
131
- { min: undefined, max: undefined, value: dataConfig.default },
132
- { min: undefined, max: breakpoints.small, value: dataConfig.xsmall },
133
- { min: breakpoints.small + 1, max: breakpoints.large, value: dataConfig.medium },
134
- { min: breakpoints.large + 1, max: undefined, value: dataConfig.fullHd },
135
- ]);
136
- });
137
- });
138
-
139
- // todo: name
140
- describe("when dataset B", () => {
141
- const dataConfig: TestViewportDataConfig<number> = {
142
- default: 12,
143
- xsmall: undefined,
144
- small: 10,
145
- medium: undefined,
146
- large: 20,
147
- hd: 25,
148
- fullHd: undefined,
149
- };
150
-
151
- it("should return matching rules", () => {
152
- const result = generateViewportRulesRangeFromDataMatcher(dataConfig, strategy);
153
- expect(result).toEqual([
154
- { min: undefined, max: undefined, value: dataConfig.default },
155
- { min: undefined, max: breakpoints.medium, value: dataConfig.small },
156
- { min: breakpoints.medium + 1, max: breakpoints.large, value: dataConfig.large },
157
- { min: breakpoints.large + 1, max: undefined, value: dataConfig.hd },
158
- ]);
159
- });
160
- });
161
-
162
- });
163
-
164
- describe("given strategy is closestLargerFirst", () => {
165
- const strategy = ViewportDataMatchStrategy.closestLargerFirst;
166
-
167
- // todo: name
168
- describe("when dataset A", () => {
169
- const dataConfig: TestViewportDataConfig<number> = {
170
- default: 12,
171
- xsmall: 5,
172
- small: undefined,
173
- medium: 15,
174
- large: undefined,
175
- hd: undefined,
176
- fullHd: 50,
177
- };
178
- /**
179
- * [*-450]=5
180
- * [451-1200]=10
181
- * [1201-*]=50
182
- * */
183
-
184
- it("should return matching rules", () => {
185
- const result = generateViewportRulesRangeFromDataMatcher(dataConfig, strategy);
186
- expect(result).toEqual([
187
- { min: undefined, max: undefined, value: dataConfig.default },
188
- { min: undefined, max: breakpoints.xsmall, value: dataConfig.xsmall },
189
- { min: breakpoints.xsmall + 1, max: breakpoints.large, value: dataConfig.medium },
190
- { min: breakpoints.large + 1, max: undefined, value: dataConfig.fullHd },
191
- ]);
192
- });
193
- });
194
-
195
- // todo: name
196
- describe("when dataset B", () => {
197
- const dataConfig: TestViewportDataConfig<number> = {
198
- default: 12,
199
- xsmall: undefined,
200
- small: 10,
201
- medium: undefined,
202
- large: 20,
203
- hd: 25,
204
- fullHd: undefined,
205
- };
206
- /**
207
- * [*-767]=10
208
- * [768-1200]=20
209
- * [1201-*]=25
210
- * */
211
- it("should return matching rules", () => {
212
- const result = generateViewportRulesRangeFromDataMatcher(dataConfig, strategy);
213
- expect(result).toEqual([
214
- { min: undefined, max: undefined, value: dataConfig.default },
215
- { min: undefined, max: breakpoints.small, value: dataConfig.small },
216
- { min: breakpoints.small + 1, max: breakpoints.large, value: dataConfig.large },
217
- { min: breakpoints.large + 1, max: undefined, value: dataConfig.hd },
218
- ]);
219
- });
220
- });
221
-
222
- });
223
-
224
-
225
- });
226
-
227
-
228
- });
@@ -1,137 +0,0 @@
1
- import { Dictionary } from "../../internal/internal.model";
2
- import { ViewportSizeTypeInfo } from "../viewport.model";
3
- import { ViewportDataConfig, ViewportDataMatchStrategy } from "./viewport-data-matcher";
4
-
5
- export interface ViewportDataRule<T> {
6
- min?: number;
7
- max?: number;
8
- value: T;
9
- }
10
-
11
- /**
12
- * Utility function to generate rules based on strategies.
13
- *
14
- * @param dataConfig Data config to generate rules based on.
15
- * @param strategy Strategy to use when building rules.
16
- * @param sizeTypes Available size types ordered by index type. (Can be obtained from `ViewportService`)
17
- * @param sizeTypeMap Available size type map. (Can be obtained from `ViewportService`)
18
- * @returns Returns a collection of rules (ordered).
19
- */
20
- export function generateViewportRulesRangeFromDataMatcher<T>(
21
- dataConfig: ViewportDataConfig<T>,
22
- strategy: ViewportDataMatchStrategy,
23
- sizeTypes: ViewportSizeTypeInfo[],
24
- sizeTypeMap: Dictionary<ViewportSizeTypeInfo>,
25
- ): ViewportDataRule<T>[] {
26
- const ruleBuilderFn = matchStrategyHandlerMap[strategy];
27
- if (!ruleBuilderFn) {
28
- throw Error(`generateViewportRulesRangeFromDataMatcher: Viewport Data strategy not implemented. Strategy: '${strategy}'`);
29
- }
30
-
31
- let dataSizes: ViewportSizeTypeInfo[] = [];
32
- for (const key in dataConfig) {
33
- if (Object.prototype.hasOwnProperty.call(dataConfig, key)) {
34
- const data = dataConfig[key];
35
- if (data === undefined) {
36
- continue;
37
- }
38
- const size = sizeTypeMap[key];
39
- if (size) {
40
- dataSizes.push(size);
41
- }
42
- }
43
- }
44
- dataSizes = dataSizes.sort(({ type: typeA }, { type: typeB }) => typeA - typeB);
45
-
46
- const rules: ViewportDataRule<T>[] = [];
47
- if (dataConfig.default) {
48
- rules.push({ value: dataConfig.default, min: undefined, max: undefined });
49
- }
50
-
51
- let prevRule: ViewportDataRule<T> | undefined;
52
- for (let index = 0; index < dataSizes.length; index++) {
53
- const prevDataSize = dataSizes[index - 1];
54
- const nextDataSize = dataSizes[index + 1];
55
- const dataSize = dataSizes[index];
56
- const prevSize = sizeTypes[dataSize.type - 1];
57
- // const nextSize = sizeTypes[dataSize.type + 1];
58
- const data = dataConfig[dataSize.name];
59
- const rule: ViewportDataRule<T> = {
60
- value: data,
61
- min: undefined,
62
- max: undefined,
63
- };
64
-
65
- ruleBuilderFn(rule, dataSize, nextDataSize, prevDataSize, prevSize, prevRule, sizeTypes);
66
-
67
- prevRule = rule;
68
- rules.push(rule);
69
- }
70
- return rules;
71
- }
72
-
73
- export interface ViewportRuleRangeBuilder<T = unknown> {
74
- (
75
- rule: ViewportDataRule<T>,
76
- dataSize: ViewportSizeTypeInfo,
77
- nextDataSize: ViewportSizeTypeInfo | undefined,
78
- prevDataSize: ViewportSizeTypeInfo | undefined,
79
- prevSize: ViewportSizeTypeInfo | undefined,
80
- prevRule: ViewportDataRule<T> | undefined,
81
- sizeTypes: ViewportSizeTypeInfo[],
82
- ): void;
83
- }
84
-
85
- const matchStrategyHandlerMap: Dictionary<ViewportRuleRangeBuilder> = {
86
- [ViewportDataMatchStrategy.exact]: (rule, dataSize, _nextDataSize, _prevDataSize, prevSize) => {
87
- rule.max = dataSize.widthThreshold;
88
- if (prevSize) {
89
- rule.min = prevSize.widthThreshold + 1;
90
- }
91
- },
92
- [ViewportDataMatchStrategy.smaller]: (rule, dataSize, nextDataSize, _prevDataSize, prevSize) => {
93
- if (nextDataSize) {
94
- rule.max = dataSize.widthThreshold;
95
- }
96
- if (prevSize) {
97
- rule.min = prevSize.widthThreshold + 1;
98
- }
99
- },
100
- [ViewportDataMatchStrategy.larger]: (rule, dataSize, _nextDataSize, prevDataSize) => {
101
- if (dataSize) {
102
- rule.max = dataSize.widthThreshold;
103
- }
104
- if (prevDataSize) {
105
- rule.min = prevDataSize.widthThreshold + 1;
106
- }
107
- },
108
- [ViewportDataMatchStrategy.closestSmallerFirst]: (rule, dataSize, nextDataSize, _prevDataSize, _prevSize, prevRule, sizeTypes) => {
109
- if (nextDataSize) {
110
- rule.max = calculateClosestWidthThreshold(nextDataSize, dataSize, sizeTypes, true);
111
- }
112
- if (prevRule?.max) {
113
- rule.min = prevRule.max + 1;
114
- }
115
- },
116
- [ViewportDataMatchStrategy.closestLargerFirst]: (rule, dataSize, nextDataSize, _prevDataSize, _prevSize, prevRule, sizeTypes) => {
117
- if (nextDataSize) {
118
- rule.max = calculateClosestWidthThreshold(nextDataSize, dataSize, sizeTypes, false);
119
- }
120
- if (prevRule?.max) {
121
- rule.min = prevRule.max + 1;
122
- }
123
- },
124
- };
125
-
126
- function calculateClosestWidthThreshold(
127
- nextDataSize: ViewportSizeTypeInfo,
128
- dataSize: ViewportSizeTypeInfo,
129
- sizeTypes: ViewportSizeTypeInfo[],
130
- isSmallerPreferred: boolean,
131
- ) {
132
- const fn = isSmallerPreferred ? Math.ceil : Math.floor;
133
- // get closest between curr and next
134
- const diffIndex = fn((nextDataSize.type - dataSize.type - 1) / 2);
135
- const diffNextSize = sizeTypes[dataSize.type + diffIndex];
136
- return (diffNextSize || dataSize).widthThreshold;
137
- }
@@ -1,85 +0,0 @@
1
- import {
2
- OnInit,
3
- OnDestroy,
4
- Directive,
5
- Input,
6
- TemplateRef,
7
- ViewContainerRef,
8
- EmbeddedViewRef,
9
- } from "@angular/core";
10
- import { combineLatest, ReplaySubject, Subject, tap, map, takeUntil } from "rxjs";
11
-
12
- import { ViewportService } from "./viewport.service";
13
- import {
14
- isViewportSizeMatcherExpression,
15
- isViewportSizeMatcherTupleExpression,
16
- isViewportConditionMatch
17
- } from "./viewport.util";
18
- import { ViewportMatchConditions, ViewportSizeMatcherExpression } from "./viewport.model";
19
-
20
- const NAME_CAMEL = "ssvViewportMatcherVar";
21
-
22
- export class SsvViewportMatcherVarContext {
23
-
24
- constructor(
25
- public $implicit = false,
26
- ) { }
27
-
28
- }
29
-
30
- @Directive({
31
- selector: `[${NAME_CAMEL}]`,
32
- standalone: true,
33
- })
34
- export class SsvViewportMatcherVarDirective implements OnInit, OnDestroy {
35
-
36
- private _matchConditions: ViewportMatchConditions = {};
37
- private _context = new SsvViewportMatcherVarContext();
38
- private readonly _destroy$ = new Subject<void>();
39
- private readonly _update$ = new ReplaySubject<void>(1);
40
- private _viewRef!: EmbeddedViewRef<SsvViewportMatcherVarContext>;
41
-
42
- @Input(`${NAME_CAMEL}When`) set condition(value: string | string[] | ViewportSizeMatcherExpression) {
43
- if (isViewportSizeMatcherExpression(value)) {
44
- this._matchConditions.expression = value;
45
- } else if (isViewportSizeMatcherTupleExpression(value)) {
46
- const [op, size] = value;
47
- this._matchConditions.expression = {
48
- operation: op,
49
- size
50
- };
51
- } else {
52
- this._matchConditions.sizeType = value;
53
- }
54
-
55
- this._update$.next();
56
- }
57
-
58
- constructor(
59
- private viewport: ViewportService,
60
- private viewContainer: ViewContainerRef,
61
- private templateRef: TemplateRef<SsvViewportMatcherVarContext>,
62
- ) {
63
- }
64
-
65
- ngOnInit(): void {
66
- this.updateView();
67
- combineLatest([this.viewport.sizeType$, this._update$]).pipe(
68
- map(([sizeType]) => isViewportConditionMatch(sizeType, this._matchConditions, this.viewport.sizeTypeMap)),
69
- tap(x => this._context.$implicit = x),
70
- tap(() => this._viewRef.markForCheck()),
71
- takeUntil(this._destroy$),
72
- ).subscribe();
73
- }
74
-
75
- ngOnDestroy(): void {
76
- this._destroy$.next();
77
- this._destroy$.complete();
78
- }
79
-
80
- private updateView(): void {
81
- this.viewContainer.clear();
82
- this._viewRef = this.viewContainer.createEmbeddedView(this.templateRef, this._context);
83
- }
84
-
85
- }
@@ -1,170 +0,0 @@
1
- import {
2
- OnInit,
3
- OnDestroy,
4
- Directive,
5
- Renderer2,
6
- ViewContainerRef,
7
- Input,
8
- EmbeddedViewRef,
9
- TemplateRef,
10
- ChangeDetectorRef,
11
- } from "@angular/core";
12
- import { Subscription, Subject, tap, filter, pairwise, startWith } from "rxjs";
13
-
14
- import { ViewportService } from "./viewport.service";
15
- import {
16
- isViewportSizeMatcherExpression,
17
- isViewportSizeMatcherTupleExpression,
18
- isViewportConditionMatch
19
- } from "./viewport.util";
20
- import { ViewportSizeTypeInfo, ViewportMatchConditions, ViewportSizeMatcherExpression } from "./viewport.model";
21
-
22
- export class SsvViewportMatcherContext implements ViewportMatchConditions {
23
-
24
- sizeType: string | string[] | null = null;
25
- sizeTypeExclude: string | string[] | null = null;
26
- expression?: ViewportSizeMatcherExpression;
27
-
28
- }
29
-
30
- @Directive({
31
- selector: "[ssvViewportMatcher]",
32
- exportAs: "ssvViewportMatcher",
33
- standalone: true,
34
- })
35
- export class SsvViewportMatcherDirective implements OnInit, OnDestroy {
36
-
37
- sizeInfo: ViewportSizeTypeInfo | undefined;
38
-
39
- private _context: SsvViewportMatcherContext = new SsvViewportMatcherContext();
40
- private _thenTemplateRef: TemplateRef<SsvViewportMatcherContext> | null = null;
41
- private _elseTemplateRef: TemplateRef<SsvViewportMatcherContext> | null = null;
42
- private _thenViewRef: EmbeddedViewRef<SsvViewportMatcherContext> | null = null;
43
- private _elseViewRef: EmbeddedViewRef<SsvViewportMatcherContext> | null = null;
44
- private sizeType$$ = Subscription.EMPTY;
45
- private cssClass$$ = Subscription.EMPTY;
46
- private readonly _update$ = new Subject<SsvViewportMatcherContext>();
47
-
48
- @Input() set ssvViewportMatcher(value: string | string[] | ViewportSizeMatcherExpression) {
49
- if (isViewportSizeMatcherExpression(value)) {
50
- this._context.expression = value;
51
- } else if (isViewportSizeMatcherTupleExpression(value)) {
52
- const [op, size] = value;
53
- this._context.expression = {
54
- operation: op,
55
- size
56
- };
57
- } else {
58
- this._context.sizeType = value;
59
- }
60
-
61
- if (this.sizeInfo) {
62
- this._update$.next(this._context);
63
- }
64
- }
65
-
66
- @Input() set ssvViewportMatcherExclude(value: string | string[]) {
67
- this._context.sizeTypeExclude = value;
68
-
69
- if (this.sizeInfo) {
70
- this._update$.next(this._context);
71
- }
72
- }
73
-
74
- @Input() set ssvViewportMatcherElse(templateRef: TemplateRef<SsvViewportMatcherContext> | null) {
75
- this._elseTemplateRef = templateRef;
76
- this._elseViewRef = null; // clear previous view if any.
77
- if (this.sizeInfo) {
78
- this._update$.next(this._context);
79
- }
80
- }
81
-
82
- constructor(
83
- private viewport: ViewportService,
84
- private renderer: Renderer2,
85
- private viewContainer: ViewContainerRef,
86
- private cdr: ChangeDetectorRef,
87
- templateRef: TemplateRef<SsvViewportMatcherContext>,
88
- ) {
89
- this._thenTemplateRef = templateRef;
90
- }
91
-
92
- ngOnInit(): void {
93
- // console.log("ssvViewportMatcher init");
94
-
95
- this._update$
96
- .pipe(
97
- // tap(x => console.log(">>> ssvViewportMatcher - update triggered", x)),
98
- filter(() => !!this.sizeInfo),
99
- // tap(x => console.log(">>> ssvViewportMatcher - updating...", x)),
100
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
101
- tap(() => this._updateView(this.sizeInfo!)),
102
- tap(() => this.cdr.markForCheck())
103
- )
104
- .subscribe();
105
-
106
- this.sizeType$$ = this.viewport.sizeType$
107
- .pipe(
108
- // tap(x => console.log("ssvViewportMatcher - sizeType changed", x)),
109
- tap(x => this.sizeInfo = x),
110
- tap(() => this._update$.next(this._context)),
111
- )
112
- .subscribe();
113
-
114
- this.cssClass$$ = this.viewport.sizeType$
115
- .pipe(
116
- startWith<ViewportSizeTypeInfo | undefined>(undefined),
117
- filter(() => !!(this._thenViewRef || this._elseViewRef)),
118
- pairwise(),
119
- tap(([prev, curr]) => {
120
- const el: Element = this._thenViewRef
121
- ? this._thenViewRef.rootNodes[0]
122
- : this._elseViewRef?.rootNodes[0];
123
-
124
- if (!el.classList) {
125
- return;
126
- }
127
- if (prev) {
128
- this.renderer.removeClass(el, `ssv-vp-size--${prev.name}`);
129
- }
130
- this.renderer.addClass(el, `ssv-vp-size--${curr?.name}`);
131
- }),
132
- )
133
- .subscribe();
134
- }
135
-
136
- ngOnDestroy(): void {
137
- this.cssClass$$.unsubscribe();
138
- this.sizeType$$.unsubscribe();
139
- this._update$.complete();
140
- }
141
-
142
- private _updateView(sizeInfo: ViewportSizeTypeInfo) {
143
- if (isViewportConditionMatch(sizeInfo, this._context, this.viewport.sizeTypeMap)) {
144
- if (!this._thenViewRef) {
145
- this.viewContainer.clear();
146
- this._elseViewRef = null;
147
-
148
- if (this._thenTemplateRef) {
149
- this._thenViewRef = this.viewContainer.createEmbeddedView(
150
- this._thenTemplateRef,
151
- this._context,
152
- );
153
- }
154
- }
155
- } else {
156
- if (!this._elseViewRef) {
157
- this.viewContainer.clear();
158
- this._thenViewRef = null;
159
-
160
- if (this._elseTemplateRef) {
161
- this._elseViewRef = this.viewContainer.createEmbeddedView(
162
- this._elseTemplateRef,
163
- this._context,
164
- );
165
- }
166
- }
167
- }
168
- }
169
-
170
- }
@@ -1,37 +0,0 @@
1
- import { Injectable, InjectionToken, inject } from "@angular/core";
2
-
3
- import { DeviceType, ViewportSize } from "./viewport.model";
4
-
5
- // todo: make this configurable
6
- /** Viewport size for SSR. */
7
- const viewportSizeSSR: Record<DeviceType, ViewportSize> = {
8
- [DeviceType.desktop]: {
9
- width: 1366,
10
- height: 768,
11
- },
12
- [DeviceType.tablet]: {
13
- width: 768,
14
- height: 1024,
15
- },
16
- [DeviceType.mobile]: {
17
- width: 414,
18
- height: 736,
19
- },
20
- };
21
-
22
- export const VIEWPORT_SSR_DEVICE = new InjectionToken<DeviceType>("UX_VIEWPORT_SSR_DEVICE", {
23
- factory: () => DeviceType.desktop,
24
- });
25
-
26
- @Injectable({
27
- providedIn: "root",
28
- })
29
- export class ViewportServerSizeService {
30
-
31
- private readonly deviceType = inject(VIEWPORT_SSR_DEVICE);
32
-
33
- get(): ViewportSize {
34
- return viewportSizeSSR[this.deviceType] || viewportSizeSSR[DeviceType.desktop];
35
- }
36
-
37
- }
@@ -1,54 +0,0 @@
1
- /**
2
- * The indices of each breakpoint provided based on the `UX_VIEWPORT_DEFAULT_BREAKPOINTS`.
3
- * @see UX_VIEWPORT_DEFAULT_BREAKPOINTS
4
- */
5
- export enum ViewportSizeType {
6
- xsmall = 0,
7
- small = 1,
8
- medium = 2,
9
- large = 3,
10
- xlarge = 4,
11
- xxlarge = 5,
12
- xxlarge1 = 6,
13
- }
14
-
15
- export enum ComparisonOperation {
16
- equals = "=",
17
- notEquals = "<>",
18
- lessThan = "<",
19
- lessOrEqualThan = "<=",
20
- greaterThan = ">",
21
- greaterOrEqualThan = ">=",
22
- }
23
-
24
- export type ComparisonOperationKeyType = keyof typeof ComparisonOperation;
25
- export type ComparisonOperationValueType = "=" | "<>" | "<" | "<=" | ">" | ">="; // todo: find a better way to do this
26
- export type ComparisonOperationLiteral = ComparisonOperationKeyType | ComparisonOperationValueType;
27
-
28
- export enum DeviceType {
29
- desktop = "desktop",
30
- mobile = "mobile",
31
- tablet = "tablet"
32
- }
33
-
34
- export interface ViewportSize {
35
- width: number;
36
- height: number;
37
- }
38
-
39
- export interface ViewportSizeTypeInfo {
40
- type: number;
41
- name: string;
42
- widthThreshold: number;
43
- }
44
-
45
- export interface ViewportMatchConditions {
46
- sizeType?: string | string[] | null;
47
- sizeTypeExclude?: string | string[] | null;
48
- expression?: ViewportSizeMatcherExpression;
49
- }
50
-
51
- export interface ViewportSizeMatcherExpression {
52
- size: string;
53
- operation: ComparisonOperationLiteral;
54
- }