react-solidlike 2.0.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/dist/index.js ADDED
@@ -0,0 +1,209 @@
1
+ // src/Await.tsx
2
+ import { useEffect, useState } from "react";
3
+ function Await({ promise, loading = null, error = null, children }) {
4
+ const [state, setState] = useState(() => {
5
+ if (!(promise instanceof Promise)) {
6
+ return { status: "fulfilled", value: promise };
7
+ }
8
+ return { status: "pending" };
9
+ });
10
+ useEffect(() => {
11
+ if (!(promise instanceof Promise)) {
12
+ setState({ status: "fulfilled", value: promise });
13
+ return;
14
+ }
15
+ setState({ status: "pending" });
16
+ let cancelled = false;
17
+ promise.then((value) => {
18
+ if (!cancelled) {
19
+ setState({ status: "fulfilled", value });
20
+ }
21
+ }).catch((err) => {
22
+ if (!cancelled) {
23
+ setState({ status: "rejected", error: err });
24
+ }
25
+ });
26
+ return () => {
27
+ cancelled = true;
28
+ };
29
+ }, [promise]);
30
+ if (state.status === "pending") {
31
+ return loading;
32
+ }
33
+ if (state.status === "rejected") {
34
+ if (typeof error === "function") {
35
+ return error(state.error);
36
+ }
37
+ return error;
38
+ }
39
+ if (typeof children === "function") {
40
+ return children(state.value);
41
+ }
42
+ return children;
43
+ }
44
+ // src/Dynamic.tsx
45
+ import { createElement } from "react";
46
+ function Dynamic({ component, fallback = null, ...props }) {
47
+ if (!component) {
48
+ return fallback;
49
+ }
50
+ return createElement(component, props);
51
+ }
52
+ // src/ErrorBoundary.tsx
53
+ import { Component } from "react";
54
+
55
+ class ErrorBoundary extends Component {
56
+ constructor(props) {
57
+ super(props);
58
+ this.state = { error: null };
59
+ }
60
+ static getDerivedStateFromError(error) {
61
+ return { error };
62
+ }
63
+ componentDidCatch(error, errorInfo) {
64
+ this.props.onError?.(error, errorInfo);
65
+ }
66
+ componentDidUpdate(prevProps) {
67
+ if (this.state.error && prevProps.resetKey !== this.props.resetKey) {
68
+ this.reset();
69
+ }
70
+ }
71
+ reset = () => {
72
+ this.setState({ error: null });
73
+ };
74
+ render() {
75
+ const { error } = this.state;
76
+ const { fallback, children } = this.props;
77
+ if (error) {
78
+ if (typeof fallback === "function") {
79
+ return fallback(error, this.reset);
80
+ }
81
+ return fallback;
82
+ }
83
+ return children;
84
+ }
85
+ }
86
+ // src/For.tsx
87
+ import { Fragment } from "react";
88
+ import { jsx } from "react/jsx-runtime";
89
+ function For({ each, children, keyExtractor, fallback = null }) {
90
+ if (!each || each.length === 0) {
91
+ return fallback;
92
+ }
93
+ return each.map((item, index) => {
94
+ const key = keyExtractor ? keyExtractor(item, index) : index;
95
+ return /* @__PURE__ */ jsx(Fragment, {
96
+ children: children(item, index)
97
+ }, key);
98
+ });
99
+ }
100
+ // src/QueryBoundary.tsx
101
+ function defaultIsEmpty(data) {
102
+ if (data == null)
103
+ return true;
104
+ if (Array.isArray(data))
105
+ return data.length === 0;
106
+ if (typeof data === "object")
107
+ return Object.keys(data).length === 0;
108
+ return false;
109
+ }
110
+ function QueryBoundary({
111
+ query,
112
+ loading = null,
113
+ error = null,
114
+ empty = null,
115
+ children,
116
+ isEmptyFn = defaultIsEmpty
117
+ }) {
118
+ if (!query) {
119
+ return null;
120
+ }
121
+ const { data, isPending, isError, isEmpty: queryIsEmpty } = query;
122
+ if (isPending) {
123
+ return loading;
124
+ }
125
+ if (isError && isEmptyFn(data)) {
126
+ return error;
127
+ }
128
+ const isEmpty = queryIsEmpty ?? isEmptyFn(data);
129
+ if (isEmpty) {
130
+ return empty;
131
+ }
132
+ if (typeof children === "function") {
133
+ return children(data);
134
+ }
135
+ return children;
136
+ }
137
+ // src/Repeat.tsx
138
+ import { Fragment as Fragment2 } from "react";
139
+ import { jsx as jsx2 } from "react/jsx-runtime";
140
+ function Repeat({ times, children }) {
141
+ if (times <= 0) {
142
+ return null;
143
+ }
144
+ const elements = [];
145
+ for (let i = 0;i < times; i++) {
146
+ elements.push(/* @__PURE__ */ jsx2(Fragment2, {
147
+ children: children(i)
148
+ }, i));
149
+ }
150
+ return elements;
151
+ }
152
+ // src/Show.tsx
153
+ function Show({ when, children, fallback = null }) {
154
+ if (!when) {
155
+ return fallback;
156
+ }
157
+ if (typeof children === "function") {
158
+ return children(when);
159
+ }
160
+ return children;
161
+ }
162
+ // src/Switch.tsx
163
+ import { Children } from "react";
164
+ function Match(_props) {
165
+ return null;
166
+ }
167
+ function Default(_props) {
168
+ return null;
169
+ }
170
+ Match.__isMatch = true;
171
+ Default.__isDefault = true;
172
+ function isMatchElement(child) {
173
+ return child !== null && typeof child === "object" && "type" in child && typeof child.type === "function" && "__isMatch" in child.type && child.type.__isMatch === true;
174
+ }
175
+ function isDefaultElement(child) {
176
+ return child !== null && typeof child === "object" && "type" in child && typeof child.type === "function" && "__isDefault" in child.type && child.type.__isDefault === true;
177
+ }
178
+ function Switch({ children, fallback = null }) {
179
+ const childArray = Children.toArray(children);
180
+ let defaultContent = fallback;
181
+ for (const child of childArray) {
182
+ if (isDefaultElement(child)) {
183
+ defaultContent = child.props.children;
184
+ continue;
185
+ }
186
+ if (isMatchElement(child)) {
187
+ const { when, children: matchChildren } = child.props;
188
+ if (when) {
189
+ if (typeof matchChildren === "function") {
190
+ return matchChildren(when);
191
+ }
192
+ return matchChildren;
193
+ }
194
+ }
195
+ }
196
+ return defaultContent;
197
+ }
198
+ export {
199
+ Switch,
200
+ Show,
201
+ Repeat,
202
+ QueryBoundary,
203
+ Match,
204
+ For,
205
+ ErrorBoundary,
206
+ Dynamic,
207
+ Default,
208
+ Await
209
+ };
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "react-solidlike",
3
+ "version": "2.0.0",
4
+ "description": "Declarative React control flow components inspired by Solid.js, replacing ternary expressions and array.map() in JSX",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "NODE_ENV=production bun run build:esm && bun run build:cjs && bun run build:types",
21
+ "build:esm": "bun build ./src/index.ts --outdir ./dist --format esm --external react --external 'react/*'",
22
+ "build:cjs": "bun build ./src/index.ts --outfile ./dist/index.cjs --format cjs --external react --external 'react/*'",
23
+ "build:types": "tsc -p tsconfig.build.json",
24
+ "test": "bun test",
25
+ "lint": "biome check .",
26
+ "lint:fix": "biome check --write .",
27
+ "format": "biome format --write .",
28
+ "prepublishOnly": "bun test && bun run build"
29
+ },
30
+ "keywords": [
31
+ "react",
32
+ "solidjs",
33
+ "control-flow",
34
+ "conditional-rendering",
35
+ "declarative",
36
+ "jsx"
37
+ ],
38
+ "author": "ldystudio",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/ldystudio/react-solidlike.git"
43
+ },
44
+ "peerDependencies": {
45
+ "react": ">=17.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@biomejs/biome": "^2.3.11",
49
+ "@happy-dom/global-registrator": "^20.1.0",
50
+ "@testing-library/react": "^16.3.1",
51
+ "@testing-library/user-event": "^14.6.1",
52
+ "@types/bun": "latest",
53
+ "@types/react": "^19.2.8",
54
+ "@types/react-dom": "^19.2.3",
55
+ "react": "^19.2.3",
56
+ "react-dom": "^19.2.3",
57
+ "typescript": "^5.9.3"
58
+ }
59
+ }