haya-select 1.0.45 → 1.0.46
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/package.json +1 -1
- package/src/select/index.jsx +92 -66
package/package.json
CHANGED
package/src/select/index.jsx
CHANGED
|
@@ -2,13 +2,15 @@ import {anythingDifferent} from "set-state-compare/src/diff-utils"
|
|
|
2
2
|
import config from "../config.js"
|
|
3
3
|
import {dig, digg, digs} from "diggerize"
|
|
4
4
|
import debounce from "debounce"
|
|
5
|
+
import FontAwesomeIcon from "react-native-vector-icons/FontAwesome"
|
|
5
6
|
import idForComponent from "@kaspernj/api-maker/src/inputs/id-for-component.mjs"
|
|
6
7
|
import nameForComponent from "@kaspernj/api-maker/src/inputs/name-for-component.mjs"
|
|
7
8
|
import Option from "./option"
|
|
8
9
|
import OptionGroup from "./option-group"
|
|
9
10
|
import PropTypes from "prop-types"
|
|
10
11
|
import propTypesExact from "prop-types-exact"
|
|
11
|
-
import
|
|
12
|
+
import RenderHtml from "react-native-render-html"
|
|
13
|
+
import {memo, useEffect, useRef} from "react"
|
|
12
14
|
import {shapeComponent, ShapeComponent} from "set-state-compare/src/shape-component.js"
|
|
13
15
|
import {Platform, Pressable, Text, TextInput, View} from "react-native"
|
|
14
16
|
import useEventListener from "@kaspernj/api-maker/src/use-event-listener"
|
|
@@ -24,6 +26,7 @@ const nameForComponentWithMultiple = (component) => {
|
|
|
24
26
|
export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
25
27
|
static defaultProps = {
|
|
26
28
|
multiple: false,
|
|
29
|
+
optionsWidth: null,
|
|
27
30
|
search: false
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -42,6 +45,7 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
42
45
|
onChangeValue: PropTypes.func,
|
|
43
46
|
onOptionsClosed: PropTypes.func,
|
|
44
47
|
options: PropTypes.oneOfType([PropTypes.array, PropTypes.func]).isRequired,
|
|
48
|
+
optionsWidth: PropTypes.number,
|
|
45
49
|
placeholder: PropTypes.node,
|
|
46
50
|
search: PropTypes.bool.isRequired,
|
|
47
51
|
selectContainerStyle: PropTypes.object,
|
|
@@ -55,6 +59,7 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
55
59
|
values: PropTypes.array
|
|
56
60
|
})
|
|
57
61
|
|
|
62
|
+
callOptionsPositionAboveIfOutsideScreen = false
|
|
58
63
|
p = fetchingObject(() => this.props)
|
|
59
64
|
s = fetchingObject(() => this.state)
|
|
60
65
|
tt = fetchingObject(this)
|
|
@@ -73,27 +78,38 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
73
78
|
this.setInstance({
|
|
74
79
|
endOfSelectRef: useRef(),
|
|
75
80
|
optionsContainerRef: useRef(),
|
|
76
|
-
currentSelectedRef: useRef(),
|
|
77
81
|
searchTextInputRef: useRef(),
|
|
78
82
|
t
|
|
79
83
|
})
|
|
80
84
|
this.useStates({
|
|
81
85
|
currentOptions: () => this.defaultCurrentOptions(),
|
|
86
|
+
selectContainerLayout: null,
|
|
87
|
+
endOfSelectLayout: null,
|
|
82
88
|
loadedOptions: () => this.defaultLoadedOptions(),
|
|
83
89
|
opened: false,
|
|
84
|
-
|
|
90
|
+
optionsContainerLayout: null,
|
|
85
91
|
optionsPlacement: undefined,
|
|
86
92
|
optionsTop: undefined,
|
|
87
93
|
optionsVisibility: undefined,
|
|
88
94
|
optionsWidth: undefined,
|
|
95
|
+
scrollLeft: document.documentElement.scrollLeft,
|
|
96
|
+
scrollTop: document.documentElement.scrollTop,
|
|
89
97
|
searchText: "",
|
|
90
|
-
scrollLeft: undefined,
|
|
91
|
-
scrollTop: undefined,
|
|
92
98
|
toggled: () => this.defaultToggled()
|
|
93
99
|
})
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
|
|
101
|
+
if (Platform.OS == "web") {
|
|
102
|
+
useEventListener(window, "click", this.tt.onWindowClicked)
|
|
103
|
+
useEventListener(window, "resize", this.tt.onAnythingResizedDebounced)
|
|
104
|
+
useEventListener(window, "scroll", this.tt.onAnythingScrolledDebounced)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (this.tt.callOptionsPositionAboveIfOutsideScreen && this.s.optionsContainerLayout) {
|
|
109
|
+
this.callOptionsPositionAboveIfOutsideScreen = false
|
|
110
|
+
this.setOptionsPositionAboveIfOutsideScreen()
|
|
111
|
+
}
|
|
112
|
+
}, [this.tt.callOptionsPositionAboveIfOutsideScreen, this.s.optionsContainerLayout])
|
|
97
113
|
}
|
|
98
114
|
|
|
99
115
|
defaultCurrentOptions() {
|
|
@@ -126,7 +142,7 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
126
142
|
getValues = () => ("values" in this.props) ? this.p.values : this.s.currentOptions.map((currentOption) => currentOption.value)
|
|
127
143
|
getCurrentOptions = () => {
|
|
128
144
|
if ("values" in this.props && typeof this.props.values != "undefined") {
|
|
129
|
-
if (Array.isArray(this.props.options)) {
|
|
145
|
+
if (Array.isArray(this.props.options) && this.props.values) {
|
|
130
146
|
return this.p.values.map((value) => this.p.options.find((option) => option.value == value))
|
|
131
147
|
} else if (this.s.loadedOptions || typeof this.props.options == "function" && this.props.values) {
|
|
132
148
|
if (this.s.loadedOptions) {
|
|
@@ -151,7 +167,7 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
151
167
|
} else {
|
|
152
168
|
this.setCurrentFromGivenValues()
|
|
153
169
|
}
|
|
154
|
-
} else {
|
|
170
|
+
} else if (this.props.values) {
|
|
155
171
|
return this.p.values.map((value) => ({value}))
|
|
156
172
|
}
|
|
157
173
|
}
|
|
@@ -192,7 +208,7 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
192
208
|
}
|
|
193
209
|
|
|
194
210
|
render() {
|
|
195
|
-
const {
|
|
211
|
+
const {endOfSelectRef} = this.tt
|
|
196
212
|
const {className, placeholder, toggleOptions} = this.props
|
|
197
213
|
const {opened, optionsPlacement} = this.s
|
|
198
214
|
const currentOptions = this.getCurrentOptions()
|
|
@@ -257,12 +273,12 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
257
273
|
}
|
|
258
274
|
<Pressable
|
|
259
275
|
dataSet={{class: "select-container"}}
|
|
276
|
+
onLayout={this.tt.onSelectContainerLayout}
|
|
260
277
|
onPress={this.tt.onSelectClicked}
|
|
261
278
|
style={selectContainerStyleActual}
|
|
262
279
|
>
|
|
263
280
|
<View
|
|
264
281
|
dataSet={{class: "current-selected"}}
|
|
265
|
-
ref={currentSelectedRef}
|
|
266
282
|
style={this.stylingFor("currentSelected", {width: "calc(100% - 27px)", flexWrap: "wrap"})}
|
|
267
283
|
>
|
|
268
284
|
{opened &&
|
|
@@ -284,7 +300,7 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
284
300
|
{!opened &&
|
|
285
301
|
<>
|
|
286
302
|
{currentOptions.length == 0 &&
|
|
287
|
-
<Text style={this.stylingFor("nothingSelected", {color: "grey"})}>
|
|
303
|
+
<Text numberOfLines={1} style={this.stylingFor("nothingSelected", {color: "grey"})}>
|
|
288
304
|
{placeholder || this.t(".nothing_selected")}
|
|
289
305
|
</Text>
|
|
290
306
|
}
|
|
@@ -348,7 +364,7 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
348
364
|
</Text>
|
|
349
365
|
</View>
|
|
350
366
|
</Pressable>
|
|
351
|
-
<View ref={endOfSelectRef} />
|
|
367
|
+
<View onLayout={this.tt.onEndOfSelectLayout} ref={endOfSelectRef} />
|
|
352
368
|
</View>
|
|
353
369
|
)
|
|
354
370
|
}
|
|
@@ -425,6 +441,10 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
425
441
|
this.setState({loadedOptions})
|
|
426
442
|
}
|
|
427
443
|
|
|
444
|
+
onSelectContainerLayout = (e) => this.setState({selectContainerLayout: Object.assign({}, digg(e, "nativeEvent", "layout"))})
|
|
445
|
+
onEndOfSelectLayout = (e) => this.setState({endOfSelectLayout: Object.assign({}, digg(e, "nativeEvent", "layout"))})
|
|
446
|
+
onOptionsContainerLayout = (e) => this.setState({optionsContainerLayout: Object.assign({}, digg(e, "nativeEvent", "layout"))})
|
|
447
|
+
|
|
428
448
|
onSelectClicked = (e) => {
|
|
429
449
|
e.preventDefault()
|
|
430
450
|
e.stopPropagation()
|
|
@@ -443,7 +463,8 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
443
463
|
closeOptions() {
|
|
444
464
|
this.setState({
|
|
445
465
|
loadedOptions: undefined,
|
|
446
|
-
opened: false
|
|
466
|
+
opened: false,
|
|
467
|
+
optionsContainerLayout: null
|
|
447
468
|
})
|
|
448
469
|
|
|
449
470
|
if (this.props.onOptionsClosed) {
|
|
@@ -456,11 +477,16 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
456
477
|
openOptions() {
|
|
457
478
|
this.setOptionsPositionBelow()
|
|
458
479
|
this.loadOptions()
|
|
480
|
+
this.callOptionsPositionAboveIfOutsideScreen = true
|
|
459
481
|
this.setState(
|
|
460
|
-
{
|
|
482
|
+
{
|
|
483
|
+
opened: true,
|
|
484
|
+
scrollLeft: Platform.OS == "web" ? document.documentElement.scrollLeft : null,
|
|
485
|
+
scrollTop: Platform.OS == "web" ? document.documentElement.scrollTop : null,
|
|
486
|
+
searchText: ""
|
|
487
|
+
},
|
|
461
488
|
() => {
|
|
462
489
|
this.focusTextInput()
|
|
463
|
-
this.setOptionsPositionAboveIfOutsideScreen()
|
|
464
490
|
}
|
|
465
491
|
)
|
|
466
492
|
}
|
|
@@ -472,19 +498,17 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
472
498
|
return // Debounce after un-mount handeling.
|
|
473
499
|
}
|
|
474
500
|
|
|
501
|
+
this.callOptionsPositionAboveIfOutsideScreen = true
|
|
475
502
|
this.setOptionsPositionBelow()
|
|
476
|
-
this.setOptionsPositionAboveIfOutsideScreen()
|
|
477
503
|
}
|
|
478
504
|
|
|
479
505
|
setOptionsPositionAboveIfOutsideScreen() {
|
|
480
|
-
const {
|
|
481
|
-
const
|
|
482
|
-
const
|
|
483
|
-
const
|
|
484
|
-
const optionsTotalHeight = optionsHeight + optionsTop + scrollTop
|
|
485
|
-
const windowHeightWithScroll = window.innerHeight + scrollTop
|
|
506
|
+
const {optionsContainerLayout} = this.s
|
|
507
|
+
const optionsTop = this.s.endOfSelectLayout.top
|
|
508
|
+
const optionsTotalBottomPosition = optionsContainerLayout.height + optionsTop
|
|
509
|
+
const windowHeightWithScroll = document.body.clientHeight + this.s.scrollTop
|
|
486
510
|
|
|
487
|
-
if (windowHeightWithScroll <
|
|
511
|
+
if (windowHeightWithScroll < optionsTotalBottomPosition) {
|
|
488
512
|
this.setOptionsPositionAbove()
|
|
489
513
|
} else {
|
|
490
514
|
this.setState({optionsVisibility: "visible"})
|
|
@@ -492,49 +516,32 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
492
516
|
}
|
|
493
517
|
|
|
494
518
|
setOptionsPositionAbove() {
|
|
495
|
-
const {
|
|
496
|
-
const optionsHeight = digg(optionsContainerRef, "current", "offsetHeight")
|
|
497
|
-
const position = currentSelectedRef.current.getBoundingClientRect()
|
|
498
|
-
const {top} = digs(position, "top")
|
|
499
|
-
const optionsTop = top - optionsHeight + 2
|
|
500
|
-
const {left, width} = digs(endOfSelectRef.current.getBoundingClientRect(), "left", "width")
|
|
519
|
+
const {endOfSelectLayout} = this.s
|
|
501
520
|
|
|
502
521
|
this.setState(
|
|
503
522
|
{
|
|
504
523
|
opened: true,
|
|
505
|
-
optionsLeft: left,
|
|
506
524
|
optionsPlacement: "above",
|
|
507
|
-
optionsTop,
|
|
508
525
|
optionsVisibility: "visible",
|
|
509
|
-
optionsWidth: width
|
|
510
|
-
scrollLeft: document.documentElement.scrollLeft,
|
|
511
|
-
scrollTop: document.documentElement.scrollTop
|
|
526
|
+
optionsWidth: endOfSelectLayout.width
|
|
512
527
|
},
|
|
513
528
|
() => this.focusTextInput()
|
|
514
529
|
)
|
|
515
530
|
}
|
|
516
531
|
|
|
517
532
|
setOptionsPositionBelow() {
|
|
518
|
-
const {endOfSelectRef} = this.tt
|
|
519
|
-
const position = endOfSelectRef.current.getBoundingClientRect()
|
|
520
|
-
const {left, top, width} = digs(position, "left", "top", "width")
|
|
521
|
-
|
|
522
533
|
this.setState(
|
|
523
534
|
{
|
|
524
535
|
opened: true,
|
|
525
|
-
optionsLeft: left,
|
|
526
536
|
optionsPlacement: "below",
|
|
527
|
-
optionsTop: top,
|
|
528
537
|
optionsVisibility: "hidden",
|
|
529
|
-
optionsWidth: width
|
|
530
|
-
scrollLeft: document.documentElement.scrollLeft,
|
|
531
|
-
scrollTop: document.documentElement.scrollTop
|
|
538
|
+
optionsWidth: this.s.endOfSelectLayout.width
|
|
532
539
|
},
|
|
533
540
|
() => this.focusTextInput()
|
|
534
541
|
)
|
|
535
542
|
}
|
|
536
543
|
|
|
537
|
-
onAnythingResized = (
|
|
544
|
+
onAnythingResized = () => {
|
|
538
545
|
if (this.s.opened) {
|
|
539
546
|
this.setOptionsPosition()
|
|
540
547
|
}
|
|
@@ -542,8 +549,12 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
542
549
|
|
|
543
550
|
onAnythingResizedDebounced = debounce(this.tt.onAnythingResized, 25)
|
|
544
551
|
|
|
545
|
-
onAnythingScrolled = (
|
|
552
|
+
onAnythingScrolled = () => {
|
|
546
553
|
if (this.s.opened) {
|
|
554
|
+
this.setState({
|
|
555
|
+
scrollLeft: document.documentElement.scrollLeft,
|
|
556
|
+
scrollTop: document.documentElement.scrollTop
|
|
557
|
+
})
|
|
547
558
|
this.setOptionsPosition()
|
|
548
559
|
}
|
|
549
560
|
}
|
|
@@ -562,26 +573,38 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
562
573
|
|
|
563
574
|
optionsContainer() {
|
|
564
575
|
const {optionsContainerRef} = this.tt
|
|
565
|
-
const {
|
|
576
|
+
const {selectContainerLayout, loadedOptions, endOfSelectLayout, optionsContainerLayout, optionsPlacement, optionsVisibility} = this.s
|
|
577
|
+
let left, top
|
|
578
|
+
|
|
579
|
+
if (optionsPlacement == "below") {
|
|
580
|
+
left = this.s.endOfSelectLayout.left
|
|
581
|
+
top = this.s.endOfSelectLayout.top - 2
|
|
582
|
+
} else if (optionsPlacement == "above") {
|
|
583
|
+
left = endOfSelectLayout.left
|
|
584
|
+
top = selectContainerLayout.top - optionsContainerLayout.height + 1
|
|
585
|
+
} else {
|
|
586
|
+
throw new Error(`Unkonwn options placement: ${optionsPlacement}`)
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const style = this.stylingFor("optionsContainer", {
|
|
590
|
+
position: "absolute",
|
|
591
|
+
left,
|
|
592
|
+
top,
|
|
593
|
+
zIndex: 99999,
|
|
594
|
+
visibility: optionsVisibility,
|
|
595
|
+
width: this.p.optionsWidth || this.s.optionsWidth,
|
|
596
|
+
backgroundColor: "#fff",
|
|
597
|
+
border: "1px solid #999",
|
|
598
|
+
maxHeight: 300,
|
|
599
|
+
overflowY: "auto"
|
|
600
|
+
})
|
|
566
601
|
|
|
567
602
|
return (
|
|
568
603
|
<View
|
|
569
604
|
dataSet={{class: "options-container", id: idForComponent(this)}}
|
|
605
|
+
onLayout={this.onOptionsContainerLayout}
|
|
570
606
|
ref={optionsContainerRef}
|
|
571
|
-
style={
|
|
572
|
-
this.stylingFor("optionsContainer", {
|
|
573
|
-
position: "absolute",
|
|
574
|
-
left: optionsLeft + scrollLeft,
|
|
575
|
-
top: optionsTop + scrollTop - 1,
|
|
576
|
-
zIndex: 99999,
|
|
577
|
-
visibility: optionsVisibility,
|
|
578
|
-
width: optionsWidth,
|
|
579
|
-
backgroundColor: "#fff",
|
|
580
|
-
border: "1px solid #999",
|
|
581
|
-
maxHeight: 300,
|
|
582
|
-
overflowY: "auto"
|
|
583
|
-
})
|
|
584
|
-
}
|
|
607
|
+
style={style}
|
|
585
608
|
>
|
|
586
609
|
{loadedOptions?.map((loadedOption) =>
|
|
587
610
|
this.hayaSelectOption({
|
|
@@ -720,18 +743,21 @@ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
|
|
|
720
743
|
}}
|
|
721
744
|
>
|
|
722
745
|
{toggleOptions && !(currentValue.value in toggled) &&
|
|
723
|
-
<
|
|
746
|
+
<View dataSet={{class: "toggle-icon-placeholder"}} style={{width: 25}} />
|
|
724
747
|
}
|
|
725
748
|
{toggleOptions && (currentValue.value in toggled) &&
|
|
726
|
-
<
|
|
749
|
+
<FontAwesomeIcon dataSet={{class: "toggle-icon"}} name={icon} style={{width: 25}} />
|
|
727
750
|
}
|
|
728
751
|
{currentValue.content}
|
|
729
752
|
{!currentValue.content &&
|
|
730
|
-
<Text
|
|
753
|
+
<Text dataSet={{class: "option-presentation-text"}} style={this.stylingFor("optionPresentationText", {whiteSpace: "nowrap"})}>
|
|
731
754
|
{currentValue.text}
|
|
732
755
|
</Text>
|
|
733
756
|
}
|
|
734
|
-
{("html" in currentValue) &&
|
|
757
|
+
{("html" in currentValue) && Platform.OS != "web" &&
|
|
758
|
+
<RenderHtml source={{html: digg(currentValue, "html")}} />
|
|
759
|
+
}
|
|
760
|
+
{("html" in currentValue) && Platform.OS == "web" &&
|
|
735
761
|
<div dangerouslySetInnerHTML={{__html: digg(currentValue, "html")}} />
|
|
736
762
|
}
|
|
737
763
|
</View>
|