ish-ui 1.0.2

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.
Files changed (126) hide show
  1. package/.eslintrc.json +39 -0
  2. package/.gitattributes +3 -0
  3. package/.github/workflows/release-package.yml +33 -0
  4. package/.prettierrc +5 -0
  5. package/README.md +80 -0
  6. package/assets/fonts/inter/Inter-Italic.var.woff2 +0 -0
  7. package/assets/fonts/inter/Inter-Roman.var.woff2 +0 -0
  8. package/assets/fonts/inter/LICENSE.txt +92 -0
  9. package/assets/fonts/inter/index.d.ts +10 -0
  10. package/assets/fonts/inter/inter.scss +17 -0
  11. package/package.json +108 -0
  12. package/src/actions/FieldProcessing.ts +21 -0
  13. package/src/actions/index.ts +10 -0
  14. package/src/appBar/AppBarHelpMenu.tsx +139 -0
  15. package/src/appBar/index.ts +9 -0
  16. package/src/buttons/AddButton.tsx +30 -0
  17. package/src/buttons/TimetableButton.tsx +19 -0
  18. package/src/buttons/index.ts +10 -0
  19. package/src/colorPicker/ColorPicker.tsx +223 -0
  20. package/src/colorPicker/index.ts +9 -0
  21. package/src/dialog/BrowserWarning.tsx +24 -0
  22. package/src/dialog/confirm/ConfirmBase.tsx +59 -0
  23. package/src/dialog/index.ts +11 -0
  24. package/src/dialog/message/Message.tsx +140 -0
  25. package/src/documents/DocumentIconsChooser.tsx +69 -0
  26. package/src/documents/index.ts +10 -0
  27. package/src/documents/utils/index.tsx +71 -0
  28. package/src/dynamicSizeList/DynamicSizeList.tsx +208 -0
  29. package/src/dynamicSizeList/index.ts +9 -0
  30. package/src/fieldMessage/ErrorMessage.tsx +28 -0
  31. package/src/fieldMessage/WarningMessage.tsx +28 -0
  32. package/src/fieldMessage/index.ts +11 -0
  33. package/src/fieldMessage/styles.ts +21 -0
  34. package/src/fileUploader/FileUploaderDialog.tsx +158 -0
  35. package/src/fileUploader/index.ts +9 -0
  36. package/src/formFields/CheckboxField.tsx +90 -0
  37. package/src/formFields/CodeEditorField.tsx +103 -0
  38. package/src/formFields/ColoredCheckBox.tsx +74 -0
  39. package/src/formFields/DateTimeField.tsx +61 -0
  40. package/src/formFields/EditInPlaceDateTimeField.tsx +285 -0
  41. package/src/formFields/EditInPlaceDurationField.tsx +79 -0
  42. package/src/formFields/EditInPlaceField.tsx +131 -0
  43. package/src/formFields/EditInPlaceFieldBase.tsx +241 -0
  44. package/src/formFields/EditInPlaceFileField.tsx +67 -0
  45. package/src/formFields/EditInPlaceMoneyField.tsx +64 -0
  46. package/src/formFields/EditInPlacePhoneField.tsx +97 -0
  47. package/src/formFields/EditInPlaceSearchSelect.tsx +562 -0
  48. package/src/formFields/FieldAdornments.tsx +70 -0
  49. package/src/formFields/FilePreview.tsx +149 -0
  50. package/src/formFields/FormRadioButtons.tsx +62 -0
  51. package/src/formFields/SelectCustomComponents.tsx +228 -0
  52. package/src/formFields/Switch.tsx +146 -0
  53. package/src/formFields/TextField.tsx +60 -0
  54. package/src/formFields/UneditableBase.tsx +101 -0
  55. package/src/formFields/index.ts +27 -0
  56. package/src/index.ts +26 -0
  57. package/src/layout/InfoPill.tsx +36 -0
  58. package/src/layout/index.ts +10 -0
  59. package/src/layout/resizable/ResizableWrapper.tsx +87 -0
  60. package/src/markdownEditor/HtmlEditor.tsx +44 -0
  61. package/src/markdownEditor/WysiwygEditor.tsx +137 -0
  62. package/src/markdownEditor/index.ts +12 -0
  63. package/src/markdownEditor/style.ts +188 -0
  64. package/src/markdownEditor/utils/index.ts +48 -0
  65. package/src/model/AppBar.ts +20 -0
  66. package/src/model/CommomObjects.ts +8 -0
  67. package/src/model/CommonFunctions.ts +20 -0
  68. package/src/model/Confirm.ts +27 -0
  69. package/src/model/Fields.ts +254 -0
  70. package/src/model/NestedEntity.ts +14 -0
  71. package/src/model/Theme.ts +42 -0
  72. package/src/model/index.ts +16 -0
  73. package/src/styles/GlobalStylesProvider.tsx +520 -0
  74. package/src/styles/StylesProviderCustom.tsx +8 -0
  75. package/src/styles/animateStyles.ts +94 -0
  76. package/src/styles/bootstrap.ts +503 -0
  77. package/src/styles/hooks.ts +44 -0
  78. package/src/styles/index.ts +18 -0
  79. package/src/styles/makeStyles.ts +21 -0
  80. package/src/styles/mixins/common.ts +40 -0
  81. package/src/styles/mixins/keyframes.ts +16 -0
  82. package/src/styles/mixins/prefixer.ts +21 -0
  83. package/src/tagsInput/AddTagMenu.tsx +74 -0
  84. package/src/tagsInput/AddTagMenuItem.tsx +50 -0
  85. package/src/tagsInput/TagInputList.tsx +428 -0
  86. package/src/tagsInput/index.ts +4 -0
  87. package/src/themes/appTheme.ts +324 -0
  88. package/src/themes/index.ts +10 -0
  89. package/src/themes/ishTheme.ts +401 -0
  90. package/src/timetable/datePeriodFilter/DayPeriodIcons.tsx +95 -0
  91. package/src/timetable/datePeriodFilter/index.ts +10 -0
  92. package/src/timetable/index.ts +11 -0
  93. package/src/timetable/miniCalendar/CalendarDay.tsx +109 -0
  94. package/src/timetable/miniCalendar/CalendarHeader.tsx +37 -0
  95. package/src/timetable/miniCalendar/CalendarWeekPanel.tsx +35 -0
  96. package/src/timetable/miniCalendar/WeekDay.tsx +49 -0
  97. package/src/timetable/miniCalendar/index.ts +13 -0
  98. package/src/utils/DOM/Links.ts +17 -0
  99. package/src/utils/DOM/getCaretCoordinates.ts +122 -0
  100. package/src/utils/DOM/getOS.ts +32 -0
  101. package/src/utils/DOM/index.ts +34 -0
  102. package/src/utils/dates/datesNormalizing.ts +4 -0
  103. package/src/utils/dates/format.ts +52 -0
  104. package/src/utils/dates/formatRelative.ts +22 -0
  105. package/src/utils/dates/formatString.ts +233 -0
  106. package/src/utils/dates/formatTimezone.ts +37 -0
  107. package/src/utils/dates/index.ts +15 -0
  108. package/src/utils/dates/timetable.ts +46 -0
  109. package/src/utils/events/index.ts +8 -0
  110. package/src/utils/fields/index.ts +23 -0
  111. package/src/utils/formatting/PhoneMasks.ts +320 -0
  112. package/src/utils/formatting/index.tsx +20 -0
  113. package/src/utils/hooks/index.ts +19 -0
  114. package/src/utils/index.ts +15 -0
  115. package/src/utils/numbers/decimalCalculation.ts +26 -0
  116. package/src/utils/numbers/index.ts +11 -0
  117. package/src/utils/numbers/numbersNormalizing.ts +52 -0
  118. package/src/utils/stubs/index.tsx +12 -0
  119. package/src/utils/tags/index.ts +53 -0
  120. package/tools/webpack/webpack.aliases.js +13 -0
  121. package/tools/webpack/webpack.config.dev.js +29 -0
  122. package/tools/webpack/webpack.config.prod.js +36 -0
  123. package/tools/webpack/webpack.helpers.js +26 -0
  124. package/tools/webpack/webpack.plugins.js +15 -0
  125. package/tools/webpack/webpack.rules.js +51 -0
  126. package/tsconfig.json +24 -0
@@ -0,0 +1,562 @@
1
+ /*
2
+ * Copyright ish group pty ltd 2023.
3
+ *
4
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
5
+ *
6
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
7
+ */
8
+
9
+ import CircularProgress from '@mui/material/CircularProgress';
10
+ import Popper from '@mui/material/Popper';
11
+ import { Autocomplete, IconButton, InputAdornment, Select } from '@mui/material';
12
+ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
13
+ import CloseIcon from '@mui/icons-material//Close';
14
+ import clsx from 'clsx';
15
+ import ExpandMore from '@mui/icons-material/ExpandMore';
16
+ import { ListboxComponent, selectStyles } from './SelectCustomComponents';
17
+ import EditInPlaceFieldBase from './EditInPlaceFieldBase';
18
+ import { EditInPlaceSearchSelectFieldProps, FieldInputProps, FieldMetaProps } from '@src/model';
19
+ import { makeAppStyles } from '@src/styles';
20
+ import { getHighlightedPartLabel, stubComponent } from '@src/utils';
21
+ import { usePrevious } from '@src/utils/hooks';
22
+
23
+ const useStyles = makeAppStyles(theme => ({
24
+ selectAdornment: {
25
+ marginBottom: '6px',
26
+ color: theme.palette.primary.main
27
+ },
28
+ popper: {
29
+ zIndex: 1400
30
+ },
31
+ ...selectStyles(theme)
32
+ }))
33
+
34
+ const SelectContext = React.createContext<any>({});
35
+
36
+ const ListBoxAdapter = React.forwardRef<any, any>(({ children, ...other }, ref) => {
37
+ const {
38
+ rowHeight,
39
+ remoteRowCount,
40
+ loadMoreRows,
41
+ classes,
42
+ fieldClasses = {},
43
+ loading,
44
+ selectAdornment
45
+ } = useContext(SelectContext);
46
+
47
+ return (
48
+ <ListboxComponent
49
+ rowHeight={rowHeight}
50
+ remoteRowCount={remoteRowCount}
51
+ loadMoreRows={loadMoreRows}
52
+ classes={classes}
53
+ fieldClasses={fieldClasses}
54
+ loading={loading}
55
+ selectAdornment={selectAdornment}
56
+ ref={ref}
57
+ {...other}
58
+ >
59
+ {children}
60
+ </ListboxComponent>
61
+ );
62
+ });
63
+
64
+ const PopperAdapter = React.memo<any>(params => {
65
+ const {
66
+ hideMenuOnNoResults,
67
+ items,
68
+ popperAnchor,
69
+ inline
70
+ } = useContext(SelectContext);
71
+
72
+ return (hideMenuOnNoResults && !items.length
73
+ ? null
74
+ : popperAnchor
75
+ // @ts-ignore
76
+ ? <Popper
77
+ {...params}
78
+ style={{ ...params.style, width: popperAnchor.clientWidth }}
79
+ anchorEl={popperAnchor}
80
+ />
81
+ // @ts-ignore
82
+ : <Popper
83
+ {...params}
84
+ style={inline ? null : params.style}
85
+ />);
86
+ });
87
+
88
+ function EditInPlaceSearchSelect<InputProps extends FieldInputProps, MetaProps extends FieldMetaProps>({
89
+ label,
90
+ disabled,
91
+ className,
92
+ labelAdornment,
93
+ inline,
94
+ loading,
95
+ creatable,
96
+ allowEmpty,
97
+ fieldClasses = {},
98
+ selectAdornment,
99
+ selectValueMark = 'value',
100
+ selectLabelMark = 'label',
101
+ selectLabelCondition,
102
+ selectFilterCondition,
103
+ defaultValue,
104
+ disableUnderline,
105
+ items = [],
106
+ rowHeight,
107
+ remoteRowCount,
108
+ loadMoreRows,
109
+ onCreateOption,
110
+ itemRenderer,
111
+ onInputChange,
112
+ onClearRows,
113
+ onInnerValueChange,
114
+ hideMenuOnNoResults,
115
+ hideEditIcon,
116
+ remoteData,
117
+ createLabel,
118
+ returnType = 'string',
119
+ alwaysDisplayDefault,
120
+ valueRenderer,
121
+ popperAnchor,
122
+ input,
123
+ meta: { error, invalid },
124
+ placeholder,
125
+ sort,
126
+ sortPropKey,
127
+ inputRef,
128
+ warning,
129
+ multiple,
130
+ rightAligned,
131
+ hasError,
132
+ categoryKey
133
+ }: EditInPlaceSearchSelectFieldProps<InputProps, MetaProps>) {
134
+
135
+ const classes = useStyles();
136
+
137
+ const sortedItems = useMemo(() => {
138
+ const sorted = items && (sort
139
+ ? [...items].sort(typeof sort === 'function'
140
+ ? sort
141
+ : (aOption, bOption) => {
142
+ const aLabel = selectLabelCondition ? selectLabelCondition(aOption) : aOption[sortPropKey || selectLabelMark];
143
+ const bLabel = selectLabelCondition ? selectLabelCondition(bOption) : bOption[sortPropKey || selectLabelMark];
144
+
145
+ return (aLabel.toLowerCase()).localeCompare(bLabel.toLowerCase());
146
+ })
147
+ : [...items]
148
+ );
149
+
150
+ if (categoryKey) {
151
+ sorted.sort((aOption, bOption) => (aOption[categoryKey].toLowerCase()).localeCompare(bOption[categoryKey].toLowerCase()));
152
+ }
153
+
154
+ return sorted;
155
+
156
+ }, [items, selectLabelCondition, selectLabelMark, sortPropKey, categoryKey]);
157
+
158
+ const inputNode = useRef<any>(null);
159
+
160
+ const [searchValue, setSearchValue] = useState<string>('');
161
+ const [isEditing, setIsEditing] = useState<boolean>(false);
162
+ const [formattedDisplayValue, setFormattedDisplayValue] = useState<any>('');
163
+
164
+ const prevDefaultDisplayValue = usePrevious(defaultValue);
165
+
166
+ useEffect(() => {
167
+ if (formattedDisplayValue && !input.value) {
168
+ setFormattedDisplayValue(null);
169
+ }
170
+ }, [formattedDisplayValue, input.value, input.name]);
171
+
172
+ useEffect(() => {
173
+ if (selectLabelCondition && formattedDisplayValue && defaultValue !== prevDefaultDisplayValue) {
174
+ setFormattedDisplayValue(null);
175
+ }
176
+ if (selectLabelCondition && !defaultValue) {
177
+ const selected = sortedItems.find(i => getOptionSelected(i, input.value));
178
+ if (selected) {
179
+ setFormattedDisplayValue(selectLabelCondition(selected));
180
+ }
181
+ }
182
+ }, [selectLabelCondition, formattedDisplayValue, defaultValue, sortedItems, input.value]);
183
+
184
+ const onBlur = () => {
185
+ setIsEditing(false);
186
+
187
+ if (!hideMenuOnNoResults) {
188
+ setSearchValue('');
189
+ }
190
+
191
+ if (remoteData) {
192
+ onInputChange('');
193
+ onClearRows();
194
+ }
195
+ };
196
+
197
+ const formatCreateLabel = inputValue => `${createLabel} "${inputValue}"`;
198
+
199
+ const filterItems = items => {
200
+ let filtered = [];
201
+
202
+ if (!Array.isArray(items)) return filtered;
203
+
204
+ if (!searchValue || remoteData) return items;
205
+
206
+ const searchRegexp = new RegExp(searchValue.replace(',', '')
207
+ // eslint-disable-next-line no-useless-escape
208
+ .replace(/[\[\]\{\}\(\)\*]/g, '\\$&')
209
+ .trim().toLowerCase());
210
+
211
+ if (typeof selectFilterCondition === 'function') {
212
+ filtered = items.filter(item => {
213
+ const label = selectFilterCondition(item);
214
+
215
+ if (!label) {
216
+ return false;
217
+ }
218
+
219
+ return searchRegexp.test(label.trim().toLowerCase());
220
+ });
221
+ } else if (typeof selectLabelCondition === 'function') {
222
+ filtered = items.filter(item => {
223
+ const label = selectLabelCondition(item);
224
+
225
+ if (!label) {
226
+ return false;
227
+ }
228
+
229
+ return searchRegexp.test(label.trim().toLowerCase());
230
+ });
231
+ } else {
232
+ filtered = items.filter( item => item[selectLabelMark]
233
+ && searchRegexp.test(item[selectLabelMark].toLowerCase().trim()));
234
+ }
235
+
236
+ if (creatable) {
237
+ filtered.push({
238
+ [selectLabelMark]: formatCreateLabel(searchValue),
239
+ [selectValueMark]: searchValue,
240
+ createdOption: true
241
+ });
242
+ }
243
+
244
+ return filtered;
245
+ };
246
+
247
+ const edit = () => {
248
+ setIsEditing(true);
249
+ setTimeout(() => inputNode?.current?.focus(), 50);
250
+ };
251
+
252
+ const onClear = () => {
253
+ if (remoteData) {
254
+ onInputChange('');
255
+ onClearRows();
256
+ }
257
+
258
+ if (typeof onInnerValueChange === 'function') {
259
+ onInnerValueChange(null);
260
+ }
261
+
262
+ if (input && input.onChange) {
263
+ input.onChange(null);
264
+ }
265
+
266
+ setSearchValue('');
267
+ setIsEditing(false);
268
+ setFormattedDisplayValue(null);
269
+ };
270
+
271
+ const onEditButtonFocus = () => {
272
+ if (disabled) {
273
+ return;
274
+ }
275
+ edit();
276
+ };
277
+
278
+ const onCreateOptionInner = value => {
279
+ delete value.createdOption;
280
+ onCreateOption(value[selectValueMark]);
281
+ onBlur();
282
+ };
283
+
284
+ const handleChange = (e, value, reason) => {
285
+ if (reason === 'clear') {
286
+ return;
287
+ }
288
+
289
+ if (creatable && value && value.createdOption) {
290
+ onCreateOptionInner(value);
291
+ }
292
+
293
+ if (typeof onInnerValueChange === 'function') {
294
+ onInnerValueChange(value);
295
+ }
296
+
297
+ if (selectLabelCondition && returnType !== 'object') {
298
+ setFormattedDisplayValue(selectLabelCondition(value));
299
+ }
300
+
301
+ let newValue = value[selectValueMark];
302
+
303
+ if (returnType === 'object') {
304
+ newValue = (Object.keys(value).length ? value : null);
305
+ }
306
+
307
+ if (multiple) {
308
+ newValue = value.map(v => v[selectValueMark]);
309
+ if (!newValue.length) {
310
+ newValue = null;
311
+ }
312
+ }
313
+
314
+ input.onChange(newValue);
315
+
316
+ setTimeout(() => {
317
+ onBlur();
318
+ input?.onBlur(newValue);
319
+ }, 100);
320
+ };
321
+
322
+ const handleInputChange = e => {
323
+ setSearchValue(e.target.value);
324
+
325
+ if (onInputChange) {
326
+ onInputChange(e.target.value);
327
+ }
328
+
329
+ if (remoteData && !e.target.value) {
330
+ onClearRows();
331
+ }
332
+ };
333
+
334
+ const getOptionLabel = option => (selectLabelCondition ? selectLabelCondition(option) : option && option[selectLabelMark]) || '';
335
+
336
+ const getOptionSelected = (option: any, value: any) => {
337
+ if (multiple || returnType === 'object') {
338
+ return option[selectValueMark] === value[selectValueMark];
339
+ }
340
+ return option[selectValueMark] === value;
341
+ };
342
+
343
+ const renderOption = (optionProps, data) => {
344
+ if (typeof itemRenderer === 'function') {
345
+ return itemRenderer(getHighlightedPartLabel(getOptionLabel(data), searchValue), data, searchValue, optionProps) as any;
346
+ }
347
+ return getHighlightedPartLabel(getOptionLabel(data), searchValue, optionProps);
348
+ };
349
+
350
+ const selectedOption = useMemo(() => sortedItems.find(i => multiple
351
+ ? (input.value as any[] || []).includes(i[selectValueMark])
352
+ : getOptionSelected(i, input.value)),
353
+ [sortedItems, multiple, selectValueMark, input.value]);
354
+
355
+ const displayedValue = useMemo(() => {
356
+ let response;
357
+
358
+ if (selectLabelCondition) {
359
+ response = returnType === 'object' ? selectLabelCondition(input.value) : formattedDisplayValue || defaultValue;
360
+ } else if (returnType === 'object') {
361
+ response = input.value && input.value[selectLabelMark];
362
+ } else {
363
+ response = getOptionLabel(selectedOption) || defaultValue || input.value;
364
+ }
365
+
366
+ if (alwaysDisplayDefault) {
367
+ response = defaultValue;
368
+ }
369
+
370
+ return (
371
+ ![null, undefined].includes(input.value)
372
+ ? response
373
+ : null
374
+ );
375
+ }, [
376
+ formattedDisplayValue,
377
+ selectedOption,
378
+ selectLabelCondition,
379
+ alwaysDisplayDefault,
380
+ returnType,
381
+ defaultValue,
382
+ selectLabelMark,
383
+ input.value,
384
+ classes
385
+ ]);
386
+
387
+ const renderValue = useMemo(() => {
388
+ const valueForRender = returnType === 'object'
389
+ ? input.value
390
+ : selectedOption;
391
+
392
+ return valueRenderer
393
+ ? valueRenderer(
394
+ displayedValue,
395
+ valueForRender,
396
+ searchValue,
397
+ { value: valueForRender && valueForRender[selectValueMark] }
398
+ )
399
+ : null;
400
+ },
401
+ [selectedOption, searchValue, displayedValue, selectValueMark, returnType, valueRenderer, input.value]);
402
+
403
+ const renderIcons = useMemo(() => !disabled && (
404
+ loading
405
+ ? <CircularProgress size={24} thickness={4} className={fieldClasses.loading} />
406
+ : (
407
+ <div className={classes.inputEndAdornment}>
408
+ {allowEmpty && input.value && (
409
+ <IconButton
410
+ size="small"
411
+ onClick={onClear}
412
+ color="inherit"
413
+ >
414
+ <CloseIcon fontSize="inherit" />
415
+ </IconButton>
416
+ )}
417
+ <IconButton
418
+ size="small"
419
+ color="inherit"
420
+ className={clsx(classes.expandIcon, 'pl-0 pr-0')}
421
+ disableRipple
422
+ >
423
+ <ExpandMore className={inline && 'fsInherit'} />
424
+ </IconButton>
425
+ </div>
426
+ )
427
+ ), [disabled, loading, inline, allowEmpty, input.value]);
428
+
429
+
430
+ const inputValue = useMemo(() => {
431
+ if (multiple) {
432
+ return (input.value as any[] || []).map(v => sortedItems.find(s => s[selectValueMark] === v));
433
+ }
434
+ return input.value || null;
435
+ }, [input.value, multiple, selectValueMark, sortedItems]);
436
+
437
+ const renderedPlaceholder = useMemo(() => {
438
+ const rendered = placeholder || (!isEditing ? 'No value' : '');
439
+ return multiple && (inputValue as any[]).length ? null : rendered;
440
+ }, [multiple, placeholder, isEditing, inputValue]);
441
+
442
+ return (
443
+ <div
444
+ className={clsx(className, 'outline-none', inline && classes.inline)}
445
+ id={input?.name}
446
+ >
447
+ <SelectContext.Provider value={{
448
+ rowHeight,
449
+ remoteRowCount,
450
+ loadMoreRows,
451
+ classes,
452
+ fieldClasses,
453
+ hideMenuOnNoResults,
454
+ inline,
455
+ items,
456
+ popperAnchor,
457
+ loading,
458
+ selectAdornment
459
+ }}
460
+ >
461
+ <Autocomplete
462
+ blurOnSelect={!multiple}
463
+ disableCloseOnSelect={multiple}
464
+ multiple={multiple}
465
+ value={inputValue}
466
+ options={sortedItems}
467
+ loading={loading}
468
+ freeSolo={creatable}
469
+ disabled={disabled}
470
+ disableClearable={!allowEmpty}
471
+ isOptionEqualToValue={getOptionSelected}
472
+ onChange={handleChange}
473
+ classes={{
474
+ root: clsx('d-inline-flex', classes.root),
475
+ hasPopupIcon: classes.hasPopup,
476
+ hasClearIcon: classes.hasClear,
477
+ inputRoot: clsx(classes.inputWrapper, multiple && classes.multiple),
478
+ option: 'w-100 text-pre',
479
+ popper: classes.popper,
480
+ groupUl: 'm-0'
481
+ }}
482
+ groupBy={categoryKey && (option => option[categoryKey])}
483
+ renderOption={renderOption}
484
+ getOptionLabel={getOptionLabel}
485
+ filterOptions={filterItems}
486
+ ListboxComponent={inline || categoryKey ? null : ListBoxAdapter as any}
487
+ PopperComponent={categoryKey ? null : PopperAdapter as any}
488
+ renderInput={({
489
+ InputLabelProps, InputProps, inputProps, ...params
490
+ }) => (
491
+ <EditInPlaceFieldBase
492
+ {...params}
493
+ name={input.name}
494
+ value={displayedValue}
495
+ error={error}
496
+ invalid={hasError || invalid}
497
+ inline={inline}
498
+ label={label}
499
+ warning={warning}
500
+ fieldClasses={fieldClasses}
501
+ endAdornmentClass={classes.selectAdornment}
502
+ rightAligned={rightAligned}
503
+ shrink={Boolean(label || input.value)}
504
+ labelAdornment={labelAdornment}
505
+ placeholder={renderedPlaceholder}
506
+ editIcon={!hideEditIcon && renderIcons}
507
+ InputProps={{
508
+ ...InputProps,
509
+ onChange: handleInputChange,
510
+ onFocus: edit,
511
+ onClick: onEditButtonFocus,
512
+ onBlur,
513
+ disableUnderline,
514
+ classes: {
515
+ underline: fieldClasses.underline,
516
+ input: clsx(disabled && classes.readonly, fieldClasses.text),
517
+ },
518
+ inputProps: {
519
+ ...inputProps,
520
+ className: clsx(fieldClasses.text, 'mr-auto'),
521
+ ref: ref => {
522
+ (inputProps as any).ref.current = ref;
523
+ inputNode.current = ref;
524
+ if (inputRef) inputRef.current = ref;
525
+ },
526
+ value: (isEditing ? searchValue : multiple ? '' : (typeof displayedValue === 'string' ? displayedValue : '')),
527
+ }
528
+ }}
529
+ CustomInput={(valueRenderer && !multiple && !isEditing && input.value)
530
+ ?
531
+ <Select
532
+ {...InputProps}
533
+ inputRef={ref => {
534
+ (inputProps as any).ref.current = ref?.node;
535
+ }}
536
+ classes={{ select: 'cursor-text' }}
537
+ onFocus={edit}
538
+ value={(returnType === 'object' ? input.value[selectValueMark] : input.value) || ''}
539
+ endAdornment={
540
+ <InputAdornment
541
+ position="end"
542
+ className={clsx( classes.selectAdornment, 'd-none')}>
543
+ {renderIcons}
544
+ </InputAdornment>
545
+ }
546
+ IconComponent={stubComponent}
547
+ >
548
+ {renderValue}
549
+ </Select>
550
+ : null}
551
+ />)
552
+ }
553
+ disableListWrap
554
+ openOnFocus
555
+ fullWidth
556
+ />
557
+ </SelectContext.Provider>
558
+ </div>
559
+ );
560
+ }
561
+
562
+ export default EditInPlaceSearchSelect;
@@ -0,0 +1,70 @@
1
+ /*
2
+ * Copyright ish group pty ltd 2023.
3
+ *
4
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
5
+ *
6
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
7
+ */
8
+
9
+ import React from 'react';
10
+ import IconButton from '@mui/material/IconButton';
11
+ import Launch from '@mui/icons-material/Launch';
12
+ import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
13
+ import { IconButtonTypeMap } from '@mui/material';
14
+
15
+ interface Props {
16
+ link?: string | number;
17
+ linkHandler?: (arg: string | number) => void;
18
+ linkColor?: IconButtonTypeMap['props']['color'];
19
+ clickHandler?: any;
20
+ className?: string;
21
+ disabled?: boolean;
22
+ }
23
+
24
+ export const LinkAdornment = ({
25
+ link,
26
+ linkColor = 'primary',
27
+ linkHandler,
28
+ clickHandler,
29
+ className,
30
+ disabled
31
+ }: Props) => {
32
+ const onClick = () => (clickHandler ? clickHandler() : linkHandler(link));
33
+
34
+ return (
35
+ <span className={className}>
36
+ <IconButton
37
+ disabled={disabled || (!link && link !== 0)}
38
+ onClick={onClick}
39
+ color={linkColor}
40
+ classes={{
41
+ root: 'inputAdornmentButton'
42
+ }}
43
+ >
44
+ <Launch className="inputAdornmentIcon" color="inherit" />
45
+ </IconButton>
46
+ </span>
47
+ );
48
+ };
49
+
50
+ export const SettingsAdornment = ({
51
+ link, linkHandler, clickHandler, className, disabled
52
+ }: Props) => {
53
+ const onClick = e => (clickHandler ? clickHandler(e) : linkHandler(link));
54
+
55
+ return (
56
+ <span className={className}>
57
+ <IconButton
58
+ disabled={disabled}
59
+ onClick={onClick}
60
+ classes={{
61
+ root: 'inputAdornmentButton ml-0-5'
62
+ }}
63
+ color="inherit"
64
+ size="small"
65
+ >
66
+ <SettingsOutlinedIcon color="inherit" />
67
+ </IconButton>
68
+ </span>
69
+ );
70
+ };