match-breakpoint 1.0.0-canary.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Oleg Putseiko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ <div align="center">
2
+
3
+ # match-breakpoint
4
+
5
+ [![Latest Release](https://badgen.net/github/release/oleg-putseiko/match-breakpoint?icon=github&cache=240)](https://github.com/oleg-putseiko/match-breakpoint/releases)
6
+ [![Total Downloads](https://badgen.net/npm/dt/match-breakpoint?icon=npm&cache=240)](https://www.npmjs.com/package/match-breakpoint)
7
+ [![Install Size](https://badgen.net/packagephobia/install/match-breakpoint?color=purple&cache=240)](https://www.npmjs.com/package/match-breakpoint)
8
+ [![License](https://badgen.net/npm/license/match-breakpoint?color=black&cache=240)](https://github.com/oleg-putseiko/match-breakpoint/blob/main/LICENSE.md)
9
+
10
+ </div>
11
+
12
+ A library of optimized React components and hooks for matching screen widths.
13
+
14
+ # Installation
15
+
16
+ ```bash
17
+ yarn add match-breakpoint@latest
18
+
19
+ // or
20
+
21
+ npm install match-breakpoint@latest
22
+
23
+ // or
24
+
25
+ pnpm install match-breakpoint@latest
26
+ ```
27
+
28
+ # Advantages
29
+
30
+ ❇️ ⚡️ The library's components and hooks use a finite and predefined number of media match listeners, which reduces the computational load.
31
+
32
+ ❇️ ✨ Using `Breakpoint` components allows you to avoid cumulative layout shift.
33
+
34
+ ❇️ 💫 Conditional content display is compatible with most CSS frameworks, preprocessors, etc. In particular, it's possible to use the Tailwind CSS class name preset out of the box.
35
+
36
+ ❇️ 🛡️ The library supports breakpoint typing used in all components and hooks from one place.
37
+
38
+ # Getting started
39
+
40
+ First of all, you need to add a breakpoints provider to your application:
41
+
42
+ ```tsx
43
+ import { BreakpointsProvider } from 'match-breakpoint';
44
+
45
+ const App: FC = () => (
46
+ <BreakpointsProvider breakpoints={/* … */}>
47
+ {/* … */}
48
+ {/* … */}
49
+ </BreakpointsProvider>
50
+ );
51
+ ```
52
+
53
+ Next you need to define the breakpoints that will be used in the application:
54
+
55
+ ```tsx
56
+ const BREAKPOINTS = {
57
+ // …
58
+ md: '768px',
59
+ lg: '1024px',
60
+ xl: '1280px',
61
+ // …
62
+ };
63
+
64
+ const App: FC = () => (
65
+ <BreakpointsProvider breakpoints={BREAKPOINTS}>
66
+ {/* … */}
67
+ {/* … */}
68
+ </BreakpointsProvider>
69
+ );
70
+ ```
71
+
72
+ You can also pass a value from some configuration as breakpoints, for example from the Tailwind CSS configuration:
73
+
74
+ ```tsx
75
+ import tailwindConfig from '../../tailwind.config.ts';
76
+
77
+ const DEFAULT_BREAKPOINTS = {
78
+ // …
79
+ md: '768px',
80
+ lg: '1024px',
81
+ xl: '1280px',
82
+ // …
83
+ };
84
+
85
+ const breakpoints = tailwindConfig.theme?.screens ?? DEFAULT_BREAKPOINTS;
86
+
87
+ const App: FC = () => (
88
+ <BreakpointsProvider breakpoints={breakpoints}>
89
+ {/* … */}
90
+ {/* … */}
91
+ </BreakpointsProvider>
92
+ );
93
+ ```
94
+
95
+ After that you can use the `Breakpoint` component, for example:
96
+
97
+ ```tsx
98
+ import { Breakpoint } from 'match-breakpoint';
99
+
100
+ const Content: FC = () => (
101
+ <>
102
+ <Breakpoint size="md">Desktop Device Content</Breakpoint>
103
+
104
+ <Breakpoint size="md" matchTo="max">
105
+ Mobile Device Content
106
+ </Breakpoint>
107
+ </>
108
+ );
109
+ ```
110
+
111
+ Or using the `useBreakpoint` hook:
112
+
113
+ ```tsx
114
+ import { useBreakpoint } from 'match-breakpoint';
115
+
116
+ const Content: FC = () => {
117
+ const isScreenMinMd = useBreakpoint('md');
118
+
119
+ if (isScreenMinMd) {
120
+ return 'Desktop Device Content';
121
+ }
122
+
123
+ return 'Mobile Device Content';
124
+ };
125
+ ```
126
+
127
+ # Components
128
+
129
+ ## BreakpointsProvider
130
+
131
+ Defined breakpoint contexts.
132
+
133
+ ### Usage:
134
+
135
+ Example:
136
+
137
+ ```tsx
138
+ const BREAKPOINTS = {
139
+ md: 768, // Same as '768px'
140
+ lg: '1024px',
141
+ xl: {
142
+ min: '1280px',
143
+ minClassName: 'max-xl:hidden',
144
+ },
145
+ combined: {
146
+ max: '768px',
147
+ maxClassName: 'md:hidden',
148
+
149
+ min: '1280px',
150
+ minClassName: 'max-xl:hidden',
151
+ },
152
+ };
153
+
154
+ const App: FC = () => (
155
+ <BreakpointsProvider breakpoints={BREAKPOINTS}>
156
+ {/* … */}
157
+ {/* … */}
158
+ </BreakpointsProvider>
159
+ );
160
+ ```
161
+
162
+ ### Properties:
163
+
164
+ | Property | Type | Default value | Description |
165
+ | -------------------- | ---------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
166
+ | breakpoints | `Record<string, string \| number \| BreakpointData>` | - | Defines the set of breakpoints used in the application. |
167
+ | classNamePreset | `'tailwind' \| undefined` | `undefined` | Defines a preset of class names used to hide elements. |
168
+ | mergeClassesFunction | `MergeClassesFunction \| undefined` | `(...classes) => classes.filter(Boolean).join(' ')` | Defines a function for merging classes when adding them to elements. For example, you can pass a function to prevent class name conflicts. |
169
+ | children | `ReactNode` | - | Required child elements. |
@@ -0,0 +1,17 @@
1
+ import { type ComponentPropsWithoutRef, type ElementType, type ReactNode } from 'react';
2
+ import { type Breakpoints, type MatchTo, type ScreenSize } from '../../types/breakpoints';
3
+ import { type ExtractRef } from '../../types/react';
4
+ type BreakpointControlledProps<T extends ElementType> = {
5
+ as?: T;
6
+ size: ScreenSize;
7
+ breakpoints?: Breakpoints;
8
+ matchTo?: MatchTo;
9
+ isDefaultMatches?: boolean;
10
+ children: ReactNode;
11
+ };
12
+ type BreakpointComponentNativeProps<T extends ElementType> = Omit<ComponentPropsWithoutRef<T>, keyof BreakpointControlledProps<T>>;
13
+ type BreakpointProps<T extends ElementType> = BreakpointComponentNativeProps<T> & BreakpointControlledProps<T>;
14
+ export declare const Breakpoint: <T extends ElementType = import("react").ExoticComponent<{
15
+ children?: ReactNode;
16
+ }>>(props: BreakpointProps<T>, ref: ExtractRef<BreakpointComponentNativeProps<T>>) => import("react/jsx-runtime").JSX.Element[] | null | undefined;
17
+ export {};
@@ -0,0 +1,103 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _object_spread(target) {
15
+ for(var i = 1; i < arguments.length; i++){
16
+ var source = arguments[i] != null ? arguments[i] : {};
17
+ var ownKeys = Object.keys(source);
18
+ if (typeof Object.getOwnPropertySymbols === "function") {
19
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
21
+ }));
22
+ }
23
+ ownKeys.forEach(function(key) {
24
+ _define_property(target, key, source[key]);
25
+ });
26
+ }
27
+ return target;
28
+ }
29
+ function ownKeys(object, enumerableOnly) {
30
+ var keys = Object.keys(object);
31
+ if (Object.getOwnPropertySymbols) {
32
+ var symbols = Object.getOwnPropertySymbols(object);
33
+ if (enumerableOnly) {
34
+ symbols = symbols.filter(function(sym) {
35
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
36
+ });
37
+ }
38
+ keys.push.apply(keys, symbols);
39
+ }
40
+ return keys;
41
+ }
42
+ function _object_spread_props(target, source) {
43
+ source = source != null ? source : {};
44
+ if (Object.getOwnPropertyDescriptors) {
45
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
46
+ } else {
47
+ ownKeys(Object(source)).forEach(function(key) {
48
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
49
+ });
50
+ }
51
+ return target;
52
+ }
53
+ function _object_without_properties(source, excluded) {
54
+ if (source == null) return {};
55
+ var target = _object_without_properties_loose(source, excluded);
56
+ var key, i;
57
+ if (Object.getOwnPropertySymbols) {
58
+ var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
59
+ for(i = 0; i < sourceSymbolKeys.length; i++){
60
+ key = sourceSymbolKeys[i];
61
+ if (excluded.indexOf(key) >= 0) continue;
62
+ if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
63
+ target[key] = source[key];
64
+ }
65
+ }
66
+ return target;
67
+ }
68
+ function _object_without_properties_loose(source, excluded) {
69
+ if (source == null) return {};
70
+ var target = {};
71
+ var sourceKeys = Object.keys(source);
72
+ var key, i;
73
+ for(i = 0; i < sourceKeys.length; i++){
74
+ key = sourceKeys[i];
75
+ if (excluded.indexOf(key) >= 0) continue;
76
+ target[key] = source[key];
77
+ }
78
+ return target;
79
+ }
80
+ import { jsx as _jsx } from "react/jsx-runtime";
81
+ import { Children, forwardRef, Fragment } from "react";
82
+ import { BreakpointChild } from "./BreakpointChild";
83
+ import { useMatchBreakpoint } from "../BreakpointsProvider";
84
+ const DEFAULT_COMPONENT = Fragment;
85
+ const BreakpointRenderFunction = (props, ref)=>{
86
+ const { as = DEFAULT_COMPONENT, size, matchTo = 'min', isDefaultMatches = true, children } = props, componentProps = _object_without_properties(props, [
87
+ "as",
88
+ "size",
89
+ "matchTo",
90
+ "isDefaultMatches",
91
+ "children"
92
+ ]);
93
+ const shouldRenderChildren = useMatchBreakpoint(size, matchTo, isDefaultMatches);
94
+ if (!shouldRenderChildren) return null;
95
+ return Children.map(children, (child)=>/*#__PURE__*/ _jsx(BreakpointChild, _object_spread_props(_object_spread({}, componentProps), {
96
+ ref: ref,
97
+ as: as,
98
+ size: size,
99
+ matchTo: matchTo,
100
+ child: child
101
+ })));
102
+ };
103
+ export const Breakpoint = /*#__PURE__*/ forwardRef(BreakpointRenderFunction);
@@ -0,0 +1,15 @@
1
+ import { type ComponentProps, type ElementType, type ReactNode } from 'react';
2
+ import { type MatchTo, type ScreenSize } from '../../types/breakpoints';
3
+ import { type ExtractRef } from '../../types/react';
4
+ type ControlledProps<E extends ElementType> = {
5
+ as?: E;
6
+ size: ScreenSize;
7
+ matchTo: MatchTo;
8
+ child: ReactNode;
9
+ };
10
+ type ComponentNativeProps<E extends ElementType> = Omit<ComponentProps<E>, 'children' | keyof ControlledProps<E>>;
11
+ type TailwindBreakpointProps<E extends ElementType> = ComponentNativeProps<E> & ControlledProps<E>;
12
+ export declare const BreakpointChild: <T extends ElementType = import("react").ExoticComponent<{
13
+ children?: ReactNode;
14
+ }>>(props: TailwindBreakpointProps<T>, ref: ExtractRef<ComponentNativeProps<T>>) => import("react/jsx-runtime").JSX.Element;
15
+ export {};
@@ -0,0 +1,155 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _object_spread(target) {
15
+ for(var i = 1; i < arguments.length; i++){
16
+ var source = arguments[i] != null ? arguments[i] : {};
17
+ var ownKeys = Object.keys(source);
18
+ if (typeof Object.getOwnPropertySymbols === "function") {
19
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
21
+ }));
22
+ }
23
+ ownKeys.forEach(function(key) {
24
+ _define_property(target, key, source[key]);
25
+ });
26
+ }
27
+ return target;
28
+ }
29
+ function ownKeys(object, enumerableOnly) {
30
+ var keys = Object.keys(object);
31
+ if (Object.getOwnPropertySymbols) {
32
+ var symbols = Object.getOwnPropertySymbols(object);
33
+ if (enumerableOnly) {
34
+ symbols = symbols.filter(function(sym) {
35
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
36
+ });
37
+ }
38
+ keys.push.apply(keys, symbols);
39
+ }
40
+ return keys;
41
+ }
42
+ function _object_spread_props(target, source) {
43
+ source = source != null ? source : {};
44
+ if (Object.getOwnPropertyDescriptors) {
45
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
46
+ } else {
47
+ ownKeys(Object(source)).forEach(function(key) {
48
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
49
+ });
50
+ }
51
+ return target;
52
+ }
53
+ function _object_without_properties(source, excluded) {
54
+ if (source == null) return {};
55
+ var target = _object_without_properties_loose(source, excluded);
56
+ var key, i;
57
+ if (Object.getOwnPropertySymbols) {
58
+ var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
59
+ for(i = 0; i < sourceSymbolKeys.length; i++){
60
+ key = sourceSymbolKeys[i];
61
+ if (excluded.indexOf(key) >= 0) continue;
62
+ if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
63
+ target[key] = source[key];
64
+ }
65
+ }
66
+ return target;
67
+ }
68
+ function _object_without_properties_loose(source, excluded) {
69
+ if (source == null) return {};
70
+ var target = {};
71
+ var sourceKeys = Object.keys(source);
72
+ var key, i;
73
+ for(i = 0; i < sourceKeys.length; i++){
74
+ key = sourceKeys[i];
75
+ if (excluded.indexOf(key) >= 0) continue;
76
+ target[key] = source[key];
77
+ }
78
+ return target;
79
+ }
80
+ import { jsx as _jsx } from "react/jsx-runtime";
81
+ import { cloneElement, forwardRef, Fragment, isValidElement } from "react";
82
+ import { getMaxScreenWidth, getMinScreenWidth } from "../../utils/screen-width";
83
+ import { useBreakpointsContext } from "../BreakpointsProvider/BreakpointsProvider";
84
+ const DEFAULT_COMPONENT = Fragment;
85
+ const CLASS_NAME_PRESETS = {
86
+ tailwind: {
87
+ maxClassName: 'min-[var(--max-screen-width)]:hidden',
88
+ minClassName: 'max-[var(--min-screen-width)]:hidden'
89
+ }
90
+ };
91
+ const buildProps = (options)=>{
92
+ const { ref, initialProps, matchTo, breakpoint, classNamePreset, mergeClassesFunction } = options;
93
+ const classNamePresetData = classNamePreset !== undefined ? CLASS_NAME_PRESETS[classNamePreset] : null;
94
+ if (matchTo === 'max') {
95
+ var _breakpoint_data_maxClassName;
96
+ return _object_spread_props(_object_spread({}, initialProps), {
97
+ ref,
98
+ style: _object_spread_props(_object_spread({}, initialProps === null || initialProps === void 0 ? void 0 : initialProps.style), {
99
+ ['--max-screen-width']: getMaxScreenWidth(breakpoint.data)
100
+ }),
101
+ className: mergeClassesFunction(initialProps === null || initialProps === void 0 ? void 0 : initialProps.className, typeof breakpoint.data === 'object' ? (_breakpoint_data_maxClassName = breakpoint.data.maxClassName) !== null && _breakpoint_data_maxClassName !== void 0 ? _breakpoint_data_maxClassName : classNamePresetData === null || classNamePresetData === void 0 ? void 0 : classNamePresetData.maxClassName : classNamePresetData === null || classNamePresetData === void 0 ? void 0 : classNamePresetData.maxClassName)
102
+ });
103
+ }
104
+ var _breakpoint_data_minClassName;
105
+ return _object_spread_props(_object_spread({}, initialProps), {
106
+ ref,
107
+ style: _object_spread_props(_object_spread({}, initialProps === null || initialProps === void 0 ? void 0 : initialProps.style), {
108
+ ['--min-screen-width']: getMinScreenWidth(breakpoint.data)
109
+ }),
110
+ className: mergeClassesFunction(initialProps === null || initialProps === void 0 ? void 0 : initialProps.className, typeof breakpoint.data === 'object' ? (_breakpoint_data_minClassName = breakpoint.data.minClassName) !== null && _breakpoint_data_minClassName !== void 0 ? _breakpoint_data_minClassName : classNamePresetData === null || classNamePresetData === void 0 ? void 0 : classNamePresetData.minClassName : classNamePresetData === null || classNamePresetData === void 0 ? void 0 : classNamePresetData.minClassName)
111
+ });
112
+ };
113
+ const BreakpointChildRenderFunction = (props, ref)=>{
114
+ const { as: Component = DEFAULT_COMPONENT, size, matchTo, child } = props, componentProps = _object_without_properties(props, [
115
+ "as",
116
+ "size",
117
+ "matchTo",
118
+ "child"
119
+ ]);
120
+ const { breakpoints, classNamePreset, mergeClassesFunction } = useBreakpointsContext();
121
+ const breakpoint = breakpoints[size];
122
+ if (!breakpoint) {
123
+ throw new Error(`Breakpoint with screen size "${size}" is not defined`);
124
+ }
125
+ if (Component === Fragment && /*#__PURE__*/ isValidElement(child)) {
126
+ return /*#__PURE__*/ cloneElement(child, buildProps({
127
+ initialProps: child.props,
128
+ matchTo,
129
+ breakpoint,
130
+ classNamePreset,
131
+ mergeClassesFunction
132
+ }));
133
+ }
134
+ if (Component !== Fragment) {
135
+ return /*#__PURE__*/ _jsx(Component, _object_spread_props(_object_spread({}, buildProps({
136
+ initialProps: componentProps,
137
+ ref,
138
+ matchTo,
139
+ breakpoint,
140
+ classNamePreset,
141
+ mergeClassesFunction
142
+ })), {
143
+ children: child
144
+ }));
145
+ }
146
+ return /*#__PURE__*/ _jsx("div", _object_spread_props(_object_spread({}, buildProps({
147
+ matchTo,
148
+ breakpoint,
149
+ classNamePreset,
150
+ mergeClassesFunction
151
+ })), {
152
+ children: child
153
+ }));
154
+ };
155
+ export const BreakpointChild = /*#__PURE__*/ forwardRef(BreakpointChildRenderFunction);
@@ -0,0 +1 @@
1
+ export * from '../../components/Breakpoint/Breakpoint';
@@ -0,0 +1 @@
1
+ export * from "./Breakpoint";
@@ -0,0 +1,16 @@
1
+ import { type Context, type FC, type ReactNode } from 'react';
2
+ import { type BreakpointData } from '../../types/breakpoints';
3
+ type BreakpointContext = {
4
+ isMatches: boolean | null;
5
+ };
6
+ export type ContextualizedBreakpointData = {
7
+ data: BreakpointData;
8
+ context: Context<BreakpointContext>;
9
+ };
10
+ export declare const DEFAULT_BREAKPOINT_CONTEXT: BreakpointContext;
11
+ type BreakpointProviderProps = {
12
+ breakpoint: ContextualizedBreakpointData;
13
+ children: ReactNode;
14
+ };
15
+ export declare const BreakpointProvider: FC<BreakpointProviderProps>;
16
+ export {};
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { getMinScreenWidth } from "../../utils/screen-width";
4
+ export const DEFAULT_BREAKPOINT_CONTEXT = {
5
+ isMatches: null
6
+ };
7
+ export const BreakpointProvider = (props)=>{
8
+ const { breakpoint, children } = props;
9
+ const { Provider } = breakpoint.context;
10
+ const [isMatches, setIsMatches] = useState(DEFAULT_BREAKPOINT_CONTEXT.isMatches);
11
+ const minScreenWidth = getMinScreenWidth(breakpoint.data);
12
+ useEffect(()=>{
13
+ const media = window.matchMedia(`(min-width: ${minScreenWidth})`);
14
+ setIsMatches(media.matches);
15
+ const handleMediaChange = (event)=>{
16
+ setIsMatches(event.matches);
17
+ };
18
+ media.addEventListener('change', handleMediaChange);
19
+ return ()=>{
20
+ media.removeEventListener('change', handleMediaChange);
21
+ };
22
+ }, [
23
+ minScreenWidth
24
+ ]);
25
+ const value = useMemo(()=>({
26
+ isMatches
27
+ }), [
28
+ isMatches
29
+ ]);
30
+ return /*#__PURE__*/ _jsx(Provider, {
31
+ value: value,
32
+ children: children
33
+ });
34
+ };
@@ -0,0 +1,20 @@
1
+ import { type FC, type ReactNode } from 'react';
2
+ import { type Breakpoints, type MatchTo, type ScreenSize } from '../../types/breakpoints';
3
+ import { type ClassNamePreset, type MergeClassesFunction } from '../../types/styles';
4
+ import { type ContextualizedBreakpointData } from '../../components/BreakpointsProvider/BreakpointProvider';
5
+ type ContextualizedBreakpoints = Record<ScreenSize, ContextualizedBreakpointData>;
6
+ type BreakpointsContext = {
7
+ breakpoints: ContextualizedBreakpoints;
8
+ classNamePreset?: ClassNamePreset;
9
+ mergeClassesFunction: MergeClassesFunction;
10
+ };
11
+ type BreakpointsProviderProps = {
12
+ breakpoints: Breakpoints;
13
+ classNamePreset?: ClassNamePreset;
14
+ children: ReactNode;
15
+ mergeClassesFunction?: MergeClassesFunction;
16
+ };
17
+ export declare const useBreakpointsContext: () => BreakpointsContext;
18
+ export declare const useMatchBreakpoint: (size: ScreenSize, matchTo?: MatchTo, defaultValue?: boolean) => boolean;
19
+ export declare const MatchBreakpointProvider: FC<BreakpointsProviderProps>;
20
+ export {};
@@ -0,0 +1,95 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _object_spread(target) {
15
+ for(var i = 1; i < arguments.length; i++){
16
+ var source = arguments[i] != null ? arguments[i] : {};
17
+ var ownKeys = Object.keys(source);
18
+ if (typeof Object.getOwnPropertySymbols === "function") {
19
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
21
+ }));
22
+ }
23
+ ownKeys.forEach(function(key) {
24
+ _define_property(target, key, source[key]);
25
+ });
26
+ }
27
+ return target;
28
+ }
29
+ function ownKeys(object, enumerableOnly) {
30
+ var keys = Object.keys(object);
31
+ if (Object.getOwnPropertySymbols) {
32
+ var symbols = Object.getOwnPropertySymbols(object);
33
+ if (enumerableOnly) {
34
+ symbols = symbols.filter(function(sym) {
35
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
36
+ });
37
+ }
38
+ keys.push.apply(keys, symbols);
39
+ }
40
+ return keys;
41
+ }
42
+ function _object_spread_props(target, source) {
43
+ source = source != null ? source : {};
44
+ if (Object.getOwnPropertyDescriptors) {
45
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
46
+ } else {
47
+ ownKeys(Object(source)).forEach(function(key) {
48
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
49
+ });
50
+ }
51
+ return target;
52
+ }
53
+ import { jsx as _jsx } from "react/jsx-runtime";
54
+ import { createContext, useContext, useMemo } from "react";
55
+ import { BreakpointProvider, DEFAULT_BREAKPOINT_CONTEXT } from "./BreakpointProvider";
56
+ const DEFAULT_BREAKPOINTS_CONTEXT = {
57
+ breakpoints: {},
58
+ mergeClassesFunction: (...classes)=>classes.filter(Boolean).join(' ')
59
+ };
60
+ const BreakpointsContextInstance = /*#__PURE__*/ createContext(DEFAULT_BREAKPOINTS_CONTEXT);
61
+ export const useBreakpointsContext = ()=>useContext(BreakpointsContextInstance);
62
+ export const useMatchBreakpoint = (size, matchTo = 'min', defaultValue = false)=>{
63
+ const { breakpoints } = useBreakpointsContext();
64
+ const breakpoint = breakpoints[size];
65
+ if (!breakpoint) {
66
+ throw new Error(`Breakpoint with screen size "${size}" is not defined`);
67
+ }
68
+ const { isMatches } = useContext(breakpoint.context);
69
+ if (isMatches === null) return defaultValue;
70
+ return matchTo === 'min' ? isMatches : !isMatches;
71
+ };
72
+ export const MatchBreakpointProvider = (props)=>{
73
+ const { breakpoints, classNamePreset, children, mergeClassesFunction = DEFAULT_BREAKPOINTS_CONTEXT.mergeClassesFunction } = props;
74
+ const value = useMemo(()=>({
75
+ breakpoints: Object.keys(breakpoints).reduce((acc, key)=>_object_spread_props(_object_spread({}, acc), {
76
+ [key]: {
77
+ data: breakpoints[key],
78
+ context: /*#__PURE__*/ createContext(_object_spread({}, DEFAULT_BREAKPOINT_CONTEXT))
79
+ }
80
+ }), {}),
81
+ classNamePreset,
82
+ mergeClassesFunction
83
+ }), [
84
+ breakpoints,
85
+ classNamePreset,
86
+ mergeClassesFunction
87
+ ]);
88
+ return /*#__PURE__*/ _jsx(BreakpointsContextInstance.Provider, {
89
+ value: value,
90
+ children: Object.keys(value.breakpoints).reduce((acc, size)=>/*#__PURE__*/ _jsx(BreakpointProvider, {
91
+ breakpoint: value.breakpoints[size],
92
+ children: acc
93
+ }), children)
94
+ });
95
+ };
@@ -0,0 +1 @@
1
+ export * from '../../components/BreakpointsProvider/BreakpointsProvider';
@@ -0,0 +1 @@
1
+ export * from "./BreakpointsProvider";
@@ -0,0 +1,3 @@
1
+ import { type BreakpointData } from '../types/breakpoints';
2
+ export declare const getMaxScreenWidth: (breakpointData: BreakpointData) => string;
3
+ export declare const getMinScreenWidth: (breakpointData: BreakpointData) => string;
@@ -0,0 +1,12 @@
1
+ export const getMaxScreenWidth = (breakpointData)=>{
2
+ if (typeof breakpointData === 'string') return breakpointData;
3
+ if (typeof breakpointData === 'number') return `${breakpointData}px`;
4
+ if (typeof breakpointData.max === 'string') return breakpointData.max;
5
+ return `${breakpointData.max}px`;
6
+ };
7
+ export const getMinScreenWidth = (breakpointData)=>{
8
+ if (typeof breakpointData === 'string') return breakpointData;
9
+ if (typeof breakpointData === 'number') return `${breakpointData}px`;
10
+ if (typeof breakpointData.min === 'string') return breakpointData.min;
11
+ return `${breakpointData.min}px`;
12
+ };
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "match-breakpoint",
3
+ "version": "1.0.0-canary.0",
4
+ "description": "",
5
+ "license": "MIT",
6
+ "author": "Oleg Putseiko <oleg.putseiko@gmail.com> (https://github.com/oleg-putseiko)",
7
+ "keywords": [],
8
+ "homepage": "https://github.com/oleg-putseiko/match-breakpoint",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/oleg-putseiko/match-breakpoint"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/oleg-putseiko/match-breakpoint/issues"
15
+ },
16
+ "main": "dist/src/index.js",
17
+ "files": [
18
+ "dist/src/components/**/*",
19
+ "dist/src/utils/**/*",
20
+ "dist/src/index.js"
21
+ ],
22
+ "exports": "./dist/src/index.js",
23
+ "scripts": {
24
+ "build": "swc ./src/**/* -d dist && tsc --emitDeclarationOnly && tsc-alias",
25
+ "format": "prettier -c .",
26
+ "format:fix": "prettier -w .",
27
+ "lint": "eslint ./src/**/*.{js,ts,tsx,json}",
28
+ "lint:fix": "eslint ./src/**/*.{js,ts,tsx,json} --fix && yarn format:fix",
29
+ "lint:strict": "eslint ./src/**/*.{js,ts,tsx,json} --max-warnings=0 && yarn format",
30
+ "prepare": "husky install | chmod +x ./.husky/*",
31
+ "test": "jest",
32
+ "typecheck": "tsc --noEmit --incremental false"
33
+ },
34
+ "peerDependencies": {
35
+ "react": "^18.2.0",
36
+ "react-dom": "^18.2.0"
37
+ },
38
+ "devDependencies": {
39
+ "@commitlint/cli": "^18.2.0",
40
+ "@commitlint/config-conventional": "^18.1.0",
41
+ "@swc/cli": "^0.5.1",
42
+ "@swc/core": "^1.9.3",
43
+ "@types/react": "^18.0.0",
44
+ "@typescript-eslint/eslint-plugin": "^6.9.1",
45
+ "@typescript-eslint/parser": "^6.9.1",
46
+ "eslint": "^8.53.0",
47
+ "eslint-config-prettier": "^9.0.0",
48
+ "eslint-config-woofmeow": "^1.0.1",
49
+ "eslint-plugin-react": "^7.37.2",
50
+ "eslint-plugin-react-hooks": "^4.6.0",
51
+ "eslint-plugin-simple-import-sort": "^10.0.0",
52
+ "eslint-plugin-unused-imports": "^3.0.0",
53
+ "husky": "^8.0.3",
54
+ "lint-staged": "^15.0.2",
55
+ "prettier": "^3.0.3",
56
+ "tsc-alias": "^1.8.10",
57
+ "typescript": "~5.2.2"
58
+ },
59
+ "engines": {
60
+ "node": "18.18.0",
61
+ "yarn": "3.6.4"
62
+ },
63
+ "volta": {
64
+ "node": "18.18.0",
65
+ "yarn": "3.6.4"
66
+ },
67
+ "packageManager": "yarn@3.6.4"
68
+ }