haya-select 1.0.63 → 1.0.64

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.
@@ -0,0 +1,625 @@
1
+ import { anythingDifferent } from "set-state-compare/src/diff-utils";
2
+ import { dig, digg } from "diggerize";
3
+ import debounce from "debounce";
4
+ import FontAwesomeIcon from "react-native-vector-icons/FontAwesome";
5
+ import idForComponent from "@kaspernj/api-maker/build/inputs/id-for-component";
6
+ import nameForComponent from "@kaspernj/api-maker/build/inputs/name-for-component";
7
+ import Option from "./option";
8
+ import OptionGroup from "./option-group";
9
+ import PropTypes from "prop-types";
10
+ import propTypesExact from "prop-types-exact";
11
+ import RenderHtml from "react-native-render-html";
12
+ import { Portal } from "conjointment";
13
+ import { memo, useEffect, useRef } from "react";
14
+ import { shapeComponent, ShapeComponent } from "set-state-compare/src/shape-component.js";
15
+ import { Platform, Pressable, Text, TextInput, View } from "react-native";
16
+ import useEventListener from "@kaspernj/api-maker/build/use-event-listener";
17
+ const nameForComponentWithMultiple = (component) => {
18
+ let name = nameForComponent(component);
19
+ const currentOptions = component.getCurrentOptions();
20
+ if (component.props.multiple && name && currentOptions.length > 0) {
21
+ name += "[]";
22
+ }
23
+ return name;
24
+ };
25
+ export default memo(shapeComponent(class HayaSelect extends ShapeComponent {
26
+ static defaultProps = {
27
+ multiple: false,
28
+ optionsAbsolute: true,
29
+ optionsWidth: null,
30
+ search: false
31
+ };
32
+ static propTypes = propTypesExact({
33
+ attribute: PropTypes.string,
34
+ className: PropTypes.string,
35
+ defaultToggled: PropTypes.object,
36
+ defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
37
+ defaultValues: PropTypes.array,
38
+ defaultValuesFromOptions: PropTypes.array,
39
+ id: PropTypes.node,
40
+ model: PropTypes.object,
41
+ multiple: PropTypes.bool.isRequired,
42
+ name: PropTypes.string,
43
+ onChange: PropTypes.func,
44
+ onChangeValue: PropTypes.func,
45
+ onOptionsClosed: PropTypes.func,
46
+ options: PropTypes.oneOfType([PropTypes.array, PropTypes.func]).isRequired,
47
+ optionsAbsolute: PropTypes.bool.isRequired,
48
+ optionsWidth: PropTypes.number,
49
+ placeholder: PropTypes.node,
50
+ search: PropTypes.bool.isRequired,
51
+ selectContainerStyle: PropTypes.object,
52
+ styles: PropTypes.object,
53
+ toggled: PropTypes.object,
54
+ toggleOptions: PropTypes.arrayOf(PropTypes.shape({
55
+ icon: PropTypes.string.isRequired,
56
+ label: PropTypes.string.isRequired,
57
+ value: PropTypes.string.isRequired
58
+ })),
59
+ values: PropTypes.array
60
+ });
61
+ callOptionsPositionAboveIfOutsideScreen = false;
62
+ p = fetchingObject(() => this.props);
63
+ s = fetchingObject(() => this.state);
64
+ tt = fetchingObject(this);
65
+ setup() {
66
+ const { t } = useI18n({ namespace: "haya_select" });
67
+ if (this.props.values) {
68
+ for (const value of this.props.values) {
69
+ if (typeof value == "undefined") {
70
+ throw new Error("HayaSelect: Undefined given as value");
71
+ }
72
+ }
73
+ }
74
+ this.setInstance({
75
+ endOfSelectRef: useRef(),
76
+ optionsContainerRef: useRef(),
77
+ searchTextInputRef: useRef(),
78
+ selectContainerRef: useRef(),
79
+ t
80
+ });
81
+ this.useStates({
82
+ currentOptions: () => this.defaultCurrentOptions(),
83
+ selectContainerLayout: null,
84
+ endOfSelectLayout: null,
85
+ height: null,
86
+ loadedOptions: () => this.defaultLoadedOptions(),
87
+ opened: false,
88
+ optionsContainerLayout: null,
89
+ optionsPlacement: undefined,
90
+ optionsTop: undefined,
91
+ optionsVisibility: undefined,
92
+ optionsWidth: undefined,
93
+ scrollLeft: document.documentElement.scrollLeft,
94
+ scrollTop: document.documentElement.scrollTop,
95
+ searchText: "",
96
+ toggled: () => this.defaultToggled()
97
+ });
98
+ if (Platform.OS == "web") {
99
+ useEventListener(window, "click", this.tt.onWindowClicked);
100
+ useEventListener(window, "resize", this.tt.onAnythingResizedDebounced);
101
+ useEventListener(window, "scroll", this.tt.onAnythingScrolledDebounced);
102
+ }
103
+ useEffect(() => {
104
+ if (this.tt.callOptionsPositionAboveIfOutsideScreen && this.s.optionsContainerLayout) {
105
+ this.callOptionsPositionAboveIfOutsideScreen = false;
106
+ this.setOptionsPositionAboveIfOutsideScreen();
107
+ }
108
+ }, [this.tt.callOptionsPositionAboveIfOutsideScreen, this.s.optionsContainerLayout]);
109
+ useEffect(() => {
110
+ const currentOptionIds = this.s.currentOptions?.map((currentOption) => currentOption.value);
111
+ if (this.props.values && anythingDifferent(currentOptionIds, this.props.values) && typeof this.props.options == "function") {
112
+ this.setCurrentFromGivenValues();
113
+ }
114
+ }, [this.props.values]);
115
+ }
116
+ defaultCurrentOptions() {
117
+ const { defaultValue, defaultValues, values } = this.props;
118
+ const { options } = this.p;
119
+ if (!Array.isArray(options))
120
+ return [];
121
+ return options.filter(({ value }) => (defaultValue && value == defaultValue) ||
122
+ (defaultValues && defaultValues.includes(value)) ||
123
+ (values && values.includes(value)));
124
+ }
125
+ defaultLoadedOptions() {
126
+ const { options } = this.p;
127
+ if (typeof options == "function") {
128
+ return undefined;
129
+ }
130
+ else if (Array.isArray(options)) {
131
+ return options;
132
+ }
133
+ throw new Error(`Unknown type of options: ${typeof options}`);
134
+ }
135
+ defaultToggled = () => ("toggled" in this.props) ? this.p.toggled : this.props.defaultToggled || {};
136
+ getToggled = () => ("toggled" in this.props) ? this.p.toggled : this.s.toggled;
137
+ getValues = () => ("values" in this.props) ? this.p.values : this.s.currentOptions.map((currentOption) => currentOption.value);
138
+ getCurrentOptions = () => {
139
+ if ("values" in this.props && typeof this.props.values != "undefined") {
140
+ if (Array.isArray(this.props.options) && this.props.values) {
141
+ return this.p.values.map((value) => this.p.options.find((option) => option.value == value));
142
+ }
143
+ else if (this.s.loadedOptions && this.props.values) {
144
+ return this.p.values.map((value) => {
145
+ let foundOption = this.s.loadedOptions.find((option) => option.value == value);
146
+ if (!foundOption) {
147
+ foundOption = this.s.currentOptions.find((option) => option.value == value);
148
+ if (!foundOption) {
149
+ console.error(`Couldn't find option: ${value} in loadedOptions or state currentOptions`, { stateLoadedOptions: this.s.loadedOptions, stateCurrentOptions: this.s.currentOptions, propsOptions: this.props.options });
150
+ }
151
+ }
152
+ return foundOption;
153
+ });
154
+ }
155
+ else if (typeof this.props.options == "function" && this.props.values) {
156
+ // Options haven't been loaded yet.
157
+ }
158
+ else if (this.props.values) {
159
+ return this.p.values.map((value) => ({ value }));
160
+ }
161
+ }
162
+ return this.s.currentOptions;
163
+ };
164
+ getCurrentOptionValues() {
165
+ if ("values" in this.props) {
166
+ return this.p.values;
167
+ }
168
+ return this.getCurrentOptions().map((option) => option.value);
169
+ }
170
+ componentDidMount() {
171
+ const { attribute, defaultValue, defaultValues, defaultValuesFromOptions, model, options } = this.props;
172
+ if (((defaultValue || defaultValues || defaultValuesFromOptions) || (attribute && model)) && typeof options == "function") {
173
+ this.loadDefaultValuesFromOptionsCallback();
174
+ }
175
+ }
176
+ componentDidUpdate() {
177
+ const newState = {};
178
+ if ("toggled" in this.props) {
179
+ const newToggled = this.p.toggled;
180
+ if (anythingDifferent(this.state.toggled, newToggled)) {
181
+ newState.toggled = newToggled;
182
+ }
183
+ }
184
+ if (Object.keys(newState).length > 0) {
185
+ this.setState(newState);
186
+ }
187
+ }
188
+ render() {
189
+ const { endOfSelectRef } = this.tt;
190
+ const { className, placeholder, toggleOptions } = this.props;
191
+ const { opened, optionsPlacement } = this.s;
192
+ const currentOptions = this.getCurrentOptions();
193
+ const selectContainerStyleActual = this.stylingFor("selectContainer", {
194
+ flexDirection: "row",
195
+ alignItems: "center",
196
+ backgroundColor: "#fff",
197
+ border: "1px solid #999",
198
+ borderRadius: 4,
199
+ color: "#000",
200
+ cursor: "pointer",
201
+ paddingTop: 5,
202
+ paddingBottom: 5,
203
+ paddingLeft: 5
204
+ });
205
+ if (opened) {
206
+ // Prevent select from changing size once the content is replaced with search text once opened
207
+ selectContainerStyleActual.height = this.s.height;
208
+ if (optionsPlacement == "above") {
209
+ selectContainerStyleActual.borderTopLeftRadius = 0;
210
+ selectContainerStyleActual.borderTopRightRadius = 0;
211
+ selectContainerStyleActual.borderBottomRightRadius = selectContainerStyleActual.borderRadius;
212
+ selectContainerStyleActual.borderBottomLeftRadius = selectContainerStyleActual.borderRadius;
213
+ delete selectContainerStyleActual.borderRadius;
214
+ }
215
+ else if (optionsPlacement == "below") {
216
+ selectContainerStyleActual.borderTopLeftRadius = selectContainerStyleActual.borderRadius;
217
+ selectContainerStyleActual.borderTopRightRadius = selectContainerStyleActual.borderRadius;
218
+ selectContainerStyleActual.borderBottomRightRadius = 0;
219
+ selectContainerStyleActual.borderBottomLeftRadius = 0;
220
+ delete selectContainerStyleActual.borderRadius;
221
+ }
222
+ }
223
+ const chevronStyle = this.stylingFor("chevron", { fontSize: 24 });
224
+ if (opened) {
225
+ chevronStyle.marginBottom = -15;
226
+ }
227
+ else {
228
+ chevronStyle.marginTop = -9;
229
+ }
230
+ return (React.createElement(View, { dataSet: {
231
+ class: className,
232
+ component: "haya-select",
233
+ id: idForComponent(this),
234
+ opened,
235
+ optionsPlacement,
236
+ toggles: Boolean(toggleOptions)
237
+ }, style: this.stylingFor("main") },
238
+ React.createElement(Pressable, { dataSet: { class: "select-container" }, onLayout: this.tt.onSelectContainerLayout, onPress: this.tt.onSelectClicked, ref: this.tt.selectContainerRef, style: selectContainerStyleActual },
239
+ React.createElement(View, { dataSet: { class: "current-selected" }, style: this.stylingFor("currentSelected", { width: "calc(100% - 25px)", flexWrap: "wrap" }) },
240
+ opened &&
241
+ React.createElement(TextInput, { dataSet: { class: "search-text-input" }, onChange: this.tt.onSearchTextInputChangedDebounced, onChangeText: this.tt.onChangeSearchText, placeholder: this.t(".search_dot_dot_dot"), ref: this.tt.searchTextInputRef, style: this.stylingFor("searchTextInput", {
242
+ width: "100%",
243
+ border: 0,
244
+ outline: "none",
245
+ padding: 0
246
+ }), value: this.state.searchText }),
247
+ !opened &&
248
+ React.createElement(React.Fragment, null,
249
+ currentOptions.length == 0 &&
250
+ React.createElement(Text, { numberOfLines: 1, style: this.stylingFor("nothingSelected", { color: "grey" }) }, placeholder || this.t(".nothing_selected")),
251
+ currentOptions.length == 0 && Platform.OS == "web" &&
252
+ React.createElement("input", { id: idForComponent(this), name: nameForComponentWithMultiple(this), type: "hidden", value: "" }),
253
+ currentOptions.map((currentOption) => React.createElement(View, { dataSet: { class: "current-option" }, key: currentOption.key || `current-value-${currentOption.value}`, style: this.stylingFor("currentOption", { marginRight: 6 }) },
254
+ currentOption.type == "group" &&
255
+ React.createElement(View, { style: this.stylingFor("currentOptionGroup", { fontWeight: "bold" }) },
256
+ React.createElement(Text, { style: this.stylingFor("currentOptionGroupText") }, currentOption.text)),
257
+ currentOption.type != "group" &&
258
+ React.createElement(React.Fragment, null,
259
+ Platform.OS == "web" && nameForComponentWithMultiple(this) &&
260
+ React.createElement("input", { id: idForComponent(this), name: nameForComponentWithMultiple(this), type: "hidden", value: digg(currentOption, "value") }),
261
+ this.presentOption(currentOption, "current")))))),
262
+ React.createElement(View, { className: "chevron-container", style: this.stylingFor("chevronContainer", {
263
+ alignItems: "center",
264
+ justifyContent: "center",
265
+ height: "100%",
266
+ marginLeft: "auto"
267
+ }) },
268
+ React.createElement(Text, { style: chevronStyle },
269
+ opened &&
270
+ React.createElement(React.Fragment, null, "\u2303"),
271
+ !opened &&
272
+ React.createElement(React.Fragment, null, "\u2304")))),
273
+ React.createElement(View, { dataSet: { class: "end-of-select" }, onLayout: this.tt.onEndOfSelectLayout, ref: endOfSelectRef }),
274
+ opened &&
275
+ React.createElement(Portal, null, this.optionsContainer())));
276
+ }
277
+ defaultValues() {
278
+ const { attribute, defaultValue, defaultValues, defaultValuesFromOptions, model } = this.props;
279
+ if (defaultValuesFromOptions)
280
+ return defaultValuesFromOptions;
281
+ if (defaultValue)
282
+ return defaultValue;
283
+ if (defaultValues)
284
+ return defaultValues;
285
+ if (attribute && model) {
286
+ if (!(attribute in model))
287
+ throw new Error(`No such attribute on ${model.modelClassData().name}: ${attribute}`);
288
+ return model[attribute]();
289
+ }
290
+ }
291
+ isActive() {
292
+ if (this.tt.endOfSelectRef.current) {
293
+ return true;
294
+ }
295
+ return false;
296
+ }
297
+ async loadDefaultValuesFromOptionsCallback() {
298
+ const defaultValues = this.defaultValues();
299
+ if (!defaultValues)
300
+ return;
301
+ const result = await this.props.options({
302
+ searchValue: this.s.searchText,
303
+ values: defaultValues
304
+ });
305
+ this.setState({ currentOptions: this.state.currentOptions.concat(result) });
306
+ }
307
+ loadOptions = async () => {
308
+ const { options } = this.p;
309
+ const searchValue = this.s.searchText;
310
+ if (Array.isArray(options)) {
311
+ return this.loadOptionsFromArray(options, searchValue);
312
+ }
313
+ const loadedOptions = await options({ searchValue });
314
+ this.setState({ loadedOptions });
315
+ };
316
+ hayaSelectOption({ key, loadedOption }) {
317
+ if (loadedOption.type == "group") {
318
+ return React.createElement(OptionGroup, { key: key, option: loadedOption });
319
+ }
320
+ return (React.createElement(Option, { currentOptionValues: this.getCurrentOptionValues(), icon: this.iconForOption(loadedOption), key: key, option: loadedOption, onOptionClicked: this.tt.onOptionClicked, presentOption: this.tt.presentOption }));
321
+ }
322
+ loadOptionsFromArray(options, searchValue) {
323
+ const lowerSearchValue = searchValue?.toLowerCase();
324
+ const loadedOptions = options.filter(({ text }) => !lowerSearchValue || text?.toLowerCase()?.includes(lowerSearchValue));
325
+ this.setState({ loadedOptions });
326
+ }
327
+ onSelectContainerLayout = (e) => this.setState({ selectContainerLayout: Object.assign({}, digg(e, "nativeEvent", "layout")) });
328
+ onEndOfSelectLayout = (e) => this.setState({ endOfSelectLayout: Object.assign({}, digg(e, "nativeEvent", "layout")) });
329
+ onOptionsContainerLayout = (e) => this.setState({ optionsContainerLayout: Object.assign({}, digg(e, "nativeEvent", "layout")) });
330
+ onSelectClicked = (e) => {
331
+ e.preventDefault();
332
+ e.stopPropagation();
333
+ const { opened } = this.s;
334
+ if (opened) {
335
+ this.closeOptions();
336
+ }
337
+ else {
338
+ this.openOptions();
339
+ }
340
+ };
341
+ onSearchTextInputChangedDebounced = debounce(this.tt.loadOptions, 200);
342
+ closeOptions() {
343
+ this.setState({
344
+ height: null,
345
+ loadedOptions: undefined,
346
+ opened: false,
347
+ optionsContainerLayout: null
348
+ });
349
+ if (this.props.onOptionsClosed) {
350
+ this.props.onOptionsClosed({ options: this.getCurrentOptions() });
351
+ }
352
+ }
353
+ onChangeSearchText = (searchText) => this.setState({ searchText });
354
+ openOptions() {
355
+ this.setOptionsPositionBelow();
356
+ this.loadOptions();
357
+ this.callOptionsPositionAboveIfOutsideScreen = true;
358
+ this.setState({
359
+ height: this.s.selectContainerLayout.height,
360
+ opened: true,
361
+ scrollLeft: Platform.OS == "web" ? document.documentElement.scrollLeft : null,
362
+ scrollTop: Platform.OS == "web" ? document.documentElement.scrollTop : null,
363
+ searchText: ""
364
+ }, () => {
365
+ this.focusTextInput();
366
+ });
367
+ }
368
+ focusTextInput = () => digg(this.tt.searchTextInputRef, "current")?.focus();
369
+ setOptionsPosition() {
370
+ if (!this.isActive()) {
371
+ return; // Debounce after un-mount handeling.
372
+ }
373
+ this.callOptionsPositionAboveIfOutsideScreen = true;
374
+ this.setOptionsPositionBelow();
375
+ }
376
+ setOptionsPositionAboveIfOutsideScreen() {
377
+ const { optionsContainerLayout } = this.s;
378
+ const optionsTop = this.s.endOfSelectLayout.top;
379
+ const optionsTotalBottomPosition = optionsContainerLayout.height + optionsTop;
380
+ const windowHeightWithScroll = document.body.clientHeight + this.s.scrollTop;
381
+ if (windowHeightWithScroll < optionsTotalBottomPosition) {
382
+ this.setOptionsPositionAbove();
383
+ }
384
+ else {
385
+ this.setState({ optionsVisibility: "visible" });
386
+ }
387
+ }
388
+ setOptionsPositionAbove() {
389
+ const { endOfSelectLayout } = this.s;
390
+ this.setState({
391
+ opened: true,
392
+ optionsPlacement: "above",
393
+ optionsVisibility: "visible",
394
+ optionsWidth: endOfSelectLayout.width
395
+ }, () => this.focusTextInput());
396
+ }
397
+ setOptionsPositionBelow() {
398
+ this.setState({
399
+ opened: true,
400
+ optionsPlacement: "below",
401
+ optionsVisibility: "hidden",
402
+ optionsWidth: this.s.endOfSelectLayout.width
403
+ }, () => this.focusTextInput());
404
+ }
405
+ onAnythingResized = () => {
406
+ if (this.s.opened) {
407
+ this.setOptionsPosition();
408
+ }
409
+ };
410
+ onAnythingResizedDebounced = debounce(this.tt.onAnythingResized, 25);
411
+ onAnythingScrolled = () => {
412
+ if (this.s.opened) {
413
+ this.setState({
414
+ scrollLeft: document.documentElement.scrollLeft,
415
+ scrollTop: document.documentElement.scrollTop
416
+ });
417
+ this.setOptionsPosition();
418
+ }
419
+ };
420
+ onAnythingScrolledDebounced = debounce(this.tt.onAnythingScrolled, 25);
421
+ onWindowClicked = (e) => {
422
+ const { optionsContainerRef } = this.tt;
423
+ const { opened } = this.s;
424
+ // If options are open and a click is made outside of the options container
425
+ if (opened && optionsContainerRef.current && !optionsContainerRef.current?.contains(e.target)) {
426
+ this.closeOptions();
427
+ }
428
+ };
429
+ optionsContainer() {
430
+ const { optionsContainerRef } = this.tt;
431
+ const { optionsAbsolute } = this.p;
432
+ const { selectContainerLayout, loadedOptions, endOfSelectLayout, optionsContainerLayout, optionsPlacement, optionsVisibility } = this.s;
433
+ let left, top;
434
+ let style = {
435
+ position: "absolute",
436
+ zIndex: 99999,
437
+ elevation: 99999,
438
+ visibility: optionsVisibility,
439
+ width: this.p.optionsWidth || this.s.optionsWidth,
440
+ backgroundColor: "#fff",
441
+ border: "1px solid #999",
442
+ maxHeight: 300,
443
+ overflowY: "auto"
444
+ };
445
+ if (!optionsAbsolute) {
446
+ style.left = 0;
447
+ style.bottom = 0;
448
+ }
449
+ else if (optionsPlacement == "below") {
450
+ if (Platform.OS == "web") {
451
+ // onLayout top value is sometimes negative so use browser JS to get it instead
452
+ top = digg(this.tt.endOfSelectRef.current.getBoundingClientRect(), "top") + document.documentElement.scrollTop + 1;
453
+ // onLayout left values doesn't always update when changed
454
+ left = digg(this.tt.endOfSelectRef.current.getBoundingClientRect(), "left") + document.documentElement.scrollLeft;
455
+ }
456
+ else {
457
+ left = this.s.endOfSelectLayout.left;
458
+ top = selectContainerLayout.top;
459
+ }
460
+ style.left = left;
461
+ style.top = top - 2;
462
+ }
463
+ else if (optionsPlacement == "above") {
464
+ if (Platform.OS == "web") {
465
+ // onLayout top value is sometimes negative so use browser JS to get it instead
466
+ top = digg(this.tt.selectContainerRef.current.getBoundingClientRect(), "top") + document.documentElement.scrollTop;
467
+ // onLayout left values doesn't always update when changed
468
+ left = digg(this.tt.selectContainerRef.current.getBoundingClientRect(), "left") + document.documentElement.scrollLeft;
469
+ }
470
+ else {
471
+ left = endOfSelectLayout.left;
472
+ top = selectContainerLayout.top;
473
+ }
474
+ style.left = left;
475
+ style.top = top - optionsContainerLayout.height + 1;
476
+ }
477
+ else {
478
+ throw new Error(`Unkonwn options placement: ${optionsPlacement}`);
479
+ }
480
+ style = this.stylingFor("optionsContainer", style);
481
+ return (React.createElement(View, { dataSet: { class: "options-container", id: idForComponent(this) }, onLayout: this.tt.onOptionsContainerLayout, ref: optionsContainerRef, style: style },
482
+ loadedOptions?.map((loadedOption) => this.hayaSelectOption({
483
+ key: loadedOption.key || `loaded-option-${loadedOption.value}`,
484
+ loadedOption
485
+ })),
486
+ loadedOptions?.length === 0 &&
487
+ React.createElement(View, { dataSet: { class: "no-options-container" } },
488
+ React.createElement(Text, null, this.t(".no_options_found")))));
489
+ }
490
+ onOptionClicked = (event, loadedOption) => {
491
+ event.preventDefault();
492
+ event.stopPropagation();
493
+ const { onChange, toggleOptions } = this.props;
494
+ const { multiple } = this.p;
495
+ const currentOptions = this.getCurrentOptions();
496
+ const toggled = this.getToggled();
497
+ const newState = {};
498
+ const existingOption = currentOptions.find((currentOption) => currentOption.value == loadedOption.value);
499
+ const newToggled = { ...toggled };
500
+ if (existingOption) {
501
+ if (toggleOptions) {
502
+ const currentToggle = toggled[loadedOption.value];
503
+ const currentIndex = toggleOptions.findIndex((element) => element.value == currentToggle);
504
+ if (currentIndex >= (toggleOptions.length - 1)) {
505
+ // No next toggled - remove toggled and option
506
+ delete newToggled[loadedOption.value];
507
+ newState.currentOptions = currentOptions.filter((currentOption) => currentOption.value != loadedOption.value);
508
+ }
509
+ else {
510
+ // Already toggled - set to next toggle
511
+ newToggled[loadedOption.value] = digg(toggleOptions, currentIndex + 1, "value");
512
+ }
513
+ newState.toggled = newToggled;
514
+ }
515
+ else {
516
+ // Remove from current options
517
+ newState.currentOptions = currentOptions.filter((currentOption) => currentOption.value != loadedOption.value);
518
+ }
519
+ }
520
+ else {
521
+ // Don't do anything if the clicked option is disabled
522
+ if (loadedOption.disabled)
523
+ return;
524
+ if (toggleOptions) {
525
+ // Set fresh toggle
526
+ newToggled[loadedOption.value] = toggleOptions[0].value;
527
+ newState.toggled = newToggled;
528
+ }
529
+ if (multiple || toggleOptions) {
530
+ newState.currentOptions = currentOptions.concat([loadedOption]);
531
+ }
532
+ else {
533
+ newState.currentOptions = [loadedOption];
534
+ }
535
+ }
536
+ const options = newState.currentOptions || currentOptions;
537
+ if (onChange) {
538
+ onChange({
539
+ event,
540
+ options,
541
+ toggles: newToggled
542
+ });
543
+ }
544
+ if (this.props.onChangeValue) {
545
+ let optionValue;
546
+ if (multiple) {
547
+ optionValue = options.map((option) => option.value);
548
+ }
549
+ else {
550
+ optionValue = dig(options, 0, "value");
551
+ }
552
+ this.p.onChangeValue(optionValue);
553
+ }
554
+ if (!multiple)
555
+ this.closeOptions();
556
+ this.setState(newState);
557
+ };
558
+ stylingFor(stylingName, style = {}) {
559
+ let customStyling = dig(this, "props", "styles", stylingName);
560
+ if (typeof customStyling == "function")
561
+ customStyling = customStyling({ state: this.state, style });
562
+ if (customStyling) {
563
+ return Object.assign(style, customStyling);
564
+ }
565
+ return style;
566
+ }
567
+ iconForOption(option) {
568
+ const { toggleOptions } = this.props || {};
569
+ const toggled = this.getToggled();
570
+ if (toggleOptions && (option.value in toggled)) {
571
+ const toggledValue = toggled[option.value];
572
+ const toggledOption = toggleOptions.find((element) => element.value == toggledValue);
573
+ if (!toggledOption) {
574
+ throw new Error(`Couldn't find a toggle option for value: ${toggledValue}`);
575
+ }
576
+ return toggledOption.icon;
577
+ }
578
+ }
579
+ presentOption = (option, mode) => {
580
+ const { toggleOptions } = this.props || {};
581
+ const toggled = this.getToggled();
582
+ const icon = this.iconForOption(option);
583
+ const toggleValue = toggled[option.value];
584
+ const toggleOption = toggleOptions?.find((toggleOptionI) => toggleOptionI.value == toggleValue);
585
+ return (React.createElement(View, { dataSet: {
586
+ class: "option-presentation",
587
+ text: option.text,
588
+ toggleIcon: toggleOption?.icon,
589
+ toggleValue: toggleOption?.value,
590
+ value: option.value
591
+ }, style: { flexDirection: "row" } },
592
+ toggleOptions && !(option.value in toggled) &&
593
+ React.createElement(View, { dataSet: { class: "toggle-icon-placeholder" }, style: { width: 20 } }),
594
+ toggleOptions && (option.value in toggled) &&
595
+ React.createElement(View, { style: { alignItems: "center", justifyContent: "center", width: 20 } },
596
+ React.createElement(FontAwesomeIcon, { dataSet: { class: "toggle-icon" }, name: icon })),
597
+ (() => {
598
+ if (mode == "current" && option.currentContent) {
599
+ return option.currentContent();
600
+ }
601
+ else if (option.content) {
602
+ return option.content();
603
+ }
604
+ else if ("html" in option && Platform.OS != "web") {
605
+ return React.createElement(RenderHtml, { source: { html: digg(option, "html") } });
606
+ }
607
+ else if ("html" in option && Platform.OS == "web") {
608
+ return React.createElement("div", { dangerouslySetInnerHTML: { __html: digg(option, "html") } });
609
+ }
610
+ else {
611
+ return (React.createElement(Text, { dataSet: { class: "option-presentation-text" }, style: this.stylingFor("optionPresentationText", { flex: 1, whiteSpace: "nowrap" }) }, option.text));
612
+ }
613
+ })()));
614
+ };
615
+ async setCurrentFromGivenValues() {
616
+ const { options, values } = this.p;
617
+ const currentOptions = await options({ values });
618
+ const currentValues = currentOptions?.map((currentOption) => currentOption.value);
619
+ const stateValues = this.s.currentOptions?.map((currentOption) => currentOption.value);
620
+ if (anythingDifferent(currentValues, stateValues)) {
621
+ this.setState({ currentOptions });
622
+ }
623
+ }
624
+ }));
625
+ //# sourceMappingURL=index.js.map