@applicaster/zapp-react-native-utils 12.0.0-alpha.4837064762 → 12.0.0-alpha.4945500007
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/appUtils/focusManager/index.ts +24 -127
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +42 -97
- package/headersUtils/index.ts +1 -0
- package/package.json +2 -2
- package/reactHooks/navigation/index.ts +0 -15
- package/reactHooks/screen/useScreenContext.ts +2 -3
- package/reactUtils/helpers.tsx +1 -1
- package/stringUtils/index.ts +5 -0
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
-
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
3
|
-
|
|
4
2
|
import { Tree } from "./treeDataStructure/Tree";
|
|
5
3
|
import { findFocusableNode, findPriorityItem } from "./treeDataStructure/Utils";
|
|
6
4
|
import { subscriber } from "../../functionUtils";
|
|
@@ -19,22 +17,6 @@ const isFocusEnabled = (focusableItem): boolean => {
|
|
|
19
17
|
return true;
|
|
20
18
|
};
|
|
21
19
|
|
|
22
|
-
const waitUntil = (conditionFn) => {
|
|
23
|
-
let interval;
|
|
24
|
-
return new Promise((resolve, reject) => {
|
|
25
|
-
interval = setInterval(() => {
|
|
26
|
-
if (conditionFn()) {
|
|
27
|
-
clearInterval(interval);
|
|
28
|
-
resolve(undefined);
|
|
29
|
-
}
|
|
30
|
-
}, 1000);
|
|
31
|
-
|
|
32
|
-
setTimeout(() => {
|
|
33
|
-
reject();
|
|
34
|
-
}, 2500);
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
|
|
38
20
|
/**
|
|
39
21
|
* The focusManager is meant to keep references of focusables elements in the UI, and provide
|
|
40
22
|
* ways to assign the focus to one of the focusables.
|
|
@@ -64,7 +46,7 @@ export const focusManager = (function () {
|
|
|
64
46
|
let lastFocusedId = null;
|
|
65
47
|
let initialFocusOnContent = null;
|
|
66
48
|
let focusDisabled = false;
|
|
67
|
-
let focusAutoEnabled =
|
|
49
|
+
let focusAutoEnabled = true;
|
|
68
50
|
const eventHandler = subscriber();
|
|
69
51
|
|
|
70
52
|
function disableAutomaticFocus() {
|
|
@@ -111,7 +93,7 @@ export const focusManager = (function () {
|
|
|
111
93
|
!currentFocusable.isGroup &&
|
|
112
94
|
currentFocusable.isMounted()
|
|
113
95
|
) {
|
|
114
|
-
currentFocusable.blur(direction);
|
|
96
|
+
currentFocusable.blur(currentFocusable, direction);
|
|
115
97
|
currentFocusable.hasLostFocus(getCurrentFocus(), direction);
|
|
116
98
|
}
|
|
117
99
|
}
|
|
@@ -244,41 +226,31 @@ export const focusManager = (function () {
|
|
|
244
226
|
|
|
245
227
|
logger.log({ message: "setInitialFocus", data: { preferredFocus } });
|
|
246
228
|
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
229
|
+
if (preferredFocus && preferredFocus.length > 0) {
|
|
230
|
+
const focusCandidate = preferredFocus[0];
|
|
231
|
+
const focusableItem = focusCandidate.component;
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
focusableItem &&
|
|
235
|
+
!focusableItem.isGroup &&
|
|
236
|
+
isFocusEnabled(focusableItem)
|
|
237
|
+
) {
|
|
238
|
+
if (initialFocusOnContent) {
|
|
239
|
+
blur();
|
|
240
|
+
initialFocusOnContent = false;
|
|
241
|
+
}
|
|
256
242
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
243
|
+
logger.log({
|
|
244
|
+
message: "setInitialFocus – currentFocusNode",
|
|
245
|
+
data: {
|
|
246
|
+
focusableItem,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
261
249
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
isFocusEnabled(focusableItem)
|
|
266
|
-
) {
|
|
267
|
-
if (initialFocusOnContent) {
|
|
268
|
-
blur();
|
|
269
|
-
initialFocusOnContent = false;
|
|
250
|
+
currentFocusNode = focusCandidate;
|
|
251
|
+
focusableItem.willReceiveFocus(focusableItem, null);
|
|
252
|
+
focusableItem && setFocus(focusCandidate.id, null);
|
|
270
253
|
}
|
|
271
|
-
|
|
272
|
-
logger.log({
|
|
273
|
-
message: "setInitialFocus – currentFocusNode",
|
|
274
|
-
data: {
|
|
275
|
-
focusableItem,
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
currentFocusNode = focusCandidate;
|
|
280
|
-
focusableItem.willReceiveFocus(focusableItem, null);
|
|
281
|
-
focusableItem && setFocus(focusCandidate.id, null);
|
|
282
254
|
}
|
|
283
255
|
}
|
|
284
256
|
|
|
@@ -351,8 +323,6 @@ export const focusManager = (function () {
|
|
|
351
323
|
* @returns {React.Component} the registered component
|
|
352
324
|
*/
|
|
353
325
|
function registerGroup(id, component) {
|
|
354
|
-
// console.log("debug_2", "FM registerGroup", { id, component });
|
|
355
|
-
|
|
356
326
|
focusableTree.addNode({ id, component });
|
|
357
327
|
|
|
358
328
|
return component;
|
|
@@ -368,8 +338,6 @@ export const focusManager = (function () {
|
|
|
368
338
|
* @returns {React.Component} the registered component
|
|
369
339
|
*/
|
|
370
340
|
function registerItem(id, component) {
|
|
371
|
-
// console.log("debug_2", "FM registerItem", { id, component });
|
|
372
|
-
|
|
373
341
|
focusableTree.addNode({ id, component });
|
|
374
342
|
|
|
375
343
|
if (!currentFocusNode && id === lastFocusedId && !id.includes("menu")) {
|
|
@@ -386,8 +354,6 @@ export const focusManager = (function () {
|
|
|
386
354
|
* @param {String} id of the group to unregister
|
|
387
355
|
*/
|
|
388
356
|
function unregisterGroup(id) {
|
|
389
|
-
// console.log("debug_2", "FM unregisterGroup", { id });
|
|
390
|
-
|
|
391
357
|
focusableTree.findAndRemoveNode(id);
|
|
392
358
|
}
|
|
393
359
|
|
|
@@ -399,8 +365,6 @@ export const focusManager = (function () {
|
|
|
399
365
|
* @param {String} id of the Focusable to unregister
|
|
400
366
|
*/
|
|
401
367
|
function unregisterItem(id) {
|
|
402
|
-
// console.log("debug_2", "FM unregisterItem", { id });
|
|
403
|
-
|
|
404
368
|
focusableTree.findAndRemoveNode(id);
|
|
405
369
|
|
|
406
370
|
if (currentFocusNode && currentFocusNode.id === id) {
|
|
@@ -439,8 +403,6 @@ export const focusManager = (function () {
|
|
|
439
403
|
}
|
|
440
404
|
|
|
441
405
|
function setupInitialFocus(id: string, focusOnContent?: boolean) {
|
|
442
|
-
console.log("debug_2", "FM - setupInitialFocus", { id, focusOnContent });
|
|
443
|
-
|
|
444
406
|
if (focusOnContent) {
|
|
445
407
|
initialFocusOnContent = focusOnContent;
|
|
446
408
|
} else {
|
|
@@ -448,62 +410,6 @@ export const focusManager = (function () {
|
|
|
448
410
|
}
|
|
449
411
|
}
|
|
450
412
|
|
|
451
|
-
function setFocusOnMenu() {
|
|
452
|
-
const activeScreen = R.last(focusableTree.root.children);
|
|
453
|
-
const navbar = R.find(
|
|
454
|
-
(child) => R.startsWith("navbar", child.id),
|
|
455
|
-
activeScreen.children
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
const selectedMenuItemId = R.find(
|
|
459
|
-
(child) => child.component.props.selected,
|
|
460
|
-
navbar?.children || []
|
|
461
|
-
)?.id;
|
|
462
|
-
|
|
463
|
-
if (selectedMenuItemId) {
|
|
464
|
-
// set focus on selected menu item
|
|
465
|
-
setFocus(selectedMenuItemId);
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const firstMenuItemId = R.path(["children", 0, "id"], navbar);
|
|
470
|
-
|
|
471
|
-
if (firstMenuItemId) {
|
|
472
|
-
// set focus on first menu item
|
|
473
|
-
setFocus(firstMenuItemId);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function setFocusOnContent() {
|
|
478
|
-
// we need to put focus on content here
|
|
479
|
-
// For doing it we have to wait registration of first focusable node into focusableTree
|
|
480
|
-
// this is the temporary hack with waiting until content node starts having children
|
|
481
|
-
const getContentNode = () => {
|
|
482
|
-
const activeScreen = R.last(focusableTree.root.children);
|
|
483
|
-
const content = R.find(
|
|
484
|
-
(child) => R.startsWith("content", child.id),
|
|
485
|
-
activeScreen.children
|
|
486
|
-
);
|
|
487
|
-
|
|
488
|
-
return content;
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
const contentHasAnyChildren = () => {
|
|
492
|
-
return getContentNode()?.children.length > 0;
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
waitUntil(contentHasAnyChildren)
|
|
496
|
-
.then(() => {
|
|
497
|
-
const content = getContentNode();
|
|
498
|
-
|
|
499
|
-
setInitialFocus(content);
|
|
500
|
-
})
|
|
501
|
-
.catch(() => {
|
|
502
|
-
// we failed to wait any content, put focus on menu as a last chance
|
|
503
|
-
setFocusOnMenu();
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
|
|
507
413
|
/**
|
|
508
414
|
* moves the focus in a provided direction
|
|
509
415
|
* @public
|
|
@@ -516,13 +422,6 @@ export const focusManager = (function () {
|
|
|
516
422
|
currentFocusNode.component.willLoseFocus(currentFocusNode, direction);
|
|
517
423
|
const nextFocusable = findFocusableNode(direction, currentFocusNode);
|
|
518
424
|
|
|
519
|
-
console.log("debug_2", "FM moveFocus", {
|
|
520
|
-
direction: direction?.value,
|
|
521
|
-
currentFocusNode,
|
|
522
|
-
focusableTree,
|
|
523
|
-
nextFocusable,
|
|
524
|
-
});
|
|
525
|
-
|
|
526
425
|
if (nextFocusable?.component?.isMounted?.()) {
|
|
527
426
|
const nextRect = nextFocusable.component.getRect();
|
|
528
427
|
|
|
@@ -619,7 +518,5 @@ export const focusManager = (function () {
|
|
|
619
518
|
enableAutomaticFocus,
|
|
620
519
|
getGroupRootById,
|
|
621
520
|
isGroupItemFocused,
|
|
622
|
-
setFocusOnMenu,
|
|
623
|
-
setFocusOnContent,
|
|
624
521
|
};
|
|
625
522
|
})();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as R from "ramda";
|
|
2
2
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
3
3
|
|
|
4
4
|
import { rejectItemByPath } from "../../../../objectUtils";
|
|
@@ -143,49 +143,7 @@ const hasChildren = (target) =>
|
|
|
143
143
|
|
|
144
144
|
const hasComponent = (target) => !!target?.component;
|
|
145
145
|
|
|
146
|
-
const hasComponentAndChildren = allPass([hasComponent, hasChildren]);
|
|
147
|
-
|
|
148
|
-
const positiveOrZero = (value) => value >= 0;
|
|
149
|
-
|
|
150
|
-
const excludeFromFocusSearching = pathOr(false, [
|
|
151
|
-
"component",
|
|
152
|
-
"props",
|
|
153
|
-
"excludeFromFocusSearching",
|
|
154
|
-
]);
|
|
155
|
-
|
|
156
|
-
// const isAllScreens = all(excludeFromFocusSearching);
|
|
157
|
-
|
|
158
|
-
const isNavBar = (target) => startsWith("navbar", target?.id);
|
|
159
|
-
|
|
160
|
-
const ignoreMenuIfMultipleTargets = (nodes) => {
|
|
161
|
-
if (nodes.length > 1 && nodes.map((n) => n?.target?.id).includes("navbar")) {
|
|
162
|
-
return nodes.filter((n) => !isNavBar(n?.target));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return nodes;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const toDistance = ({ target, direction, comparisonRect }) => {
|
|
169
|
-
const componentRect = target.component?.getRect();
|
|
170
|
-
|
|
171
|
-
const orientationDistance = measureDistance(
|
|
172
|
-
direction,
|
|
173
|
-
comparisonRect,
|
|
174
|
-
componentRect
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
if (orientationDistance === -1) {
|
|
178
|
-
// ignores offscreen targets when moving up/down
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const euclidDistance = measureEuclidianDistanceToCenter(
|
|
183
|
-
comparisonRect,
|
|
184
|
-
componentRect
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
return orientationDistance > 0 ? euclidDistance : orientationDistance;
|
|
188
|
-
};
|
|
146
|
+
const hasComponentAndChildren = R.allPass([hasComponent, hasChildren]);
|
|
189
147
|
|
|
190
148
|
/**
|
|
191
149
|
* Get target in shortest distance in specified direction
|
|
@@ -215,32 +173,52 @@ export function getTargetInDirection(
|
|
|
215
173
|
|
|
216
174
|
const distances = targets
|
|
217
175
|
.filter(hasComponentAndChildren)
|
|
218
|
-
.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
176
|
+
.map((target) => {
|
|
177
|
+
const componentRect = target.component?.getRect();
|
|
178
|
+
|
|
179
|
+
const distance = measureDistance(
|
|
180
|
+
direction,
|
|
181
|
+
comparisonRect,
|
|
182
|
+
componentRect
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
if (distance === -1) {
|
|
186
|
+
// ignores offscreen targets when moving up/down
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
224
189
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
190
|
+
const orientationDistance = measureDistance(
|
|
191
|
+
direction,
|
|
192
|
+
comparisonRect,
|
|
193
|
+
componentRect
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const euclidDistance = measureEuclidianDistanceToCenter(
|
|
197
|
+
comparisonRect,
|
|
198
|
+
componentRect
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
distance:
|
|
203
|
+
orientationDistance > 0 ? euclidDistance : orientationDistance,
|
|
204
|
+
target,
|
|
205
|
+
};
|
|
206
|
+
})
|
|
207
|
+
.filter((obj) => obj && obj.distance >= 0);
|
|
228
208
|
|
|
229
209
|
const match = distances
|
|
230
210
|
.sort((a, b) => a.distance - b.distance) // sorts the distances
|
|
231
211
|
.filter(ignoreMenuIfMultipleTargets)[0]; // filters out multiple targets if necessary // gets the first element
|
|
232
212
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
return match?.target;
|
|
213
|
+
return distances && distances.length > 0 ? match.target : null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function ignoreMenuIfMultipleTargets(nodes) {
|
|
217
|
+
if (nodes.length > 1 && nodes.map((n) => n?.target?.id).includes("navbar")) {
|
|
218
|
+
return nodes.filter((n) => n?.target?.id !== "navbar");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return nodes;
|
|
244
222
|
}
|
|
245
223
|
|
|
246
224
|
/**
|
|
@@ -331,36 +309,3 @@ export function hasPriorityInDirection(direction, component) {
|
|
|
331
309
|
|
|
332
310
|
return (prioritiseFocusOn & mappedDirection) > 0;
|
|
333
311
|
}
|
|
334
|
-
|
|
335
|
-
export const getNodeBeforeRoot = (node) => {
|
|
336
|
-
if (isNil(node)) {
|
|
337
|
-
return undefined;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (node.parent.id === "root") {
|
|
341
|
-
return node;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return getNodeBeforeRoot(node.parent);
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
export const isAllowedToMoveTo = (currentNode, nextNode) => {
|
|
348
|
-
const screenForCurrentNode = getNodeBeforeRoot(currentNode);
|
|
349
|
-
const screenForNextNode = getNodeBeforeRoot(nextNode);
|
|
350
|
-
|
|
351
|
-
return screenForCurrentNode.id === screenForNextNode.id;
|
|
352
|
-
|
|
353
|
-
// console.log("debug_2", "isAllowedToMoveTo", {
|
|
354
|
-
// nodeBeforeRoot,
|
|
355
|
-
// });
|
|
356
|
-
|
|
357
|
-
// if (nodeBeforeRoot?.id === "navbar") {
|
|
358
|
-
// return true;
|
|
359
|
-
// }
|
|
360
|
-
|
|
361
|
-
// if (nodeBeforeRoot?.component?.props?.isActive) {
|
|
362
|
-
// return true;
|
|
363
|
-
// }
|
|
364
|
-
|
|
365
|
-
// return false;
|
|
366
|
-
};
|
package/headersUtils/index.ts
CHANGED
|
@@ -28,5 +28,6 @@ export const getAppDataHeaders = (screenId: string) => {
|
|
|
28
28
|
"x-applicaster-signed-device-info-token": appData.signedDeviceInfoToken,
|
|
29
29
|
"x-applicaster-device-make": appData.deviceMake,
|
|
30
30
|
"x-applicaster-device-model": appData.deviceModel,
|
|
31
|
+
"x-applicaster-uuid": appData?.uuid,
|
|
31
32
|
};
|
|
32
33
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "12.0.0-alpha.
|
|
3
|
+
"version": "12.0.0-alpha.4945500007",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "12.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "12.0.0-alpha.4945500007",
|
|
32
32
|
"buffer": "^5.2.1",
|
|
33
33
|
"camelize": "^1.0.0",
|
|
34
34
|
"create-react-context": "^0.2.3",
|
|
@@ -496,18 +496,3 @@ export const useIsScreenActive = () => {
|
|
|
496
496
|
|
|
497
497
|
return pathname === currentRoute;
|
|
498
498
|
};
|
|
499
|
-
|
|
500
|
-
type Return = (prefix: string) => string;
|
|
501
|
-
|
|
502
|
-
export const useUniqueRouteSuffix = (): Return => {
|
|
503
|
-
const pathname = usePathname();
|
|
504
|
-
const { currentRoute } = useNavigation();
|
|
505
|
-
|
|
506
|
-
const suffix = `currentRoute:${currentRoute}_pathname:${pathname}`;
|
|
507
|
-
|
|
508
|
-
const withUniqueRouteSuffix = React.useCallback((prefix: string) => {
|
|
509
|
-
return `${prefix}___${suffix}`;
|
|
510
|
-
}, []);
|
|
511
|
-
|
|
512
|
-
return withUniqueRouteSuffix;
|
|
513
|
-
};
|
|
@@ -5,7 +5,6 @@ import { useNestedNavigationContext } from "@applicaster/zapp-react-native-ui-co
|
|
|
5
5
|
import { useNavigation } from "../navigation/useNavigation";
|
|
6
6
|
|
|
7
7
|
import { ScreenContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenContext";
|
|
8
|
-
import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/Components/Layout/TV";
|
|
9
8
|
|
|
10
9
|
export function useScreenContextV2() {
|
|
11
10
|
return useContext(ScreenContext);
|
|
@@ -46,8 +45,8 @@ export function useSetNavbarState() {
|
|
|
46
45
|
export function useScreenContext(): NavigationScreenData {
|
|
47
46
|
const navigator = useNavigation();
|
|
48
47
|
|
|
49
|
-
const
|
|
50
|
-
|
|
48
|
+
const screenKey = useMemo(() => navigator?.key, [navigator]);
|
|
49
|
+
const screenData = useMemo(() => navigator?.data, [screenKey]);
|
|
51
50
|
const modalScreenData = navigator?.modalData;
|
|
52
51
|
|
|
53
52
|
const isModal = useModalNavigationContext();
|
package/reactUtils/helpers.tsx
CHANGED
|
@@ -7,7 +7,7 @@ export const isAnyNil = R.any(R.isNil);
|
|
|
7
7
|
|
|
8
8
|
export const isNilOrEmpty = R.either(R.isNil, R.isEmpty);
|
|
9
9
|
|
|
10
|
-
export const isNotEmpty = R.
|
|
10
|
+
export const isNotEmpty = R.complement(R.isEmpty);
|
|
11
11
|
|
|
12
12
|
export const withoutNil = R.reject(R.isNil);
|
|
13
13
|
|
package/stringUtils/index.ts
CHANGED
|
@@ -152,3 +152,8 @@ export function objectToCamelCase(obj) {
|
|
|
152
152
|
export function isString(value: unknown): value is string {
|
|
153
153
|
return typeof value === "string" || value instanceof String;
|
|
154
154
|
}
|
|
155
|
+
|
|
156
|
+
/** Checks if a value is a non-empty string */
|
|
157
|
+
export function isNotEmptyString(value: unknown): value is string {
|
|
158
|
+
return isString(value) && value.length > 0;
|
|
159
|
+
}
|