@lightningtv/solid 0.0.3 → 0.0.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/dist/esm/index.js +466 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/source/index.js +1 -0
- package/dist/source/primitives/announcer/announcer.js +121 -0
- package/dist/source/primitives/announcer/index.js +8 -0
- package/dist/source/primitives/announcer/speech.js +152 -0
- package/dist/source/primitives/createInfiniteItems.js +45 -0
- package/dist/source/primitives/createSpriteMap.js +17 -0
- package/dist/source/primitives/index.js +5 -0
- package/dist/source/primitives/useFocusManager.js +88 -0
- package/dist/source/primitives/withPadding.js +48 -0
- package/dist/source/solidOpts.js +2 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/primitives/announcer/announcer.d.ts +37 -0
- package/dist/types/primitives/announcer/index.d.ts +2 -0
- package/dist/types/primitives/announcer/speech.d.ts +10 -0
- package/dist/types/primitives/createInfiniteItems.d.ts +27 -0
- package/dist/types/primitives/createSpriteMap.d.ts +8 -0
- package/dist/types/primitives/index.d.ts +5 -0
- package/dist/types/primitives/useFocusManager.d.ts +46 -0
- package/dist/types/primitives/withPadding.d.ts +3 -0
- package/package.json +5 -2
- package/src/index.ts +1 -0
- package/src/primitives/announcer/announcer.ts +190 -0
- package/src/primitives/announcer/index.ts +10 -0
- package/src/primitives/announcer/speech.ts +174 -0
- package/src/primitives/createInfiniteItems.ts +66 -0
- package/src/primitives/createSpriteMap.ts +31 -0
- package/src/primitives/index.ts +5 -0
- package/src/primitives/jsx-runtime.d.ts +11 -0
- package/src/primitives/useFocusManager.ts +194 -0
- package/src/primitives/withPadding.ts +56 -0
- package/src/solidOpts.ts +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { createSignal, mergeProps as mergeProps$1, createRoot, createRenderEffect, createMemo, createComponent as createComponent$1, untrack, splitProps } from 'solid-js';
|
|
1
|
+
import { createSignal, mergeProps as mergeProps$1, createRoot, createRenderEffect, createMemo, createComponent as createComponent$1, untrack, splitProps, createEffect, on, createResource, createComputed, batch } from 'solid-js';
|
|
2
2
|
export { ErrorBoundary, For, Index, Match, Show, Suspense, SuspenseList, Switch } from 'solid-js';
|
|
3
|
-
import { Config, isInteger, ElementNode,
|
|
3
|
+
import { Config, isInteger, ElementNode, NodeType, log, startLightningRenderer } from '@lightningtv/core';
|
|
4
4
|
export * from '@lightningtv/core';
|
|
5
|
-
import { createElement as createElement$1, spread as spread$1 } from '@lightningtv/solid';
|
|
5
|
+
import { createElement as createElement$1, spread as spread$1, isArray, activeElement as activeElement$1, isFunc, renderer as renderer$1 } from '@lightningtv/solid';
|
|
6
|
+
import { useKeyDownEvent } from '@solid-primitives/keyboard';
|
|
7
|
+
import { debounce } from '@solid-primitives/scheduled';
|
|
6
8
|
|
|
7
9
|
const [activeElement, setActiveElement] = createSignal(undefined);
|
|
8
10
|
Config.setActiveElement = setActiveElement;
|
|
@@ -335,7 +337,7 @@ var nodeOpts = {
|
|
|
335
337
|
createTextNode(text) {
|
|
336
338
|
// A text node is just a string - not the <text> node
|
|
337
339
|
return {
|
|
338
|
-
type:
|
|
340
|
+
type: NodeType.Text,
|
|
339
341
|
text,
|
|
340
342
|
parent: undefined
|
|
341
343
|
};
|
|
@@ -458,5 +460,464 @@ function Dynamic(props) {
|
|
|
458
460
|
});
|
|
459
461
|
}
|
|
460
462
|
|
|
461
|
-
|
|
463
|
+
/**
|
|
464
|
+
* Generates a map of event handlers for each key in the KeyMap
|
|
465
|
+
*/
|
|
466
|
+
|
|
467
|
+
const keyMapEntries = {
|
|
468
|
+
ArrowLeft: 'Left',
|
|
469
|
+
ArrowRight: 'Right',
|
|
470
|
+
ArrowUp: 'Up',
|
|
471
|
+
ArrowDown: 'Down',
|
|
472
|
+
Enter: 'Enter',
|
|
473
|
+
l: 'Last',
|
|
474
|
+
' ': 'Space',
|
|
475
|
+
Backspace: 'Back',
|
|
476
|
+
Escape: 'Escape'
|
|
477
|
+
};
|
|
478
|
+
const [focusPath, setFocusPath] = createSignal([]);
|
|
479
|
+
const useFocusManager = userKeyMap => {
|
|
480
|
+
const keypressEvent = useKeyDownEvent();
|
|
481
|
+
if (userKeyMap) {
|
|
482
|
+
// Flatten the userKeyMap to a hash
|
|
483
|
+
for (const [key, value] of Object.entries(userKeyMap)) {
|
|
484
|
+
if (isArray(value)) {
|
|
485
|
+
value.forEach(v => {
|
|
486
|
+
keyMapEntries[v] = key;
|
|
487
|
+
});
|
|
488
|
+
} else {
|
|
489
|
+
keyMapEntries[value] = key;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
createEffect(on(activeElement$1, (currentFocusedElm, prevFocusedElm, prevFocusPath = []) => {
|
|
494
|
+
let current = currentFocusedElm;
|
|
495
|
+
const fp = [];
|
|
496
|
+
while (current) {
|
|
497
|
+
if (!current.states.has('focus')) {
|
|
498
|
+
current.states.add('focus');
|
|
499
|
+
isFunc(current.onFocus) && current.onFocus.call(current, currentFocusedElm, prevFocusedElm);
|
|
500
|
+
}
|
|
501
|
+
fp.push(current);
|
|
502
|
+
current = current.parent;
|
|
503
|
+
}
|
|
504
|
+
prevFocusPath.forEach(elm => {
|
|
505
|
+
if (!fp.includes(elm)) {
|
|
506
|
+
elm.states.remove('focus');
|
|
507
|
+
isFunc(elm.onBlur) && elm.onBlur.call(elm, currentFocusedElm, prevFocusedElm);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
setFocusPath(fp);
|
|
511
|
+
return fp;
|
|
512
|
+
}, {
|
|
513
|
+
defer: true
|
|
514
|
+
}));
|
|
515
|
+
createEffect(() => {
|
|
516
|
+
const e = keypressEvent();
|
|
517
|
+
if (e) {
|
|
518
|
+
// Search keyMap for the value of the pressed key or keyCode if value undefined
|
|
519
|
+
const mappedKeyEvent = keyMapEntries[e.key] || keyMapEntries[e.keyCode];
|
|
520
|
+
untrack(() => {
|
|
521
|
+
const fp = focusPath();
|
|
522
|
+
let finalFocusElm = undefined;
|
|
523
|
+
for (const elm of fp) {
|
|
524
|
+
finalFocusElm = finalFocusElm || elm;
|
|
525
|
+
if (mappedKeyEvent) {
|
|
526
|
+
const onKeyHandler = elm[`on${mappedKeyEvent}`];
|
|
527
|
+
if (isFunc(onKeyHandler)) {
|
|
528
|
+
if (onKeyHandler.call(elm, e, elm, finalFocusElm) === true) {
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
} else {
|
|
533
|
+
console.log(`Unhandled key event: ${e.key || e.keyCode}`);
|
|
534
|
+
}
|
|
535
|
+
if (isFunc(elm.onKeyPress)) {
|
|
536
|
+
if (elm.onKeyPress.call(elm, e, mappedKeyEvent, elm, finalFocusElm) === true) {
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return false;
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
return focusPath;
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
// To use with TS import withPadding and then put withPadding; on the next line to prevent tree shaking
|
|
549
|
+
function withPadding(el, padding) {
|
|
550
|
+
const pad = padding();
|
|
551
|
+
let top, left, right, bottom;
|
|
552
|
+
if (Array.isArray(pad)) {
|
|
553
|
+
// top right bottom left
|
|
554
|
+
if (pad.length === 2) {
|
|
555
|
+
top = bottom = pad[0];
|
|
556
|
+
left = right = pad[1];
|
|
557
|
+
} else if (pad.length === 3) {
|
|
558
|
+
top = pad[0];
|
|
559
|
+
left = right = pad[1];
|
|
560
|
+
bottom = pad[2];
|
|
561
|
+
} else {
|
|
562
|
+
[top, right, bottom, left] = pad;
|
|
563
|
+
}
|
|
564
|
+
} else {
|
|
565
|
+
top = right = bottom = left = pad;
|
|
566
|
+
}
|
|
567
|
+
el.onBeforeLayout = (node, size) => {
|
|
568
|
+
if (size) {
|
|
569
|
+
el.width = el.children.reduce((acc, c) => {
|
|
570
|
+
return acc + (c.width || 0);
|
|
571
|
+
}, 0) + left + right;
|
|
572
|
+
const firstChild = el.children[0];
|
|
573
|
+
if (firstChild) {
|
|
574
|
+
// set padding or marginLeft for flex
|
|
575
|
+
firstChild.x = left;
|
|
576
|
+
firstChild.marginLeft = left;
|
|
577
|
+
}
|
|
578
|
+
let maxHeight = 0;
|
|
579
|
+
el.children.forEach(c => {
|
|
580
|
+
c.y = top;
|
|
581
|
+
c.marginTop = top;
|
|
582
|
+
maxHeight = Math.max(maxHeight, c.height || 0);
|
|
583
|
+
});
|
|
584
|
+
el.height = maxHeight + top + bottom;
|
|
585
|
+
// let flex know we need to re-layout
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/* global SpeechSynthesisErrorEvent */
|
|
592
|
+
function flattenStrings(series = []) {
|
|
593
|
+
const flattenedSeries = [];
|
|
594
|
+
let i;
|
|
595
|
+
for (i = 0; i < series.length; i++) {
|
|
596
|
+
const s = series[i];
|
|
597
|
+
if (typeof s === 'string' && !s.includes('PAUSE-')) {
|
|
598
|
+
flattenedSeries.push(series[i]);
|
|
599
|
+
} else {
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// add a "word boundary" to ensure the Announcer doesn't automatically try to
|
|
604
|
+
// interpret strings that look like dates but are not actually dates
|
|
605
|
+
// for example, if "Rising Sun" and "1993" are meant to be two separate lines,
|
|
606
|
+
// when read together, "Sun 1993" is interpretted as "Sunday 1993"
|
|
607
|
+
return [flattenedSeries.join(',\b ')].concat(series.slice(i));
|
|
608
|
+
}
|
|
609
|
+
function delay(pause) {
|
|
610
|
+
return new Promise(resolve => {
|
|
611
|
+
setTimeout(resolve, pause);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Speak a string
|
|
617
|
+
*
|
|
618
|
+
* @param phrase Phrase to speak
|
|
619
|
+
* @param utterances An array which the new SpeechSynthesisUtterance instance representing this utterance will be appended
|
|
620
|
+
* @param lang Language to speak in
|
|
621
|
+
* @return {Promise<void>} Promise resolved when the utterance has finished speaking, and rejected if there's an error
|
|
622
|
+
*/
|
|
623
|
+
function speak(phrase, utterances, lang = 'en-US') {
|
|
624
|
+
const synth = window.speechSynthesis;
|
|
625
|
+
return new Promise((resolve, reject) => {
|
|
626
|
+
const utterance = new SpeechSynthesisUtterance(phrase);
|
|
627
|
+
utterance.lang = lang;
|
|
628
|
+
utterance.onend = () => {
|
|
629
|
+
resolve();
|
|
630
|
+
};
|
|
631
|
+
utterance.onerror = e => {
|
|
632
|
+
reject(e);
|
|
633
|
+
};
|
|
634
|
+
utterances.push(utterance);
|
|
635
|
+
synth.speak(utterance);
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
function speakSeries(series, lang, root = true) {
|
|
639
|
+
const synth = window.speechSynthesis;
|
|
640
|
+
const remainingPhrases = flattenStrings(Array.isArray(series) ? series : [series]);
|
|
641
|
+
const nestedSeriesResults = [];
|
|
642
|
+
/*
|
|
643
|
+
We hold this array of SpeechSynthesisUtterances in order to prevent them from being
|
|
644
|
+
garbage collected prematurely on STB hardware which can cause the 'onend' events of
|
|
645
|
+
utterances to not fire consistently.
|
|
646
|
+
*/
|
|
647
|
+
const utterances = [];
|
|
648
|
+
let active = true;
|
|
649
|
+
const seriesChain = (async () => {
|
|
650
|
+
try {
|
|
651
|
+
while (active && remainingPhrases.length) {
|
|
652
|
+
const phrase = await Promise.resolve(remainingPhrases.shift());
|
|
653
|
+
if (!active) {
|
|
654
|
+
// Exit
|
|
655
|
+
// Need to check this after the await in case it was cancelled in between
|
|
656
|
+
break;
|
|
657
|
+
} else if (typeof phrase === 'string' && phrase.includes('PAUSE-')) {
|
|
658
|
+
// Pause it
|
|
659
|
+
let pause = Number(phrase.split('PAUSE-')[1]) * 1000;
|
|
660
|
+
if (isNaN(pause)) {
|
|
661
|
+
pause = 0;
|
|
662
|
+
}
|
|
663
|
+
await delay(pause);
|
|
664
|
+
} else if (typeof phrase === 'string' && phrase.length) {
|
|
665
|
+
// Speak it
|
|
666
|
+
const totalRetries = 3;
|
|
667
|
+
let retriesLeft = totalRetries;
|
|
668
|
+
while (active && retriesLeft > 0) {
|
|
669
|
+
try {
|
|
670
|
+
await speak(phrase, utterances, lang);
|
|
671
|
+
retriesLeft = 0;
|
|
672
|
+
} catch (e) {
|
|
673
|
+
// eslint-disable-next-line no-undef
|
|
674
|
+
if (e instanceof SpeechSynthesisErrorEvent) {
|
|
675
|
+
if (e.error === 'network') {
|
|
676
|
+
retriesLeft--;
|
|
677
|
+
console.warn(`Speech synthesis network error. Retries left: ${retriesLeft}`);
|
|
678
|
+
await delay(500 * (totalRetries - retriesLeft));
|
|
679
|
+
} else if (e.error === 'canceled' || e.error === 'interrupted') {
|
|
680
|
+
// Cancel or interrupt error (ignore)
|
|
681
|
+
retriesLeft = 0;
|
|
682
|
+
} else {
|
|
683
|
+
throw new Error(`SpeechSynthesisErrorEvent: ${e.error}`);
|
|
684
|
+
}
|
|
685
|
+
} else {
|
|
686
|
+
throw e;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
} else if (typeof phrase === 'function') {
|
|
691
|
+
const seriesResult = speakSeries(phrase(), lang, false);
|
|
692
|
+
nestedSeriesResults.push(seriesResult);
|
|
693
|
+
await seriesResult.series;
|
|
694
|
+
} else if (Array.isArray(phrase)) {
|
|
695
|
+
// Speak it (recursively)
|
|
696
|
+
const seriesResult = speakSeries(phrase, lang, false);
|
|
697
|
+
nestedSeriesResults.push(seriesResult);
|
|
698
|
+
await seriesResult.series;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
} finally {
|
|
702
|
+
active = false;
|
|
703
|
+
}
|
|
704
|
+
})();
|
|
705
|
+
return {
|
|
706
|
+
series: seriesChain,
|
|
707
|
+
get active() {
|
|
708
|
+
return active;
|
|
709
|
+
},
|
|
710
|
+
append: toSpeak => {
|
|
711
|
+
remainingPhrases.push(toSpeak);
|
|
712
|
+
},
|
|
713
|
+
cancel: () => {
|
|
714
|
+
if (!active) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (root) {
|
|
718
|
+
synth.cancel();
|
|
719
|
+
}
|
|
720
|
+
nestedSeriesResults.forEach(nestedSeriesResults => {
|
|
721
|
+
nestedSeriesResults.cancel();
|
|
722
|
+
});
|
|
723
|
+
active = false;
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
let currentSeries;
|
|
728
|
+
function SpeechEngine (toSpeak, lang = 'en-US') {
|
|
729
|
+
currentSeries && currentSeries.cancel();
|
|
730
|
+
currentSeries = speakSeries(toSpeak, lang);
|
|
731
|
+
return currentSeries;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
let resetFocusPathTimer;
|
|
735
|
+
let prevFocusPath = [];
|
|
736
|
+
let currentlySpeaking;
|
|
737
|
+
let voiceOutDisabled = false;
|
|
738
|
+
const fiveMinutes = 300000;
|
|
739
|
+
function debounceWithFlush(callback, time) {
|
|
740
|
+
const trigger = debounce(callback, time);
|
|
741
|
+
let scopedValue;
|
|
742
|
+
const debounced = newValue => {
|
|
743
|
+
scopedValue = newValue;
|
|
744
|
+
trigger(newValue);
|
|
745
|
+
};
|
|
746
|
+
debounced.flush = () => {
|
|
747
|
+
trigger.clear();
|
|
748
|
+
callback(scopedValue);
|
|
749
|
+
};
|
|
750
|
+
debounced.clear = trigger.clear;
|
|
751
|
+
return debounced;
|
|
752
|
+
}
|
|
753
|
+
function getElmName(elm) {
|
|
754
|
+
return elm.id || elm.name;
|
|
755
|
+
}
|
|
756
|
+
function onFocusChangeCore(focusPath = []) {
|
|
757
|
+
if (!Announcer.onFocusChange || !Announcer.enabled) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const loaded = focusPath.every(elm => !elm.loading);
|
|
761
|
+
const focusDiff = focusPath.filter(elm => !prevFocusPath.includes(elm));
|
|
762
|
+
resetFocusPathTimer();
|
|
763
|
+
if (!loaded && Announcer.onFocusChange) {
|
|
764
|
+
Announcer.onFocusChange([]);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
prevFocusPath = focusPath.slice(0);
|
|
768
|
+
const toAnnounceText = [];
|
|
769
|
+
const toAnnounce = focusDiff.reduce((acc, elm) => {
|
|
770
|
+
if (elm.announce) {
|
|
771
|
+
acc.push([getElmName(elm), 'Announce', elm.announce]);
|
|
772
|
+
toAnnounceText.push(elm.announce);
|
|
773
|
+
} else if (elm.title) {
|
|
774
|
+
acc.push([getElmName(elm), 'Title', elm.title]);
|
|
775
|
+
toAnnounceText.push(elm.title);
|
|
776
|
+
} else {
|
|
777
|
+
acc.push([getElmName(elm), 'No Announce', '']);
|
|
778
|
+
}
|
|
779
|
+
return acc;
|
|
780
|
+
}, []);
|
|
781
|
+
focusDiff.reverse().reduce((acc, elm) => {
|
|
782
|
+
if (elm.announceContext) {
|
|
783
|
+
acc.push([getElmName(elm), 'Context', elm.announceContext]);
|
|
784
|
+
toAnnounceText.push(elm.announceContext);
|
|
785
|
+
} else {
|
|
786
|
+
acc.push([getElmName(elm), 'No Context', '']);
|
|
787
|
+
}
|
|
788
|
+
return acc;
|
|
789
|
+
}, toAnnounce);
|
|
790
|
+
if (Announcer.debug) {
|
|
791
|
+
console.table(toAnnounce);
|
|
792
|
+
}
|
|
793
|
+
if (toAnnounceText.length) {
|
|
794
|
+
return Announcer.speak(toAnnounceText.reduce((acc, val) => acc.concat(val), []));
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
function textToSpeech(toSpeak) {
|
|
798
|
+
if (voiceOutDisabled) {
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
return currentlySpeaking = SpeechEngine(toSpeak);
|
|
802
|
+
}
|
|
803
|
+
const Announcer = {
|
|
804
|
+
debug: false,
|
|
805
|
+
enabled: true,
|
|
806
|
+
cancel: function () {
|
|
807
|
+
currentlySpeaking && currentlySpeaking.cancel();
|
|
808
|
+
},
|
|
809
|
+
clearPrevFocus: function (depth = 0) {
|
|
810
|
+
prevFocusPath = prevFocusPath.slice(0, depth);
|
|
811
|
+
resetFocusPathTimer();
|
|
812
|
+
},
|
|
813
|
+
speak: function (text, {
|
|
814
|
+
append = false,
|
|
815
|
+
notification = false
|
|
816
|
+
} = {}) {
|
|
817
|
+
if (Announcer.onFocusChange && Announcer.enabled) {
|
|
818
|
+
Announcer.onFocusChange.flush();
|
|
819
|
+
if (append && currentlySpeaking && currentlySpeaking.active) {
|
|
820
|
+
currentlySpeaking.append(text);
|
|
821
|
+
} else {
|
|
822
|
+
Announcer.cancel();
|
|
823
|
+
textToSpeech(text);
|
|
824
|
+
}
|
|
825
|
+
if (notification) {
|
|
826
|
+
voiceOutDisabled = true;
|
|
827
|
+
currentlySpeaking?.series.finally(() => {
|
|
828
|
+
voiceOutDisabled = false;
|
|
829
|
+
Announcer.refresh();
|
|
830
|
+
}).catch(console.error);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return currentlySpeaking;
|
|
834
|
+
},
|
|
835
|
+
refresh: function (depth = 0) {
|
|
836
|
+
Announcer.clearPrevFocus(depth);
|
|
837
|
+
Announcer.onFocusChange && Announcer.onFocusChange(untrack(() => focusPath()));
|
|
838
|
+
},
|
|
839
|
+
setupTimers: function ({
|
|
840
|
+
focusDebounce = 400,
|
|
841
|
+
focusChangeTimeout = fiveMinutes
|
|
842
|
+
} = {}) {
|
|
843
|
+
Announcer.onFocusChange = debounceWithFlush(onFocusChangeCore, focusDebounce);
|
|
844
|
+
resetFocusPathTimer = debounceWithFlush(() => {
|
|
845
|
+
// Reset focus path for full announce
|
|
846
|
+
prevFocusPath = [];
|
|
847
|
+
}, focusChangeTimeout);
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
const useAnnouncer = () => {
|
|
852
|
+
Announcer.setupTimers();
|
|
853
|
+
createEffect(on(focusPath, Announcer.onFocusChange, {
|
|
854
|
+
defer: true
|
|
855
|
+
}));
|
|
856
|
+
return Announcer;
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
// Adopted from https://github.com/solidjs-community/solid-primitives/blob/main/packages/pagination/src/index.ts
|
|
860
|
+
// As we don't have intersection observer in Lightning, we can't use the original implementation
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Provides an easy way to implement infinite items.
|
|
864
|
+
*
|
|
865
|
+
* ```ts
|
|
866
|
+
* const [items, loader, { item, setItem, setItems, end, setEnd }] = createInfiniteScroll(fetcher);
|
|
867
|
+
* ```
|
|
868
|
+
* @param fetcher `(item: number) => Promise<T[]>`
|
|
869
|
+
* @return `items()` is an accessor contains array of contents
|
|
870
|
+
* @property `items.loading` is a boolean indicator for the loading state
|
|
871
|
+
* @property `items.error` contains any error encountered
|
|
872
|
+
* @method `page` is an accessor that contains page number
|
|
873
|
+
* @method `setPage` allows to manually change the page number
|
|
874
|
+
* @method `setItems` allows to manually change the contents of the item
|
|
875
|
+
* @method `end` is a boolean indicator for end of the item
|
|
876
|
+
* @method `setEnd` allows to manually change the end
|
|
877
|
+
*/
|
|
878
|
+
function createInfiniteItems(fetcher) {
|
|
879
|
+
const [items, setItems] = createSignal([]);
|
|
880
|
+
const [page, setPage] = createSignal(0);
|
|
881
|
+
const [end, setEnd] = createSignal(false);
|
|
882
|
+
const [contents] = createResource(page, fetcher);
|
|
883
|
+
createComputed(() => {
|
|
884
|
+
const content = contents();
|
|
885
|
+
if (!content) return;
|
|
886
|
+
batch(() => {
|
|
887
|
+
if (content.length === 0) setEnd(true);
|
|
888
|
+
setItems(p => [...p, ...content]);
|
|
889
|
+
});
|
|
890
|
+
});
|
|
891
|
+
return [items, {
|
|
892
|
+
page,
|
|
893
|
+
setPage,
|
|
894
|
+
setItems,
|
|
895
|
+
end,
|
|
896
|
+
setEnd
|
|
897
|
+
}];
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
function createSpriteMap(src, subTextures) {
|
|
901
|
+
const spriteMapTexture = renderer$1.createTexture('ImageTexture', {
|
|
902
|
+
src
|
|
903
|
+
});
|
|
904
|
+
return subTextures.reduce((acc, t) => {
|
|
905
|
+
const {
|
|
906
|
+
x,
|
|
907
|
+
y,
|
|
908
|
+
width,
|
|
909
|
+
height
|
|
910
|
+
} = t;
|
|
911
|
+
acc[t.name] = renderer$1.createTexture('SubTexture', {
|
|
912
|
+
texture: spriteMapTexture,
|
|
913
|
+
x,
|
|
914
|
+
y,
|
|
915
|
+
width,
|
|
916
|
+
height
|
|
917
|
+
});
|
|
918
|
+
return acc;
|
|
919
|
+
}, {});
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
export { Dynamic, Text, View, activeElement, createComponent, createElement, createInfiniteItems, createSpriteMap, createTextNode, deg2rad, effect, focusPath, hexColor, insert, insertNode, memo, mergeProps, render, renderSync, setActiveElement, setProp, spread, startLightning, use, useAnnouncer, useFocusManager, withPadding };
|
|
462
923
|
//# sourceMappingURL=index.js.map
|