opentui-responsive 0.3.1 → 0.5.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/README.md +47 -16
- package/dist/core/breakpoints.d.ts +10 -0
- package/dist/core/index.js +40 -8
- package/dist/react/index.d.ts +28 -3
- package/dist/react/index.js +59 -9
- package/dist/solid/index.d.ts +28 -3
- package/dist/solid/index.js +60 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,15 +58,18 @@ export function Layout(props: ParentProps) {
|
|
|
58
58
|
import { useBreakpoint } from "./layout.tsx";
|
|
59
59
|
|
|
60
60
|
export function Content() {
|
|
61
|
-
const
|
|
62
|
-
//
|
|
63
|
-
const isWideAndTall =
|
|
61
|
+
const [width, height, viewport] = useBreakpoint();
|
|
62
|
+
// Each accessor can be named for its local use.
|
|
63
|
+
const isWideAndTall = viewport(["wide", "tall"]);
|
|
64
|
+
const isMediumOrWider = width.atLeast("medium");
|
|
65
|
+
const hasMediumViewport = viewport.atLeast(["medium", "medium"]);
|
|
64
66
|
|
|
65
67
|
return (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<text>
|
|
69
|
-
|
|
68
|
+
<box flexDirection={width() === "wide" ? "row" : "column"}>
|
|
69
|
+
<text>{`${width()}/${height()}`}</text>
|
|
70
|
+
<text>
|
|
71
|
+
{isWideAndTall || (isMediumOrWider && hasMediumViewport) ? "Full layout" : "Compact layout"}
|
|
72
|
+
</text>
|
|
70
73
|
</box>
|
|
71
74
|
);
|
|
72
75
|
}
|
|
@@ -87,7 +90,20 @@ export function App() {
|
|
|
87
90
|
}
|
|
88
91
|
```
|
|
89
92
|
|
|
90
|
-
`useBreakpoint()` updates when the terminal crosses a configured threshold.
|
|
93
|
+
`useBreakpoint()` updates when the terminal crosses a configured threshold. It returns `[width, height, viewport]`, letting each caller choose local names while keeping breakpoint names scoped to their axis. Width and height accept one breakpoint name; viewport accepts a `[width, height]` pair. Viewport pair comparisons return true only when both axes satisfy the relation.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
const [width, height, viewport] = useBreakpoint();
|
|
97
|
+
|
|
98
|
+
width.below("medium");
|
|
99
|
+
viewport.atMost(["medium", "tall"]);
|
|
100
|
+
height.only("short");
|
|
101
|
+
viewport.atLeast(["medium", "medium"]);
|
|
102
|
+
width.above("medium");
|
|
103
|
+
|
|
104
|
+
const [sidebarWidth] = useBreakpoint();
|
|
105
|
+
const [, , layoutViewport] = useBreakpoint();
|
|
106
|
+
```
|
|
91
107
|
|
|
92
108
|
Run `bun run demo` from the package directory to try the Solid example. Resize the terminal to see its layout respond.
|
|
93
109
|
|
|
@@ -97,14 +113,16 @@ The React adapter has the same factory, provider, hook, and inferred breakpoint
|
|
|
97
113
|
|
|
98
114
|
### `opentui-responsive/solid` and `opentui-responsive/react`
|
|
99
115
|
|
|
100
|
-
| API
|
|
101
|
-
|
|
|
102
|
-
| `createResponsiveTui(scales)`
|
|
103
|
-
| `ResponsiveTUI`
|
|
104
|
-
| `useBreakpoint()`
|
|
105
|
-
| `ResponsiveBreakpointAccessor`
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
116
|
+
| API | Description |
|
|
117
|
+
| -------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
118
|
+
| `createResponsiveTui(scales)` | Validates the scales and creates a `ResponsiveTUI` provider and `useBreakpoint` hook. |
|
|
119
|
+
| `ResponsiveTUI` | Tracks terminal dimensions and provides the current breakpoint accessor. |
|
|
120
|
+
| `useBreakpoint()` | Reads `[width, height, viewport]` from the nearest generated provider. |
|
|
121
|
+
| `ResponsiveBreakpointAccessor` | The readonly `[width, height, viewport]` accessor tuple. |
|
|
122
|
+
| `ResponsiveBreakpointAxisAccessor` | Reads or compares one axis using that axis's breakpoint names. |
|
|
123
|
+
| `ResponsiveBreakpointViewportAccessor` | Reads or compares the complete viewport using `[width, height]` pairs. |
|
|
124
|
+
| `ResponsiveTuiConfigurationError` | Thrown when the supplied breakpoint scales are invalid. |
|
|
125
|
+
| `ResponsiveTuiProviderError` | Thrown when the generated hook is called outside its provider. |
|
|
108
126
|
|
|
109
127
|
### `opentui-responsive/core`
|
|
110
128
|
|
|
@@ -117,6 +135,7 @@ For implementing another framework adapter, not application setup:
|
|
|
117
135
|
| `BreakpointScales` | The width and height scale configuration. |
|
|
118
136
|
| `BreakpointMatch` | The matched breakpoint name for each axis. |
|
|
119
137
|
| `BreakpointPair` | An exact breakpoint pair in width-then-height order. |
|
|
138
|
+
| `BreakpointRelationMatcher` | Tests one axis or an AND-combined pair against a named relation. |
|
|
120
139
|
| `ResponsiveTuiConfigurationError` | Thrown when supplied breakpoint scales are invalid. |
|
|
121
140
|
|
|
122
141
|
## Breakpoint Behavior
|
|
@@ -125,6 +144,18 @@ Width and height use independent sets of inclusive minimum thresholds measured i
|
|
|
125
144
|
|
|
126
145
|
Declaration order does not affect matching. Each axis selects its highest satisfied threshold.
|
|
127
146
|
|
|
147
|
+
Relation methods compare complete tiers rather than their raw threshold values. Given `width: { narrow: 0, medium: 60, wide: 100 }`, `width.atMost("medium")` matches widths from `0` through `99`.
|
|
148
|
+
|
|
149
|
+
| Method | Tier relation |
|
|
150
|
+
| --------- | ------------- |
|
|
151
|
+
| `below` | `<` |
|
|
152
|
+
| `atMost` | `<=` |
|
|
153
|
+
| `only` | `===` |
|
|
154
|
+
| `atLeast` | `>=` |
|
|
155
|
+
| `above` | `>` |
|
|
156
|
+
|
|
157
|
+
Width and height relation methods each accept a name from their own scale. Viewport relation methods accept `[width, height]` pairs and use AND semantics. Calling `viewport.only(["wide", "tall"])` is equivalent to calling `viewport(["wide", "tall"])`. The `/core` relation matchers retain `(viewport, axis, name)` and `(viewport, [width, height])` forms for adapter implementations.
|
|
158
|
+
|
|
128
159
|
Use breakpoints for discrete layout modes. Keep continuous measurements such as progress-bar width and available list height on OpenTUI's `useTerminalDimensions()`.
|
|
129
160
|
|
|
130
161
|
## Runtime Support
|
|
@@ -24,10 +24,20 @@ export type BreakpointPair<Scales extends BreakpointScales = BreakpointScales> =
|
|
|
24
24
|
width: BreakpointMatch<Scales>["width"],
|
|
25
25
|
height: BreakpointMatch<Scales>["height"]
|
|
26
26
|
];
|
|
27
|
+
/** Tests one axis or both axes against a named breakpoint relation. */
|
|
28
|
+
export type BreakpointRelationMatcher<Scales extends BreakpointScales = BreakpointScales> = {
|
|
29
|
+
<Axis extends BreakpointAxis>(viewport: BreakpointViewport, axis: Axis, name: BreakpointMatch<Scales>[Axis]): boolean;
|
|
30
|
+
(viewport: BreakpointViewport, pair: BreakpointPair<Scales>): boolean;
|
|
31
|
+
};
|
|
27
32
|
/** A validated breakpoint matcher for framework adapter implementations. */
|
|
28
33
|
export type BreakpointDefinition<Scales extends BreakpointScales = BreakpointScales> = {
|
|
29
34
|
readonly match: (viewport: BreakpointViewport) => BreakpointMatch<Scales>;
|
|
30
35
|
readonly matches: (viewport: BreakpointViewport, pair: BreakpointPair<Scales>) => boolean;
|
|
36
|
+
readonly below: BreakpointRelationMatcher<Scales>;
|
|
37
|
+
readonly atMost: BreakpointRelationMatcher<Scales>;
|
|
38
|
+
readonly only: BreakpointRelationMatcher<Scales>;
|
|
39
|
+
readonly atLeast: BreakpointRelationMatcher<Scales>;
|
|
40
|
+
readonly above: BreakpointRelationMatcher<Scales>;
|
|
31
41
|
};
|
|
32
42
|
/** Thrown when breakpoint scales cannot produce a valid definition. */
|
|
33
43
|
export declare class ResponsiveTuiConfigurationError extends Error {
|
package/dist/core/index.js
CHANGED
|
@@ -12,12 +12,19 @@ var createBreakpointDefinition = (scales) => {
|
|
|
12
12
|
width: matchScale(viewport.width, entries.width),
|
|
13
13
|
height: matchScale(viewport.height, entries.height)
|
|
14
14
|
});
|
|
15
|
+
const below = createRelationMatcher(entries, "below");
|
|
16
|
+
const atMost = createRelationMatcher(entries, "atMost");
|
|
17
|
+
const only = createRelationMatcher(entries, "only");
|
|
18
|
+
const atLeast = createRelationMatcher(entries, "atLeast");
|
|
19
|
+
const above = createRelationMatcher(entries, "above");
|
|
15
20
|
return {
|
|
16
21
|
match,
|
|
17
|
-
matches: (viewport, pair) =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
matches: (viewport, pair) => only(viewport, pair),
|
|
23
|
+
below,
|
|
24
|
+
atMost,
|
|
25
|
+
only,
|
|
26
|
+
atLeast,
|
|
27
|
+
above
|
|
21
28
|
};
|
|
22
29
|
};
|
|
23
30
|
var breakpointAxes = ["width", "height"];
|
|
@@ -63,14 +70,39 @@ var validateScale = (axis, scale) => {
|
|
|
63
70
|
return validated.sort((left, right) => left[1] - right[1]);
|
|
64
71
|
};
|
|
65
72
|
var matchScale = (value, entries) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
return entries[matchScaleIndex(value, entries)][0];
|
|
74
|
+
};
|
|
75
|
+
var matchScaleIndex = (value, entries) => {
|
|
76
|
+
let match = 0;
|
|
77
|
+
for (let index = 1;index < entries.length; index += 1) {
|
|
78
|
+
if (value < entries[index][1])
|
|
69
79
|
break;
|
|
70
|
-
match =
|
|
80
|
+
match = index;
|
|
71
81
|
}
|
|
72
82
|
return match;
|
|
73
83
|
};
|
|
84
|
+
var createRelationMatcher = (entries, relation) => (viewport, axisOrPair, name) => {
|
|
85
|
+
if (Array.isArray(axisOrPair)) {
|
|
86
|
+
return matchesRelation(viewport.width, entries.width, axisOrPair[0], relation) && matchesRelation(viewport.height, entries.height, axisOrPair[1], relation);
|
|
87
|
+
}
|
|
88
|
+
const axis = axisOrPair;
|
|
89
|
+
return matchesRelation(viewport[axis], entries[axis], name, relation);
|
|
90
|
+
};
|
|
91
|
+
var matchesRelation = (value, entries, name, relation) => {
|
|
92
|
+
const target = entries.findIndex(([entryName]) => entryName === name);
|
|
93
|
+
if (target === -1)
|
|
94
|
+
return false;
|
|
95
|
+
const current = matchScaleIndex(value, entries);
|
|
96
|
+
if (relation === "below")
|
|
97
|
+
return current < target;
|
|
98
|
+
if (relation === "atMost")
|
|
99
|
+
return current <= target;
|
|
100
|
+
if (relation === "only")
|
|
101
|
+
return current === target;
|
|
102
|
+
if (relation === "atLeast")
|
|
103
|
+
return current >= target;
|
|
104
|
+
return current > target;
|
|
105
|
+
};
|
|
74
106
|
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
75
107
|
var isThreshold = (value) => typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0;
|
|
76
108
|
export {
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import { type PropsWithChildren } from "react";
|
|
2
|
-
import { createBreakpointDefinition, type BreakpointMatch, type BreakpointPair, type BreakpointScales } from "../core/index.js";
|
|
2
|
+
import { createBreakpointDefinition, type BreakpointAxis, type BreakpointMatch, type BreakpointPair, type BreakpointScales } from "../core/index.js";
|
|
3
3
|
export { ResponsiveTuiConfigurationError } from "../core/index.js";
|
|
4
|
-
/** A React
|
|
5
|
-
export type
|
|
4
|
+
/** A React breakpoint relation test for one axis. */
|
|
5
|
+
export type ResponsiveBreakpointAxisRelationMatcher<Scales extends BreakpointScales = BreakpointScales, Axis extends BreakpointAxis = BreakpointAxis> = {
|
|
6
|
+
(name: BreakpointMatch<Scales>[Axis]): boolean;
|
|
7
|
+
};
|
|
8
|
+
/** A React accessor that reads or tests one breakpoint axis. */
|
|
9
|
+
export type ResponsiveBreakpointAxisAccessor<Scales extends BreakpointScales = BreakpointScales, Axis extends BreakpointAxis = BreakpointAxis> = {
|
|
10
|
+
(): BreakpointMatch<Scales>[Axis];
|
|
11
|
+
(name: BreakpointMatch<Scales>[Axis]): boolean;
|
|
12
|
+
readonly below: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
13
|
+
readonly atMost: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
14
|
+
readonly only: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
15
|
+
readonly atLeast: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
16
|
+
readonly above: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
17
|
+
};
|
|
18
|
+
/** A React accessor that reads or tests the complete breakpoint viewport. */
|
|
19
|
+
export type ResponsiveBreakpointViewportAccessor<Scales extends BreakpointScales = BreakpointScales> = {
|
|
6
20
|
(): BreakpointMatch<Scales>;
|
|
7
21
|
(pair: BreakpointPair<Scales>): boolean;
|
|
22
|
+
readonly below: (pair: BreakpointPair<Scales>) => boolean;
|
|
23
|
+
readonly atMost: (pair: BreakpointPair<Scales>) => boolean;
|
|
24
|
+
readonly only: (pair: BreakpointPair<Scales>) => boolean;
|
|
25
|
+
readonly atLeast: (pair: BreakpointPair<Scales>) => boolean;
|
|
26
|
+
readonly above: (pair: BreakpointPair<Scales>) => boolean;
|
|
8
27
|
};
|
|
28
|
+
/** React breakpoint accessors in width, height, then viewport order. */
|
|
29
|
+
export type ResponsiveBreakpointAccessor<Scales extends BreakpointScales = BreakpointScales> = readonly [
|
|
30
|
+
width: ResponsiveBreakpointAxisAccessor<Scales, "width">,
|
|
31
|
+
height: ResponsiveBreakpointAxisAccessor<Scales, "height">,
|
|
32
|
+
viewport: ResponsiveBreakpointViewportAccessor<Scales>
|
|
33
|
+
];
|
|
9
34
|
/** Thrown when a responsive hook is used outside its generated React provider. */
|
|
10
35
|
export declare class ResponsiveTuiProviderError extends Error {
|
|
11
36
|
readonly _tag = "ResponsiveTuiProviderError";
|
package/dist/react/index.js
CHANGED
|
@@ -16,12 +16,19 @@ var createBreakpointDefinition = (scales) => {
|
|
|
16
16
|
width: matchScale(viewport.width, entries.width),
|
|
17
17
|
height: matchScale(viewport.height, entries.height)
|
|
18
18
|
});
|
|
19
|
+
const below = createRelationMatcher(entries, "below");
|
|
20
|
+
const atMost = createRelationMatcher(entries, "atMost");
|
|
21
|
+
const only = createRelationMatcher(entries, "only");
|
|
22
|
+
const atLeast = createRelationMatcher(entries, "atLeast");
|
|
23
|
+
const above = createRelationMatcher(entries, "above");
|
|
19
24
|
return {
|
|
20
25
|
match,
|
|
21
|
-
matches: (viewport, pair) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
matches: (viewport, pair) => only(viewport, pair),
|
|
27
|
+
below,
|
|
28
|
+
atMost,
|
|
29
|
+
only,
|
|
30
|
+
atLeast,
|
|
31
|
+
above
|
|
25
32
|
};
|
|
26
33
|
};
|
|
27
34
|
var breakpointAxes = ["width", "height"];
|
|
@@ -67,14 +74,39 @@ var validateScale = (axis, scale) => {
|
|
|
67
74
|
return validated.sort((left, right) => left[1] - right[1]);
|
|
68
75
|
};
|
|
69
76
|
var matchScale = (value, entries) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
return entries[matchScaleIndex(value, entries)][0];
|
|
78
|
+
};
|
|
79
|
+
var matchScaleIndex = (value, entries) => {
|
|
80
|
+
let match = 0;
|
|
81
|
+
for (let index = 1;index < entries.length; index += 1) {
|
|
82
|
+
if (value < entries[index][1])
|
|
73
83
|
break;
|
|
74
|
-
match =
|
|
84
|
+
match = index;
|
|
75
85
|
}
|
|
76
86
|
return match;
|
|
77
87
|
};
|
|
88
|
+
var createRelationMatcher = (entries, relation) => (viewport, axisOrPair, name) => {
|
|
89
|
+
if (Array.isArray(axisOrPair)) {
|
|
90
|
+
return matchesRelation(viewport.width, entries.width, axisOrPair[0], relation) && matchesRelation(viewport.height, entries.height, axisOrPair[1], relation);
|
|
91
|
+
}
|
|
92
|
+
const axis = axisOrPair;
|
|
93
|
+
return matchesRelation(viewport[axis], entries[axis], name, relation);
|
|
94
|
+
};
|
|
95
|
+
var matchesRelation = (value, entries, name, relation) => {
|
|
96
|
+
const target = entries.findIndex(([entryName]) => entryName === name);
|
|
97
|
+
if (target === -1)
|
|
98
|
+
return false;
|
|
99
|
+
const current = matchScaleIndex(value, entries);
|
|
100
|
+
if (relation === "below")
|
|
101
|
+
return current < target;
|
|
102
|
+
if (relation === "atMost")
|
|
103
|
+
return current <= target;
|
|
104
|
+
if (relation === "only")
|
|
105
|
+
return current === target;
|
|
106
|
+
if (relation === "atLeast")
|
|
107
|
+
return current >= target;
|
|
108
|
+
return current > target;
|
|
109
|
+
};
|
|
78
110
|
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
79
111
|
var isThreshold = (value) => typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0;
|
|
80
112
|
|
|
@@ -91,7 +123,16 @@ var createResponsiveTui = (scales) => {
|
|
|
91
123
|
const ResponsiveTuiContext = createContext(undefined);
|
|
92
124
|
const ResponsiveTUI = (props) => {
|
|
93
125
|
const dimensions = useTerminalDimensions();
|
|
94
|
-
const
|
|
126
|
+
const width = bindAxisAccessor("width", () => dimensions, breakpoints);
|
|
127
|
+
const height = bindAxisAccessor("height", () => dimensions, breakpoints);
|
|
128
|
+
const viewport = Object.assign((pair) => pair ? breakpoints.matches(dimensions, pair) : breakpoints.match(dimensions), {
|
|
129
|
+
below: bindViewportRelation(() => dimensions, breakpoints.below),
|
|
130
|
+
atMost: bindViewportRelation(() => dimensions, breakpoints.atMost),
|
|
131
|
+
only: bindViewportRelation(() => dimensions, breakpoints.only),
|
|
132
|
+
atLeast: bindViewportRelation(() => dimensions, breakpoints.atLeast),
|
|
133
|
+
above: bindViewportRelation(() => dimensions, breakpoints.above)
|
|
134
|
+
});
|
|
135
|
+
const breakpoint = [width, height, viewport];
|
|
95
136
|
return createElement(ResponsiveTuiContext.Provider, { value: breakpoint }, props.children);
|
|
96
137
|
};
|
|
97
138
|
const useBreakpoint = () => {
|
|
@@ -103,6 +144,15 @@ var createResponsiveTui = (scales) => {
|
|
|
103
144
|
};
|
|
104
145
|
return { ResponsiveTUI, useBreakpoint };
|
|
105
146
|
};
|
|
147
|
+
var bindAxisAccessor = (axis, viewport, breakpoints) => Object.assign((name) => name === undefined ? breakpoints.match(viewport())[axis] : breakpoints.only(viewport(), axis, name), {
|
|
148
|
+
below: bindAxisRelation(axis, viewport, breakpoints.below),
|
|
149
|
+
atMost: bindAxisRelation(axis, viewport, breakpoints.atMost),
|
|
150
|
+
only: bindAxisRelation(axis, viewport, breakpoints.only),
|
|
151
|
+
atLeast: bindAxisRelation(axis, viewport, breakpoints.atLeast),
|
|
152
|
+
above: bindAxisRelation(axis, viewport, breakpoints.above)
|
|
153
|
+
});
|
|
154
|
+
var bindAxisRelation = (axis, viewport, relation) => (name) => relation(viewport(), axis, name);
|
|
155
|
+
var bindViewportRelation = (viewport, relation) => (pair) => relation(viewport(), pair);
|
|
106
156
|
export {
|
|
107
157
|
ResponsiveTuiConfigurationError,
|
|
108
158
|
ResponsiveTuiProviderError,
|
package/dist/solid/index.d.ts
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import { type ParentProps } from "solid-js";
|
|
2
|
-
import { createBreakpointDefinition, type BreakpointMatch, type BreakpointPair, type BreakpointScales } from "../core/index.js";
|
|
2
|
+
import { createBreakpointDefinition, type BreakpointAxis, type BreakpointMatch, type BreakpointPair, type BreakpointScales } from "../core/index.js";
|
|
3
3
|
export { ResponsiveTuiConfigurationError } from "../core/index.js";
|
|
4
|
-
/** A reactive Solid
|
|
5
|
-
export type
|
|
4
|
+
/** A reactive Solid breakpoint relation test for one axis. */
|
|
5
|
+
export type ResponsiveBreakpointAxisRelationMatcher<Scales extends BreakpointScales = BreakpointScales, Axis extends BreakpointAxis = BreakpointAxis> = {
|
|
6
|
+
(name: BreakpointMatch<Scales>[Axis]): boolean;
|
|
7
|
+
};
|
|
8
|
+
/** A reactive Solid accessor that reads or tests one breakpoint axis. */
|
|
9
|
+
export type ResponsiveBreakpointAxisAccessor<Scales extends BreakpointScales = BreakpointScales, Axis extends BreakpointAxis = BreakpointAxis> = {
|
|
10
|
+
(): BreakpointMatch<Scales>[Axis];
|
|
11
|
+
(name: BreakpointMatch<Scales>[Axis]): boolean;
|
|
12
|
+
readonly below: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
13
|
+
readonly atMost: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
14
|
+
readonly only: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
15
|
+
readonly atLeast: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
16
|
+
readonly above: ResponsiveBreakpointAxisRelationMatcher<Scales, Axis>;
|
|
17
|
+
};
|
|
18
|
+
/** A reactive Solid accessor that reads or tests the complete breakpoint viewport. */
|
|
19
|
+
export type ResponsiveBreakpointViewportAccessor<Scales extends BreakpointScales = BreakpointScales> = {
|
|
6
20
|
(): BreakpointMatch<Scales>;
|
|
7
21
|
(pair: BreakpointPair<Scales>): boolean;
|
|
22
|
+
readonly below: (pair: BreakpointPair<Scales>) => boolean;
|
|
23
|
+
readonly atMost: (pair: BreakpointPair<Scales>) => boolean;
|
|
24
|
+
readonly only: (pair: BreakpointPair<Scales>) => boolean;
|
|
25
|
+
readonly atLeast: (pair: BreakpointPair<Scales>) => boolean;
|
|
26
|
+
readonly above: (pair: BreakpointPair<Scales>) => boolean;
|
|
8
27
|
};
|
|
28
|
+
/** Reactive Solid breakpoint accessors in width, height, then viewport order. */
|
|
29
|
+
export type ResponsiveBreakpointAccessor<Scales extends BreakpointScales = BreakpointScales> = readonly [
|
|
30
|
+
width: ResponsiveBreakpointAxisAccessor<Scales, "width">,
|
|
31
|
+
height: ResponsiveBreakpointAxisAccessor<Scales, "height">,
|
|
32
|
+
viewport: ResponsiveBreakpointViewportAccessor<Scales>
|
|
33
|
+
];
|
|
9
34
|
/** Thrown when a responsive hook is used outside its generated Solid provider. */
|
|
10
35
|
export declare class ResponsiveTuiProviderError extends Error {
|
|
11
36
|
readonly _tag = "ResponsiveTuiProviderError";
|
package/dist/solid/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/solid/index.ts
|
|
2
2
|
import { useTerminalDimensions } from "@opentui/solid";
|
|
3
|
-
import { createContext,
|
|
3
|
+
import { createContext, useContext } from "solid-js";
|
|
4
4
|
|
|
5
5
|
// src/core/breakpoints.ts
|
|
6
6
|
class ResponsiveTuiConfigurationError extends Error {
|
|
@@ -16,12 +16,19 @@ var createBreakpointDefinition = (scales) => {
|
|
|
16
16
|
width: matchScale(viewport.width, entries.width),
|
|
17
17
|
height: matchScale(viewport.height, entries.height)
|
|
18
18
|
});
|
|
19
|
+
const below = createRelationMatcher(entries, "below");
|
|
20
|
+
const atMost = createRelationMatcher(entries, "atMost");
|
|
21
|
+
const only = createRelationMatcher(entries, "only");
|
|
22
|
+
const atLeast = createRelationMatcher(entries, "atLeast");
|
|
23
|
+
const above = createRelationMatcher(entries, "above");
|
|
19
24
|
return {
|
|
20
25
|
match,
|
|
21
|
-
matches: (viewport, pair) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
matches: (viewport, pair) => only(viewport, pair),
|
|
27
|
+
below,
|
|
28
|
+
atMost,
|
|
29
|
+
only,
|
|
30
|
+
atLeast,
|
|
31
|
+
above
|
|
25
32
|
};
|
|
26
33
|
};
|
|
27
34
|
var breakpointAxes = ["width", "height"];
|
|
@@ -67,14 +74,39 @@ var validateScale = (axis, scale) => {
|
|
|
67
74
|
return validated.sort((left, right) => left[1] - right[1]);
|
|
68
75
|
};
|
|
69
76
|
var matchScale = (value, entries) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
return entries[matchScaleIndex(value, entries)][0];
|
|
78
|
+
};
|
|
79
|
+
var matchScaleIndex = (value, entries) => {
|
|
80
|
+
let match = 0;
|
|
81
|
+
for (let index = 1;index < entries.length; index += 1) {
|
|
82
|
+
if (value < entries[index][1])
|
|
73
83
|
break;
|
|
74
|
-
match =
|
|
84
|
+
match = index;
|
|
75
85
|
}
|
|
76
86
|
return match;
|
|
77
87
|
};
|
|
88
|
+
var createRelationMatcher = (entries, relation) => (viewport, axisOrPair, name) => {
|
|
89
|
+
if (Array.isArray(axisOrPair)) {
|
|
90
|
+
return matchesRelation(viewport.width, entries.width, axisOrPair[0], relation) && matchesRelation(viewport.height, entries.height, axisOrPair[1], relation);
|
|
91
|
+
}
|
|
92
|
+
const axis = axisOrPair;
|
|
93
|
+
return matchesRelation(viewport[axis], entries[axis], name, relation);
|
|
94
|
+
};
|
|
95
|
+
var matchesRelation = (value, entries, name, relation) => {
|
|
96
|
+
const target = entries.findIndex(([entryName]) => entryName === name);
|
|
97
|
+
if (target === -1)
|
|
98
|
+
return false;
|
|
99
|
+
const current = matchScaleIndex(value, entries);
|
|
100
|
+
if (relation === "below")
|
|
101
|
+
return current < target;
|
|
102
|
+
if (relation === "atMost")
|
|
103
|
+
return current <= target;
|
|
104
|
+
if (relation === "only")
|
|
105
|
+
return current === target;
|
|
106
|
+
if (relation === "atLeast")
|
|
107
|
+
return current >= target;
|
|
108
|
+
return current > target;
|
|
109
|
+
};
|
|
78
110
|
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
79
111
|
var isThreshold = (value) => typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0;
|
|
80
112
|
|
|
@@ -91,8 +123,16 @@ var createResponsiveTui = (scales) => {
|
|
|
91
123
|
const ResponsiveTuiContext = createContext();
|
|
92
124
|
const ResponsiveTUI = (props) => {
|
|
93
125
|
const dimensions = useTerminalDimensions();
|
|
94
|
-
const
|
|
95
|
-
const
|
|
126
|
+
const width = bindAxisAccessor("width", dimensions, breakpoints);
|
|
127
|
+
const height = bindAxisAccessor("height", dimensions, breakpoints);
|
|
128
|
+
const viewport = Object.assign((pair) => pair ? breakpoints.matches(dimensions(), pair) : breakpoints.match(dimensions()), {
|
|
129
|
+
below: bindViewportRelation(dimensions, breakpoints.below),
|
|
130
|
+
atMost: bindViewportRelation(dimensions, breakpoints.atMost),
|
|
131
|
+
only: bindViewportRelation(dimensions, breakpoints.only),
|
|
132
|
+
atLeast: bindViewportRelation(dimensions, breakpoints.atLeast),
|
|
133
|
+
above: bindViewportRelation(dimensions, breakpoints.above)
|
|
134
|
+
});
|
|
135
|
+
const breakpoint = [width, height, viewport];
|
|
96
136
|
return ResponsiveTuiContext.Provider({
|
|
97
137
|
get children() {
|
|
98
138
|
return props.children;
|
|
@@ -109,6 +149,15 @@ var createResponsiveTui = (scales) => {
|
|
|
109
149
|
};
|
|
110
150
|
return { ResponsiveTUI, useBreakpoint };
|
|
111
151
|
};
|
|
152
|
+
var bindAxisAccessor = (axis, viewport, breakpoints) => Object.assign((name) => name === undefined ? breakpoints.match(viewport())[axis] : breakpoints.only(viewport(), axis, name), {
|
|
153
|
+
below: bindAxisRelation(axis, viewport, breakpoints.below),
|
|
154
|
+
atMost: bindAxisRelation(axis, viewport, breakpoints.atMost),
|
|
155
|
+
only: bindAxisRelation(axis, viewport, breakpoints.only),
|
|
156
|
+
atLeast: bindAxisRelation(axis, viewport, breakpoints.atLeast),
|
|
157
|
+
above: bindAxisRelation(axis, viewport, breakpoints.above)
|
|
158
|
+
});
|
|
159
|
+
var bindAxisRelation = (axis, viewport, relation) => (name) => relation(viewport(), axis, name);
|
|
160
|
+
var bindViewportRelation = (viewport, relation) => (pair) => relation(viewport(), pair);
|
|
112
161
|
export {
|
|
113
162
|
ResponsiveTuiConfigurationError,
|
|
114
163
|
ResponsiveTuiProviderError,
|