haya-select 1.0.63 → 1.0.65

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