ngx-mq 2.11.3 → 3.1.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.
- package/LICENSE +1 -1
- package/README.md +170 -123
- package/fesm2022/ngx-mq.mjs +465 -16
- package/fesm2022/ngx-mq.mjs.map +1 -1
- package/index.d.ts +446 -3
- package/package.json +20 -5
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,190 +1,241 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/martsinlabs/ngx-mq/refs/heads/main/assets/logo.svg" width="
|
|
2
|
+
<img src="https://raw.githubusercontent.com/martsinlabs/ngx-mq/refs/heads/main/assets/logo.svg" width="130" alt="ngx-mq logo" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h3 align="center">
|
|
6
|
-
|
|
7
|
-
</h3>
|
|
5
|
+
<h3 align="center">Signal-powered breakpoints & media queries for Angular</h3>
|
|
6
|
+
<br />
|
|
8
7
|
|
|
9
|
-
<
|
|
8
|
+
<p align="center">
|
|
10
9
|
<a href="https://github.com/martsinlabs/ngx-mq/actions/workflows/ci.yml">
|
|
11
|
-
<img src="https://img.shields.io/github/actions/workflow/status/martsinlabs/ngx-mq/ci.yml?branch=main&label=CI&color=44cc11&logo=github" alt="CI
|
|
10
|
+
<img src="https://img.shields.io/github/actions/workflow/status/martsinlabs/ngx-mq/ci.yml?branch=main&label=CI&color=44cc11&logo=github" alt="CI status" />
|
|
12
11
|
</a>
|
|
13
12
|
<a href="https://codecov.io/gh/martsinlabs/ngx-mq">
|
|
14
13
|
<img src="https://codecov.io/gh/martsinlabs/ngx-mq/branch/main/graph/badge.svg" alt="coverage" />
|
|
15
14
|
</a>
|
|
16
|
-
<br>
|
|
17
15
|
<a href="https://www.npmjs.com/package/ngx-mq">
|
|
18
16
|
<img src="https://img.shields.io/npm/v/ngx-mq.svg?color=007ec6" alt="npm version" />
|
|
19
17
|
</a>
|
|
20
18
|
<a href="https://www.npmjs.com/package/ngx-mq">
|
|
21
19
|
<img src="https://img.shields.io/npm/dm/ngx-mq.svg?color=44cc11" alt="npm downloads" />
|
|
22
20
|
</a>
|
|
21
|
+
<a href="https://bundlephobia.com/package/ngx-mq">
|
|
22
|
+
<img src="https://img.shields.io/bundlephobia/minzip/ngx-mq.svg?color=44cc11&label=minzip" alt="minzipped size" />
|
|
23
|
+
</a>
|
|
23
24
|
<a href="https://opensource.org/license/MIT">
|
|
24
25
|
<img src="https://img.shields.io/npm/l/ngx-mq.svg?color=44cc11" alt="license" />
|
|
25
26
|
</a>
|
|
26
|
-
</
|
|
27
|
+
</p>
|
|
27
28
|
|
|
28
|
-
<
|
|
29
|
+
<p align="center">
|
|
30
|
+
<a href="https://martsinlabs.github.io/ngx-mq"><b>Documentation</b></a>
|
|
31
|
+
·
|
|
32
|
+
<a href="https://stackblitz.com/github/martsinlabs/ngx-mq-demo/tree/demo/v3"><b>Live demo</b></a>
|
|
33
|
+
·
|
|
34
|
+
<a href="#why-ngx-mq"><b>Why ngx-mq?</b></a>
|
|
35
|
+
</p>
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
---
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
- SSR-safe
|
|
34
|
-
- Auto-cleanup
|
|
35
|
-
- Angular 19–21 (use `ngx-mq@1` for Angular 16–18)
|
|
36
|
-
- Well-tested
|
|
39
|
+
## Overview
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
A responsive value is just a signal: read it in the template, compose it, and never wire up cleanup.
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
```ts
|
|
44
|
+
import { Component } from '@angular/core';
|
|
45
|
+
import { up } from 'ngx-mq';
|
|
46
|
+
|
|
47
|
+
@Component({
|
|
48
|
+
selector: 'app-root',
|
|
49
|
+
template: `
|
|
50
|
+
@if (isDesktop()) {
|
|
51
|
+
<app-sidebar />
|
|
52
|
+
}
|
|
53
|
+
`,
|
|
54
|
+
})
|
|
55
|
+
export class AppComponent {
|
|
56
|
+
readonly isDesktop = up('lg');
|
|
57
|
+
}
|
|
58
|
+
```
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
- **Signal-native** so it works anywhere signals do, zoneless apps included.
|
|
61
|
+
- **Zero boilerplate**: no subscriptions, no `unsubscribe`, cleanup is automatic.
|
|
62
|
+
- **SSR-safe** with a value you control on the server.
|
|
63
|
+
- **Batteries included**: Tailwind, Bootstrap and Material presets, plus `and` / `or` / `not`.
|
|
64
|
+
- **Tiny**: ~1.9 kB gzipped, and no RxJS.
|
|
43
65
|
|
|
44
|
-
##
|
|
66
|
+
## Install
|
|
45
67
|
|
|
46
|
-
|
|
68
|
+
```bash
|
|
69
|
+
npm i ngx-mq # Angular 20-22
|
|
70
|
+
```
|
|
47
71
|
|
|
48
|
-
|
|
72
|
+
<sub>Angular 19 -> <code>ngx-mq@2</code> · Angular 16-18 -> <code>ngx-mq@1</code></sub>
|
|
49
73
|
|
|
50
|
-
|
|
74
|
+
Then register your breakpoints once, at bootstrap:
|
|
51
75
|
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
npm install ngx-mq@1
|
|
76
|
+
```ts
|
|
77
|
+
import { provideBreakpoints } from 'ngx-mq';
|
|
55
78
|
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
bootstrapApplication(AppComponent, {
|
|
80
|
+
providers: [provideBreakpoints({ sm: 640, md: 768, lg: 1024 })],
|
|
81
|
+
// or a preset: provideTailwindBreakpoints() / provideBootstrapBreakpoints() / provideMaterialBreakpoints()
|
|
82
|
+
});
|
|
58
83
|
```
|
|
59
84
|
|
|
60
|
-
|
|
85
|
+
> Call the helpers inside an [injection context](https://angular.dev/guide/di/dependency-injection-context): a component field, a constructor, or a DI factory.
|
|
61
86
|
|
|
62
|
-
|
|
87
|
+
## Examples
|
|
63
88
|
|
|
64
|
-
|
|
89
|
+
#### Show different layouts per screen size
|
|
65
90
|
|
|
66
91
|
```ts
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
92
|
+
readonly isMobile = down('md');
|
|
93
|
+
readonly isTablet = between('md', 'lg');
|
|
94
|
+
readonly isDesktop = up('lg');
|
|
95
|
+
```
|
|
70
96
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
sm: 640,
|
|
76
|
-
md: 768,
|
|
77
|
-
lg: 1024,
|
|
78
|
-
}),
|
|
79
|
-
],
|
|
80
|
-
});
|
|
97
|
+
#### Follow the system dark mode
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
readonly prefersDark = colorScheme('dark');
|
|
81
101
|
```
|
|
82
102
|
|
|
83
|
-
|
|
103
|
+
#### Drop hover styles on touch devices
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
```ts
|
|
106
|
+
// `hover()` has no direct inverse, so compose it
|
|
107
|
+
readonly isTouchLike = not(hover());
|
|
108
|
+
```
|
|
88
109
|
|
|
89
|
-
|
|
110
|
+
#### Combine any conditions
|
|
90
111
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
| `down` | `bp: string, options?: CreateMediaQueryOptions` | `Signal<boolean>` | `true` when viewport width < breakpoint |
|
|
95
|
-
| `between` | `minBp: string, maxBp: string, options?: CreateMediaQueryOptions` | `Signal<boolean>` | `true` when viewport width is in range [min, max) |
|
|
112
|
+
```ts
|
|
113
|
+
// Large screen, in landscape, with a hover-capable pointer
|
|
114
|
+
readonly isLandscapeDesktop = and(up('lg'), orientation('landscape'), hover());
|
|
96
115
|
|
|
97
|
-
|
|
116
|
+
// Small screens OR a reduced-motion preference
|
|
117
|
+
readonly prefersSimpleUi = or(down('md'), reducedMotion());
|
|
118
|
+
```
|
|
98
119
|
|
|
99
|
-
|
|
120
|
+
#### Respect reduced motion
|
|
100
121
|
|
|
101
122
|
```ts
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
import { up, down, between } from 'ngx-mq';
|
|
123
|
+
readonly reduceMotion = reducedMotion();
|
|
124
|
+
```
|
|
105
125
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
126
|
+
#### Anything else, with a raw query
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
readonly isRetina = matchMediaSignal('(min-resolution: 2dppx)');
|
|
109
130
|
```
|
|
110
131
|
|
|
111
|
-
##
|
|
132
|
+
## Why ngx-mq?
|
|
112
133
|
|
|
113
|
-
|
|
134
|
+
Angular's CDK ships [`BreakpointObserver`](https://material.angular.io/cdk/layout/overview), which works well but is built around RxJS and raw query strings. `ngx-mq` is built for the signals era: read a value in the template, subscribe to nothing, clean up automatically.
|
|
114
135
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
| `anyPointer` | `value: 'fine' \| 'coarse' \| 'none', options?: CreateMediaQueryOptions` | `Signal<boolean>` | `true` when any available input device matches the specified pointer type. |
|
|
125
|
-
| `colorGamut` | `value: 'srgb' \| 'p3' \| 'rec2020', options?: CreateMediaQueryOptions` | `Signal<boolean>` | `true` when the user's display supports the specified color gamut. |
|
|
136
|
+
| | `ngx-mq` | CDK `BreakpointObserver` |
|
|
137
|
+
| --------------------- | ------------------------------------------- | ------------------------------------- |
|
|
138
|
+
| Reactivity | `Signal<boolean>` | `Observable<BreakpointState>` |
|
|
139
|
+
| Cleanup | Automatic via `DestroyRef` | Manual (`takeUntilDestroyed`) |
|
|
140
|
+
| Named breakpoints | Tailwind / Bootstrap / Material or your own | Material breakpoints or raw strings |
|
|
141
|
+
| Media-feature helpers | `colorScheme`, `hover`, `pointer`, ... | Raw query strings |
|
|
142
|
+
| Composition | `and` / `or` / `not` | RxJS operators |
|
|
143
|
+
| SSR | Configurable static value | Handle it yourself |
|
|
144
|
+
| Footprint | ~1.9 kB standalone | Part of `@angular/cdk` |
|
|
126
145
|
|
|
127
|
-
|
|
146
|
+
## Documentation
|
|
128
147
|
|
|
129
|
-
|
|
148
|
+
Spin it up in seconds on [StackBlitz](https://stackblitz.com/github/martsinlabs/ngx-mq-demo/tree/demo/v3), no setup required.
|
|
130
149
|
|
|
131
|
-
|
|
150
|
+
Full API reference, guides and recipes live at **[martsinlabs.github.io/ngx-mq](https://martsinlabs.github.io/ngx-mq)**.
|
|
132
151
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
| `matchMediaSignal` | `query: string, options?: CreateMediaQueryOptions` | `Signal<boolean>` | Provides a signal representing the state of a media query |
|
|
152
|
+
<details>
|
|
153
|
+
<summary><b>API reference</b> (quick view)</summary>
|
|
136
154
|
|
|
137
|
-
>
|
|
155
|
+
<br>
|
|
138
156
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
import { matchMediaSignal } from 'ngx-mq';
|
|
157
|
+
Every query helper returns a `Signal<boolean>` and accepts an optional `options` argument
|
|
158
|
+
([`CreateMediaQueryOptions`](#options-and-types)).
|
|
142
159
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
160
|
+
**Breakpoints**
|
|
161
|
+
|
|
162
|
+
| Helper | Arguments | `true` when |
|
|
163
|
+
| --------- | ---------------- | ------------------------------------ |
|
|
164
|
+
| `up` | `bp` | viewport width `>=` `bp` |
|
|
165
|
+
| `down` | `bp` | viewport width `<` `bp` (exclusive) |
|
|
166
|
+
| `between` | `minBp`, `maxBp` | viewport width is in `[minBp, maxBp)` |
|
|
167
|
+
|
|
168
|
+
`down` and `between` upper bounds are exclusive: a small epsilon (default `0.02`, set via `provideBreakpointEpsilon`) is subtracted from the max so adjacent ranges never overlap.
|
|
169
|
+
|
|
170
|
+
**Media features**
|
|
171
|
+
|
|
172
|
+
| Helper | Arguments | `true` when |
|
|
173
|
+
| --------------- | ------------------------------ | ---------------------------------------- |
|
|
174
|
+
| `orientation` | `'portrait' \| 'landscape'` | the screen orientation matches |
|
|
175
|
+
| `colorScheme` | `'light' \| 'dark'` | the system color scheme matches |
|
|
176
|
+
| `displayMode` | `DisplayModeOption` | the display mode matches (PWA detection) |
|
|
177
|
+
| `reducedMotion` | none | the user prefers reduced motion |
|
|
178
|
+
| `prefersContrast` | `'more' \| 'less' \| 'no-preference' \| 'custom'` | the user's contrast preference matches |
|
|
179
|
+
| `hover` | none | the primary pointer can hover |
|
|
180
|
+
| `anyHover` | none | any available pointer can hover |
|
|
181
|
+
| `pointer` | `'fine' \| 'coarse' \| 'none'` | the primary pointer matches |
|
|
182
|
+
| `anyPointer` | `'fine' \| 'coarse' \| 'none'` | any available pointer matches |
|
|
183
|
+
| `colorGamut` | `'srgb' \| 'p3' \| 'rec2020'` | the display covers the gamut |
|
|
184
|
+
|
|
185
|
+
**Composition**
|
|
146
186
|
|
|
147
|
-
|
|
187
|
+
| Helper | Arguments | `true` when |
|
|
188
|
+
| ------ | ---------------------------------- | ----------------------------------------- |
|
|
189
|
+
| `and` | `...conditions: Signal<boolean>[]` | every condition is `true` (empty: `true`) |
|
|
190
|
+
| `or` | `...conditions: Signal<boolean>[]` | any condition is `true` (empty: `false`) |
|
|
191
|
+
| `not` | `condition: Signal<boolean>` | the condition is `false` |
|
|
148
192
|
|
|
149
|
-
|
|
193
|
+
**Custom queries**
|
|
150
194
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
153
|
-
| `
|
|
154
|
-
| `provideTailwindBreakpoints()` | none | Registers the default Tailwind CSS breakpoints. |
|
|
155
|
-
| `provideBootstrapBreakpoints()` | none | Registers the default Bootstrap breakpoints. |
|
|
156
|
-
| `provideMaterialBreakpoints()` | none | Registers the default Material 2 breakpoints. |
|
|
157
|
-
| `provideBreakpointEpsilon()` | `epsilon: number` | Sets the epsilon threshold used when comparing breakpoint values. |
|
|
158
|
-
| `provideSsrValue()` | `value: boolean` | Defines the static signal value used during SSR, since media queries are not available on the server. Defaults to `false`. |
|
|
195
|
+
| Helper | Arguments | Description |
|
|
196
|
+
| ------------------ | --------------- | -------------------------------------------- |
|
|
197
|
+
| `matchMediaSignal` | `query: string` | A signal for any raw CSS media query |
|
|
159
198
|
|
|
160
|
-
|
|
199
|
+
**Providers**
|
|
161
200
|
|
|
162
|
-
|
|
201
|
+
| Provider | Argument | Description |
|
|
202
|
+
| ----------------------------- | -------------------- | ------------------------------------------------------- |
|
|
203
|
+
| `provideBreakpoints` | `bps: MqBreakpoints` | Registers a custom breakpoint map |
|
|
204
|
+
| `provideTailwindBreakpoints` | none | Registers the Tailwind preset |
|
|
205
|
+
| `provideBootstrapBreakpoints` | none | Registers the Bootstrap preset |
|
|
206
|
+
| `provideMaterialBreakpoints` | none | Registers the Material 2 preset |
|
|
207
|
+
| `provideBreakpointEpsilon` | `epsilon: number` | Sets the exclusive-bound epsilon (default `0.02`) |
|
|
208
|
+
| `provideSsrValue` | `value: boolean` | Sets the value signals report during SSR (default `false`) |
|
|
209
|
+
|
|
210
|
+
**Options and types**
|
|
163
211
|
|
|
164
212
|
```ts
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Static signal value used during SSR.
|
|
170
|
-
*/
|
|
171
|
-
ssrValue?: boolean;
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* A debug name for the signal. Used in Angular DevTools to identify the signal.
|
|
175
|
-
*/
|
|
176
|
-
debugName?: string;
|
|
213
|
+
interface CreateMediaQueryOptions {
|
|
214
|
+
ssrValue?: boolean; // value reported during SSR; overrides provideSsrValue
|
|
215
|
+
debugName?: string; // shown for the signal in Angular DevTools
|
|
177
216
|
}
|
|
178
217
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
| 'standalone'
|
|
183
|
-
| 'minimal-ui'
|
|
184
|
-
| 'window-controls-overlay'
|
|
185
|
-
| 'picture-in-picture';
|
|
218
|
+
type MqBreakpoints = Record<string, number>;
|
|
219
|
+
|
|
220
|
+
type DisplayModeOption =
|
|
221
|
+
| 'browser' | 'fullscreen' | 'standalone'
|
|
222
|
+
| 'minimal-ui' | 'window-controls-overlay' | 'picture-in-picture';
|
|
186
223
|
```
|
|
187
224
|
|
|
225
|
+
</details>
|
|
226
|
+
|
|
227
|
+
## Server-side rendering
|
|
228
|
+
|
|
229
|
+
`matchMedia` does not exist on the server, so each signal returns a static value during SSR and switches to the live result after hydration. Set the default with `provideSsrValue(true)`, or override per call with `up('lg', { ssrValue: true })`.
|
|
230
|
+
|
|
231
|
+
## Contributing
|
|
232
|
+
|
|
233
|
+
Contributions are welcome. See [CONTRIBUTING.md](https://github.com/martsinlabs/ngx-mq/blob/main/CONTRIBUTING.md) and [ARCHITECTURE.md](https://github.com/martsinlabs/ngx-mq/blob/main/ARCHITECTURE.md).
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
[MIT](https://github.com/martsinlabs/ngx-mq/blob/main/LICENSE) © Martsin Labs
|
|
238
|
+
|
|
188
239
|
## Sponsors
|
|
189
240
|
|
|
190
241
|
<table>
|
|
@@ -194,7 +245,7 @@ export type DisplayModeOption =
|
|
|
194
245
|
<img
|
|
195
246
|
src="https://raw.githubusercontent.com/getsentry/sentry/7b65f0f23d7eb5ccc035b12776bf5d8f3d9f8965/static/images/logo-sentry.svg"
|
|
196
247
|
width="120"
|
|
197
|
-
alt="Sentry
|
|
248
|
+
alt="Sentry" />
|
|
198
249
|
</p>
|
|
199
250
|
<p>
|
|
200
251
|
<a href="https://sentry.io" target="_blank"><strong>Sentry</strong></a>
|
|
@@ -204,7 +255,3 @@ export type DisplayModeOption =
|
|
|
204
255
|
</td>
|
|
205
256
|
</tr>
|
|
206
257
|
</table>
|
|
207
|
-
|
|
208
|
-
## Contributing
|
|
209
|
-
|
|
210
|
-
[CONTRIBUTING.md](https://github.com/martsinlabs/ngx-mq/blob/main/CONTRIBUTING.md)
|