react-ternary-be-gone 0.1.3 → 0.1.4

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 CHANGED
@@ -1,4 +1,3 @@
1
- ```markdown
2
1
  # React Conditional Component
3
2
 
4
3
  A React component designed to simplify conditional rendering and list iteration, providing a more readable and maintainable alternative to ternary operators and verbose conditional logic.
@@ -241,7 +240,7 @@ const emptyList = [];
241
240
 
242
241
  ### `loading`
243
242
 
244
- A boolean value indicating whether the component is in a loading state. If true, the `fallback` prop (or a default loading message) will be rendered.
243
+ A boolean value indicating whether the component is in a loading state. If true, `loadingFallback` (or `fallback`, or a default loading message) will be rendered instead of `children`.
245
244
 
246
245
  **Type:** `boolean`
247
246
 
@@ -253,11 +252,25 @@ A boolean value indicating whether the component is in a loading state. If true,
253
252
  </Conditional>
254
253
  ```
255
254
 
255
+ ### `loadingFallback`
256
+
257
+ Content to render while `loading` is true, taking priority over `fallback`. Use this when a route or component needs a distinct loading state instead of reusing its "nothing to show" fallback.
258
+
259
+ **Type:** `ReactNode`
260
+
261
+ **Example:**
262
+
263
+ ```javascript
264
+ <Conditional loading={isLoading} loadingFallback={<Spinner />} fallback={<p>No data.</p>}>
265
+ <p>Data loaded!</p>
266
+ </Conditional>
267
+ ```
268
+
256
269
  ### `error`
257
270
 
258
- An error message to be displayed if an error occurs.
271
+ An error value to be displayed if an error occurs. Falsy values (`null`, `undefined`, `false`, `''`) are treated as "no error".
259
272
 
260
- **Type:** `string`
273
+ **Type:** `string | Error | any`
261
274
 
262
275
  **Example:**
263
276
 
@@ -267,6 +280,20 @@ An error message to be displayed if an error occurs.
267
280
  </Conditional>
268
281
  ```
269
282
 
283
+ ### `errorFallback`
284
+
285
+ Content to render instead of the default `Error: ...` message when `error` is set. Accepts a `ReactNode`, or a function that receives the `error` value and returns one.
286
+
287
+ **Type:** `ReactNode | (error: any) => ReactNode`
288
+
289
+ **Example:**
290
+
291
+ ```javascript
292
+ <Conditional error={errorMessage} errorFallback={(err) => <Alert>{err}</Alert>}>
293
+ <p>Content.</p>
294
+ </Conditional>
295
+ ```
296
+
270
297
  ### `keyExtractor`
271
298
 
272
299
  A function to extract a unique key for each item when iterating over the `each` array.
package/dist/Case.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _markers = require("./markers");
8
+ var _default = exports.default = (0, _markers.createMarker)('Case', _markers.CASE);
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _markers = require("./markers");
9
+ var _jsxRuntime = require("react/jsx-runtime");
10
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
11
+ // Pure helper: apply filter/sort/reverse/limit to an `each` array without
12
+ // mutating the caller's array (Array.prototype.sort/reverse mutate in place,
13
+ // so every step works off a copy).
14
+ const processArray = (array, {
15
+ filter,
16
+ sort,
17
+ reverse,
18
+ limit
19
+ }) => {
20
+ if (!Array.isArray(array)) return [];
21
+ let result = [...array];
22
+ if (typeof filter === 'function') result = result.filter(filter);
23
+ if (typeof sort === 'function') result = result.sort(sort);
24
+ if (reverse) result = result.reverse();
25
+ if (typeof limit === 'number') result = result.slice(0, limit);
26
+ return result;
27
+ };
28
+
29
+ // Pure helper: render one React.Fragment per item, guarding the render-prop
30
+ // call so static children (a plain element instead of a function) don't
31
+ // crash when they reach an iteration path.
32
+ const renderIterableChildren = (items, children, keyExtractor) => items.map((item, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_react.default.Fragment, {
33
+ children: typeof children === 'function' ? children(item, index, items) : children
34
+ }, keyExtractor ? keyExtractor(item, index) : index));
35
+
36
+ // `React.Children.toArray` flattens arrays produced by `.map()`, but an
37
+ // author-written `<>...</>` around a set of branches is itself a single
38
+ // Fragment element in the tree - its contents need an explicit unwrap or
39
+ // `<If>`/`<ElseIf>`/`<Else>`/`<Case>` inside one are never seen as branches.
40
+ const flattenBranches = children => {
41
+ const result = [];
42
+ _react.default.Children.toArray(children).forEach(child => {
43
+ if (child && child.type === _react.default.Fragment) {
44
+ result.push(...flattenBranches(child.props.children));
45
+ } else {
46
+ result.push(child);
47
+ }
48
+ });
49
+ return result;
50
+ };
51
+ const Conditional = props => {
52
+ const {
53
+ when,
54
+ each,
55
+ children,
56
+ fallback = null,
57
+ empty = null,
58
+ loading = false,
59
+ loadingFallback = null,
60
+ error = null,
61
+ errorFallback = null,
62
+ keyExtractor = (item, index) => index,
63
+ // New features
64
+ filter = null,
65
+ sort = null,
66
+ limit = null,
67
+ reverse = false,
68
+ animate = false,
69
+ wrapper: Wrapper = _react.default.Fragment,
70
+ debug = false,
71
+ onRender = null,
72
+ // Conditional rendering helpers
73
+ gt = null,
74
+ // greater than
75
+ lt = null,
76
+ // less than
77
+ eq = null,
78
+ // equal
79
+ ne = null,
80
+ // not equal
81
+ includes = null,
82
+ startsWith = null,
83
+ endsWith = null,
84
+ match: matchProp = null,
85
+ // regex match
86
+ // Switch-case feature
87
+ switch: switchValue,
88
+ ...rest
89
+ } = props;
90
+ if (process.env.NODE_ENV !== 'production' && Object.keys(rest).length > 0) {
91
+ (0, _markers.warnOnce)(`[react-ternary-be-gone] <Conditional> received unrecognised prop(s): ${Object.keys(rest).join(', ')}. ` + 'Check for a typo - they were ignored.');
92
+ }
93
+
94
+ // `each={undefined}` (e.g. `each={data?.items}` before data loads) must
95
+ // still enter iteration mode so `empty`/fallback render instead of trying
96
+ // to hand a render-prop function to React as a child. Distinguishing "each
97
+ // was never passed" from "each was passed as undefined" needs the raw
98
+ // props object - destructuring can't tell them apart.
99
+ const hasIteration = 'each' in props;
100
+ const hasComparisonProp = [gt, lt, eq, ne, includes, startsWith, endsWith, matchProp].some(value => value !== null && typeof value === 'object');
101
+ const hasCondition = when !== undefined || hasComparisonProp;
102
+
103
+ // Advanced condition evaluation
104
+ const evaluateCondition = (0, _react.useMemo)(() => {
105
+ if (when !== undefined) return Boolean(when);
106
+
107
+ // Numerical comparisons
108
+ if (gt !== null && typeof gt === 'object') {
109
+ const {
110
+ value,
111
+ target
112
+ } = gt;
113
+ return value > target;
114
+ }
115
+ if (lt !== null && typeof lt === 'object') {
116
+ const {
117
+ value,
118
+ target
119
+ } = lt;
120
+ return value < target;
121
+ }
122
+ if (eq !== null && typeof eq === 'object') {
123
+ const {
124
+ value,
125
+ target
126
+ } = eq;
127
+ return value === target;
128
+ }
129
+ if (ne !== null && typeof ne === 'object') {
130
+ const {
131
+ value,
132
+ target
133
+ } = ne;
134
+ return value !== target;
135
+ }
136
+
137
+ // String operations
138
+ if (includes !== null && typeof includes === 'object') {
139
+ const {
140
+ value,
141
+ target
142
+ } = includes;
143
+ return String(value).includes(target);
144
+ }
145
+ if (startsWith !== null && typeof startsWith === 'object') {
146
+ const {
147
+ value,
148
+ target
149
+ } = startsWith;
150
+ return String(value).startsWith(target);
151
+ }
152
+ if (endsWith !== null && typeof endsWith === 'object') {
153
+ const {
154
+ value,
155
+ target
156
+ } = endsWith;
157
+ return String(value).endsWith(target);
158
+ }
159
+ if (matchProp !== null && typeof matchProp === 'object') {
160
+ const {
161
+ value,
162
+ pattern
163
+ } = matchProp;
164
+ return new RegExp(pattern).test(String(value));
165
+ }
166
+ return true;
167
+ }, [when, gt, lt, eq, ne, includes, startsWith, endsWith, matchProp]);
168
+
169
+ // Array processing
170
+ const processedArray = (0, _react.useMemo)(() => {
171
+ const result = processArray(each, {
172
+ filter,
173
+ sort,
174
+ reverse,
175
+ limit
176
+ });
177
+ if (debug) {
178
+ console.log('Conditional Debug:', {
179
+ original: each,
180
+ processed: result,
181
+ filter: !!filter,
182
+ sort: !!sort,
183
+ reverse,
184
+ limit
185
+ });
186
+ }
187
+ return result;
188
+ }, [each, filter, sort, reverse, limit, debug]);
189
+
190
+ // Render callback
191
+ (0, _react.useEffect)(() => {
192
+ if (onRender && typeof onRender === 'function') {
193
+ onRender({
194
+ condition: evaluateCondition,
195
+ itemCount: processedArray.length,
196
+ hasCondition,
197
+ hasIteration
198
+ });
199
+ }
200
+ }, [evaluateCondition, processedArray.length, hasCondition, hasIteration, onRender]);
201
+
202
+ // Loading state
203
+ if (loading) {
204
+ return loadingFallback || fallback || /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
205
+ className: "conditional-loading",
206
+ children: "Loading..."
207
+ });
208
+ }
209
+
210
+ // Error state
211
+ if (error) {
212
+ if (errorFallback) {
213
+ return typeof errorFallback === 'function' ? errorFallback(error) : errorFallback;
214
+ }
215
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
216
+ className: "conditional-error",
217
+ children: ["Error: ", error]
218
+ });
219
+ }
220
+
221
+ // Switch-case logic. Presence, not strict inequality to `undefined`, is
222
+ // what enters this mode - `switch={status}` must still honour `default`
223
+ // while `status` is legitimately undefined before data loads, the same
224
+ // reasoning as the `each` presence check above.
225
+ if ('switch' in props) {
226
+ const caseChildren = flattenBranches(children).filter(child => child && (0, _markers.markerKindOf)(child) === _markers.CASE);
227
+ // `.find()` stops at the first hit, matching how a real `switch` picks
228
+ // the first matching branch instead of the last one.
229
+ const matchedCase = caseChildren.find(child => !child.props.default && child.props.when === switchValue);
230
+ const defaultCase = caseChildren.find(child => child.props.default);
231
+ if (matchedCase) {
232
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(Wrapper, {
233
+ children: matchedCase.props.children
234
+ });
235
+ }
236
+ if (defaultCase) {
237
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(Wrapper, {
238
+ children: defaultCase.props.children
239
+ });
240
+ }
241
+ return fallback;
242
+ }
243
+
244
+ // If-ElseIf-Else logic
245
+ const branches = flattenBranches(children).filter(child => child && [_markers.IF, _markers.ELSE_IF, _markers.ELSE].includes((0, _markers.markerKindOf)(child)));
246
+ if (branches.length > 0) {
247
+ let rendered = fallback;
248
+ for (const branch of branches) {
249
+ const kind = (0, _markers.markerKindOf)(branch);
250
+ const {
251
+ when: branchWhen,
252
+ each: branchEach,
253
+ filter: branchFilter,
254
+ sort: branchSort,
255
+ limit: branchLimit,
256
+ reverse: branchReverse,
257
+ wrapper: BranchWrapper = _react.default.Fragment,
258
+ keyExtractor: branchKeyExtractor,
259
+ empty: branchEmpty,
260
+ fallback: branchFallback,
261
+ children: branchChildren
262
+ } = branch.props;
263
+ const matches = kind === _markers.ELSE || Boolean(branchWhen);
264
+ if (!matches) continue;
265
+ if ('each' in branch.props) {
266
+ const items = processArray(branchEach, {
267
+ filter: branchFilter,
268
+ sort: branchSort,
269
+ reverse: branchReverse,
270
+ limit: branchLimit
271
+ });
272
+ rendered = items.length === 0 ? branchEmpty || branchFallback || null : /*#__PURE__*/(0, _jsxRuntime.jsx)(BranchWrapper, {
273
+ children: renderIterableChildren(items, branchChildren, branchKeyExtractor)
274
+ });
275
+ } else {
276
+ rendered = /*#__PURE__*/(0, _jsxRuntime.jsx)(BranchWrapper, {
277
+ children: branchChildren
278
+ });
279
+ }
280
+ break;
281
+ }
282
+ return rendered;
283
+ }
284
+
285
+ // Only condition
286
+ if (hasCondition && !hasIteration) {
287
+ return evaluateCondition ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Wrapper, {
288
+ children: children
289
+ }) : fallback;
290
+ }
291
+
292
+ // Only iteration
293
+ if (!hasCondition && hasIteration) {
294
+ if (processedArray.length === 0) {
295
+ return empty || fallback;
296
+ }
297
+ const elements = renderIterableChildren(processedArray, children, keyExtractor);
298
+ // React.Fragment rejects unknown props like `className`, so the default
299
+ // wrapper is swapped for a real host element whenever `animate` needs
300
+ // somewhere to put the class.
301
+ const AnimatedWrapper = animate && Wrapper === _react.default.Fragment ? 'div' : Wrapper;
302
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(AnimatedWrapper, {
303
+ ...(animate ? {
304
+ className: 'conditional-animated'
305
+ } : null),
306
+ children: elements
307
+ });
308
+ }
309
+
310
+ // Both condition and iteration
311
+ if (hasCondition && hasIteration) {
312
+ if (!evaluateCondition) return fallback;
313
+ if (processedArray.length === 0) return empty || fallback;
314
+ const elements = renderIterableChildren(processedArray, children, keyExtractor);
315
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(Wrapper, {
316
+ children: elements
317
+ });
318
+ }
319
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(Wrapper, {
320
+ children: children
321
+ });
322
+ };
323
+ var _default = exports.default = Conditional;
package/dist/Else.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _markers = require("./markers");
8
+ var _default = exports.default = (0, _markers.createMarker)('Else', _markers.ELSE);
package/dist/ElseIf.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _markers = require("./markers");
8
+ var _default = exports.default = (0, _markers.createMarker)('ElseIf', _markers.ELSE_IF);
package/dist/If.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _markers = require("./markers");
8
+ var _default = exports.default = (0, _markers.createMarker)('If', _markers.IF);
package/dist/index.js ADDED
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "Case", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _Case.default;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "Else", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _Else.default;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "ElseIf", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _ElseIf.default;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "If", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _If.default;
28
+ }
29
+ });
30
+ exports.default = void 0;
31
+ Object.defineProperty(exports, "useConditionalHelpers", {
32
+ enumerable: true,
33
+ get: function () {
34
+ return _useConditionalHelpers.useConditionalHelpers;
35
+ }
36
+ });
37
+ var _Conditional = _interopRequireDefault(require("./Conditional"));
38
+ var _Case = _interopRequireDefault(require("./Case"));
39
+ var _If = _interopRequireDefault(require("./If"));
40
+ var _ElseIf = _interopRequireDefault(require("./ElseIf"));
41
+ var _Else = _interopRequireDefault(require("./Else"));
42
+ var _useConditionalHelpers = require("./useConditionalHelpers");
43
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
44
+ var _default = exports.default = _Conditional.default;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.warnOnce = exports.markerKindOf = exports.createMarker = exports.IF = exports.ELSE_IF = exports.ELSE = exports.CASE = void 0;
7
+ // Branch markers (`If`, `ElseIf`, `Else`, `Case`) are never rendered by React
8
+ // when they are used correctly: `Conditional` reads their props and renders the
9
+ // winning branch itself. They exist only so that JSX can express the shape.
10
+ //
11
+ // Identifying them by reference (`child.type === If`) breaks as soon as two
12
+ // copies of this package end up in node_modules, so each marker carries a
13
+ // static tag instead.
14
+
15
+ const IF = exports.IF = 'if';
16
+ const ELSE_IF = exports.ELSE_IF = 'elseif';
17
+ const ELSE = exports.ELSE = 'else';
18
+ const CASE = exports.CASE = 'case';
19
+ const TAG = '$$rtbgMarker';
20
+ const warned = new Set();
21
+ const warnOnce = message => {
22
+ if (warned.has(message)) return;
23
+ warned.add(message);
24
+ console.warn(message);
25
+ };
26
+ exports.warnOnce = warnOnce;
27
+ const createMarker = (name, kind) => {
28
+ // Rendering nothing is the safe default: a marker that slipped outside a
29
+ // `Conditional` must not leak the content it was meant to gate.
30
+ const Marker = () => {
31
+ if (process.env.NODE_ENV !== 'production') {
32
+ warnOnce(`[react-ternary-be-gone] <${name}> only works as a child of <Conditional>. ` + 'It rendered nothing.');
33
+ }
34
+ return null;
35
+ };
36
+ Marker.displayName = name;
37
+ Marker[TAG] = kind;
38
+ return Marker;
39
+ };
40
+ exports.createMarker = createMarker;
41
+ const markerKindOf = element => element && element.type && element.type[TAG] || null;
42
+ exports.markerKindOf = markerKindOf;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useConditionalHelpers = void 0;
7
+ var _react = require("react");
8
+ const useConditionalHelpers = () => {
9
+ return (0, _react.useMemo)(() => ({
10
+ // Quick condition helpers
11
+ isEmpty: array => ({
12
+ when: !array || array.length === 0
13
+ }),
14
+ isNotEmpty: array => ({
15
+ when: Boolean(array && array.length > 0)
16
+ }),
17
+ hasLength: (array, length) => ({
18
+ when: Boolean(array && array.length === length)
19
+ }),
20
+ isEven: num => ({
21
+ when: num % 2 === 0
22
+ }),
23
+ isOdd: num => ({
24
+ when: num % 2 !== 0
25
+ }),
26
+ // Array processing helpers
27
+ sortBy: (field, order = 'asc') => (a, b) => {
28
+ const aValue = a[field];
29
+ const bValue = b[field];
30
+
31
+ // `<`/`>` compare strings by raw UTF-16 code unit, which misorders
32
+ // accented letters (Ç, Ğ, İ, Ö, Ş, Ü, ...). localeCompare sorts them
33
+ // the way a person reading the list would expect.
34
+ let comparison;
35
+ if (typeof aValue === 'string' && typeof bValue === 'string') {
36
+ comparison = aValue.localeCompare(bValue);
37
+ } else if (aValue < bValue) {
38
+ comparison = -1;
39
+ } else if (aValue > bValue) {
40
+ comparison = 1;
41
+ } else {
42
+ comparison = 0;
43
+ }
44
+ return order === 'asc' ? comparison : -comparison;
45
+ },
46
+ filterBy: (field, value) => item => item[field] === value,
47
+ unique: (array, key) => {
48
+ if (!array) return [];
49
+ const seen = new Set();
50
+ return array.filter(item => {
51
+ const keyValue = typeof key === 'function' ? key(item) : item[key];
52
+ const isNew = !seen.has(keyValue);
53
+ seen.add(keyValue);
54
+ return isNew;
55
+ });
56
+ }
57
+ }), []);
58
+ };
59
+ exports.useConditionalHelpers = useConditionalHelpers;
package/package.json CHANGED
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "react-ternary-be-gone",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A React component designed to simplify conditional rendering and list iteration, providing a more readable and maintainable alternative to ternary operators and verbose conditional logic.",
5
5
  "main": "dist/index.js",
6
+ "files": [
7
+ "dist"
8
+ ],
6
9
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1",
8
- "build": "babel . --out-dir dist"
10
+ "test": "vitest run",
11
+ "test:watch": "vitest",
12
+ "build": "babel src --out-dir dist --out-file-extension .js",
13
+ "prepublishOnly": "npm test && npm run build"
9
14
  },
10
15
  "peerDependencies": {
11
16
  "react": ">=16.8.0"
@@ -26,7 +31,15 @@
26
31
  },
27
32
  "homepage": "https://github.com/sundowatch/react-ternary-be-gone#readme",
28
33
  "devDependencies": {
29
- "@babel/cli": "^7.28.0",
30
- "@babel/preset-react": "^7.27.1"
34
+ "@babel/cli": "^8.0.0",
35
+ "@babel/core": "^8.0.0",
36
+ "@babel/preset-env": "^8.0.0",
37
+ "@babel/preset-react": "^8.0.0",
38
+ "@testing-library/react": "^16.3.3",
39
+ "@vitejs/plugin-react": "^5.0.0",
40
+ "jsdom": "^30.1.1",
41
+ "react": "^19.3.0",
42
+ "react-dom": "^19.3.0",
43
+ "vitest": "^5.0.1"
31
44
  }
32
45
  }
package/.babelrc DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "presets": ["@babel/preset-env", "@babel/preset-react"]
3
- }
package/Case.js DELETED
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
-
3
- const Case = ({ when, children, default: isDefault = false }) => {
4
- // This component is just a marker for Conditional to process
5
- return <>{children}</>;
6
- };
7
-
8
- export default Case;
package/Conditional.js DELETED
@@ -1,328 +0,0 @@
1
- import React, { useMemo, useEffect } from 'react';
2
- import Case from './Case';
3
- import If from './If';
4
- import ElseIf from './ElseIf';
5
- import Else from './Else';
6
-
7
- const Conditional = ({
8
- when,
9
- each,
10
- children,
11
- fallback = null,
12
- empty = null,
13
- loading = false,
14
- error = null,
15
- keyExtractor = (item, index) => index,
16
- // New features
17
- filter = null,
18
- sort = null,
19
- limit = null,
20
- reverse = false,
21
- animate = false,
22
- wrapper = React.Fragment,
23
- debug = false,
24
- onRender = null,
25
- // Conditional rendering helpers
26
- gt = null, // greater than
27
- lt = null, // less than
28
- eq = null, // equal
29
- ne = null, // not equal
30
- includes = null,
31
- startsWith = null,
32
- endsWith = null,
33
- match = null, // regex match
34
- // Switch-case feature
35
- switch: switchValue,
36
- }) => {
37
- const hasCondition = when !== undefined;
38
- const hasIteration = each !== undefined;
39
-
40
- // Advanced condition evaluation
41
- const evaluateCondition = useMemo(() => {
42
- if (when !== undefined) return Boolean(when);
43
-
44
- // Numerical comparisons
45
- if (gt !== null && typeof gt === 'object') {
46
- const { value, target } = gt;
47
- return value > target;
48
- }
49
- if (lt !== null && typeof lt === 'object') {
50
- const { value, target } = lt;
51
- return value < target;
52
- }
53
- if (eq !== null && typeof eq === 'object') {
54
- const { value, target } = eq;
55
- return value === target;
56
- }
57
- if (ne !== null && typeof ne === 'object') {
58
- const { value, target } = ne;
59
- return value !== target;
60
- }
61
-
62
- // String operations
63
- if (includes !== null && typeof includes === 'object') {
64
- const { value, target } = includes;
65
- return String(value).includes(target);
66
- }
67
- if (startsWith !== null && typeof startsWith === 'object') {
68
- const { value, target } = startsWith;
69
- return String(value).startsWith(target);
70
- }
71
- if (endsWith !== null && typeof endsWith === 'object') {
72
- const { value, target } = endsWith;
73
- return String(value).endsWith(target);
74
- }
75
- if (match !== null && typeof match === 'object') {
76
- const { value, pattern } = match;
77
- return new RegExp(pattern).test(String(value));
78
- }
79
-
80
- return true;
81
- }, [when, gt, lt, eq, ne, includes, startsWith, endsWith, match]);
82
-
83
- // Array processing
84
- const processedArray = useMemo(() => {
85
- if (!hasIteration || !Array.isArray(each)) return [];
86
-
87
- let result = [...each];
88
-
89
- // Apply filter
90
- if (filter && typeof filter === 'function') {
91
- result = result.filter(filter);
92
- }
93
-
94
- // Apply sort
95
- if (sort && typeof sort === 'function') {
96
- result = result.sort(sort);
97
- }
98
-
99
- // Apply reverse
100
- if (reverse) {
101
- result = result.reverse();
102
- }
103
-
104
- // Apply limit
105
- if (limit && typeof limit === 'number') {
106
- result = result.slice(0, limit);
107
- }
108
-
109
- if (debug) {
110
- console.log('Conditional Debug:', {
111
- original: each,
112
- processed: result,
113
- filter: !!filter,
114
- sort: !!sort,
115
- reverse,
116
- limit
117
- });
118
- }
119
-
120
- return result;
121
- }, [each, filter, sort, reverse, limit, hasIteration, debug]);
122
-
123
- // Render callback
124
- useEffect(() => {
125
- if (onRender && typeof onRender === 'function') {
126
- onRender({
127
- condition: evaluateCondition,
128
- itemCount: processedArray.length,
129
- hasCondition,
130
- hasIteration
131
- });
132
- }
133
- }, [evaluateCondition, processedArray.length, hasCondition, hasIteration, onRender]);
134
-
135
- // Loading state
136
- if (loading) {
137
- return fallback || <div className="conditional-loading">Loading...</div>;
138
- }
139
-
140
- // Error state
141
- if (error) {
142
- return <div className="conditional-error">Error: {error}</div>;
143
- }
144
-
145
- // Wrapper component
146
- const WrapperComponent = wrapper;
147
-
148
- // Switch-case logic
149
- if (switchValue !== undefined) {
150
- // Find all Case children
151
- const caseChildren = React.Children.toArray(children).filter(
152
- child => child && child.type === Case
153
- );
154
- // Find matching case
155
- let match = null;
156
- let defaultCase = null;
157
- caseChildren.forEach(child => {
158
- if (child.props.default) {
159
- defaultCase = child;
160
- } else if (child.props.when === switchValue) {
161
- match = child;
162
- }
163
- });
164
- if (match) {
165
- return <WrapperComponent>{match.props.children}</WrapperComponent>;
166
- } else if (defaultCase) {
167
- return <WrapperComponent>{defaultCase.props.children}</WrapperComponent>;
168
- } else {
169
- return fallback;
170
- }
171
- }
172
-
173
- // If-ElseIf-Else logic
174
- const childrenArray = React.Children.toArray(children);
175
- const ifElseBlocks = childrenArray.filter(child =>
176
- child && (child.type === If || child.type === ElseIf || child.type === Else)
177
- );
178
- if (ifElseBlocks.length > 0) {
179
- let rendered = null;
180
- for (let i = 0; i < ifElseBlocks.length; i++) {
181
- const child = ifElseBlocks[i];
182
- // Extract Conditional props from child
183
- const {
184
- when,
185
- each,
186
- filter,
187
- sort,
188
- limit,
189
- reverse,
190
- animate,
191
- wrapper: childWrapper,
192
- keyExtractor: childKeyExtractor,
193
- empty: childEmpty,
194
- fallback: childFallback,
195
- ...rest
196
- } = child.props;
197
-
198
- // Evaluate condition
199
- let condition = true;
200
- if (child.type === If || child.type === ElseIf) {
201
- condition = Boolean(when);
202
- }
203
-
204
- // Array processing for each
205
- let arrayItems = [];
206
- if (each && Array.isArray(each)) {
207
- arrayItems = [...each];
208
- if (filter && typeof filter === 'function') {
209
- arrayItems = arrayItems.filter(filter);
210
- }
211
- if (sort && typeof sort === 'function') {
212
- arrayItems = arrayItems.sort(sort);
213
- }
214
- if (reverse) {
215
- arrayItems = arrayItems.reverse();
216
- }
217
- if (limit && typeof limit === 'number') {
218
- arrayItems = arrayItems.slice(0, limit);
219
- }
220
- }
221
-
222
- // Render logic
223
- if ((child.type === If || child.type === ElseIf) && condition) {
224
- if (each && Array.isArray(each)) {
225
- if (arrayItems.length === 0) {
226
- rendered = childEmpty || childFallback || null;
227
- break;
228
- }
229
- const elements = arrayItems.map((item, idx) => (
230
- <React.Fragment key={childKeyExtractor ? childKeyExtractor(item, idx) : idx}>
231
- {typeof child.props.children === 'function'
232
- ? child.props.children(item, idx, arrayItems)
233
- : child.props.children}
234
- </React.Fragment>
235
- ));
236
- rendered = childWrapper
237
- ? React.createElement(childWrapper, null, elements)
238
- : <>{elements}</>;
239
- break;
240
- } else {
241
- rendered = childWrapper
242
- ? React.createElement(childWrapper, null, child.props.children)
243
- : <>{child.props.children}</>;
244
- break;
245
- }
246
- }
247
- if (child.type === Else) {
248
- if (each && Array.isArray(each)) {
249
- if (arrayItems.length === 0) {
250
- rendered = childEmpty || childFallback || null;
251
- break;
252
- }
253
- const elements = arrayItems.map((item, idx) => (
254
- <React.Fragment key={childKeyExtractor ? childKeyExtractor(item, idx) : idx}>
255
- {typeof child.props.children === 'function'
256
- ? child.props.children(item, idx, arrayItems)
257
- : child.props.children}
258
- </React.Fragment>
259
- ));
260
- rendered = childWrapper
261
- ? React.createElement(childWrapper, null, elements)
262
- : <>{elements}</>;
263
- break;
264
- } else {
265
- rendered = childWrapper
266
- ? React.createElement(childWrapper, null, child.props.children)
267
- : <>{child.props.children}</>;
268
- break;
269
- }
270
- }
271
- }
272
- return rendered;
273
- }
274
-
275
- // Only condition
276
- if (hasCondition && !hasIteration) {
277
- return evaluateCondition ? (
278
- <WrapperComponent>
279
- {children}
280
- </WrapperComponent>
281
- ) : fallback;
282
- }
283
-
284
- // Only iteration
285
- if (!hasCondition && hasIteration) {
286
- if (processedArray.length === 0) {
287
- return empty || fallback;
288
- }
289
-
290
- const elements = processedArray.map((item, index) => (
291
- <React.Fragment key={keyExtractor(item, index)}>
292
- {typeof children === 'function' ? children(item, index, processedArray) : children}
293
- </React.Fragment>
294
- ));
295
-
296
- return animate ? (
297
- <WrapperComponent className="conditional-animated">
298
- {elements}
299
- </WrapperComponent>
300
- ) : (
301
- <WrapperComponent>
302
- {elements}
303
- </WrapperComponent>
304
- );
305
- }
306
-
307
- // Both condition and iteration
308
- if (hasCondition && hasIteration) {
309
- if (!evaluateCondition) return fallback;
310
- if (processedArray.length === 0) return empty || fallback;
311
-
312
- const elements = processedArray.map((item, index) => (
313
- <React.Fragment key={keyExtractor(item, index)}>
314
- {children(item, index, processedArray)}
315
- </React.Fragment>
316
- ));
317
-
318
- return (
319
- <WrapperComponent>
320
- {elements}
321
- </WrapperComponent>
322
- );
323
- }
324
-
325
- return <WrapperComponent>{children}</WrapperComponent>;
326
- };
327
-
328
- export default Conditional;
package/Else.js DELETED
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
-
3
- const Else = ({ children, ...rest }) => {
4
- // Marker for Conditional, supports all Conditional props
5
- return <>{children}</>;
6
- };
7
-
8
- export default Else;
package/ElseIf.js DELETED
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
-
3
- const ElseIf = ({ when, children, ...rest }) => {
4
- // Marker for Conditional, supports all Conditional props
5
- return <>{children}</>;
6
- };
7
-
8
- export default ElseIf;
package/If.js DELETED
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
-
3
- const If = ({ when, children, ...rest }) => {
4
- // Marker for Conditional, supports all Conditional props
5
- return <>{children}</>;
6
- };
7
-
8
- export default If;
package/babel.config.js DELETED
@@ -1,3 +0,0 @@
1
- module.exports = {
2
- presets: ["@babel/preset-env", "@babel/preset-react"]
3
- };
package/index.js DELETED
@@ -1,9 +0,0 @@
1
- import Conditional from './Conditional';
2
- import Case from './Case';
3
- import If from './If';
4
- import ElseIf from './ElseIf';
5
- import Else from './Else';
6
- import { useConditionalHelpers } from './useConditionalHelpers';
7
-
8
- export default Conditional;
9
- export { Case, If, ElseIf, Else, useConditionalHelpers };
@@ -1,36 +0,0 @@
1
- import { useMemo } from 'react';
2
-
3
- export const useConditionalHelpers = () => {
4
- return useMemo(() => ({
5
- // Quick condition helpers
6
- isEmpty: (array) => ({ when: !array || array.length === 0 }),
7
- isNotEmpty: (array) => ({ when: array && array.length > 0 }),
8
- hasLength: (array, length) => ({ when: array && array.length === length }),
9
- isEven: (num) => ({ when: num % 2 === 0 }),
10
- isOdd: (num) => ({ when: num % 2 !== 0 }),
11
-
12
- // Array processing helpers
13
- sortBy: (field, order = 'asc') => (a, b) => {
14
- const aValue = a[field];
15
- const bValue = b[field];
16
-
17
- if (aValue < bValue) {
18
- return order === 'asc' ? -1 : 1;
19
- }
20
- if (aValue > bValue) {
21
- return order === 'asc' ? 1 : -1;
22
- }
23
- return 0;
24
- },
25
- filterBy: (field, value) => (item) => item[field] === value,
26
- unique: (array, key) => {
27
- const seen = new Set();
28
- return array.filter(item => {
29
- const keyValue = typeof key === 'function' ? key(item) : item[key];
30
- const isNew = !seen.has(keyValue);
31
- seen.add(keyValue);
32
- return isNew;
33
- });
34
- },
35
- }), []);
36
- };