ish-ui 1.2.9 → 1.3.1

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 (46) hide show
  1. package/dist/appBar/AppBarHelpMenu.js +1 -1
  2. package/dist/colorPicker/ColorPicker.js +2 -2
  3. package/dist/dialog/BrowserWarning.js +1 -1
  4. package/dist/dialog/message/Message.js +21 -15
  5. package/dist/dynamicSizeList/DynamicSizeList.d.ts +21 -12
  6. package/dist/dynamicSizeList/DynamicSizeList.js +30 -157
  7. package/dist/fieldMessage/styles.d.ts +2 -2
  8. package/dist/fileUploader/FileUploaderDialog.js +8 -1
  9. package/dist/formFields/AutosizeInput.d.ts +14 -0
  10. package/dist/formFields/AutosizeInput.js +156 -0
  11. package/dist/formFields/CheckboxField.d.ts +4 -2
  12. package/dist/formFields/ColoredCheckBox.js +5 -2
  13. package/dist/formFields/EditInPlaceDateTimeField.js +35 -36
  14. package/dist/formFields/EditInPlaceField.js +8 -9
  15. package/dist/formFields/EditInPlaceFieldBase.d.ts +2 -3
  16. package/dist/formFields/EditInPlaceFieldBase.js +7 -14
  17. package/dist/formFields/EditInPlaceFileField.js +2 -2
  18. package/dist/formFields/EditInPlaceMoneyField.js +2 -2
  19. package/dist/formFields/EditInPlacePhoneField.js +13 -3
  20. package/dist/formFields/EditInPlaceSearchSelect.js +6 -3
  21. package/dist/formFields/SelectCustomComponents.d.ts +18 -4
  22. package/dist/formFields/SelectCustomComponents.js +36 -20
  23. package/dist/formFields/Switch.d.ts +3 -1
  24. package/dist/markdownEditor/WysiwygEditor.js +14 -16
  25. package/dist/markdownEditor/utils/index.d.ts +1 -1
  26. package/dist/model/Fields.d.ts +1 -6
  27. package/dist/styles/makeStyles.d.ts +8 -25
  28. package/dist/styles/makeStyles.js +9 -3
  29. package/dist/tagsInput/AddTagMenu.d.ts +2 -2
  30. package/dist/tagsInput/AddTagMenu.js +8 -5
  31. package/dist/tagsInput/AddTagMenuItem.d.ts +2 -2
  32. package/dist/tagsInput/AddTagMenuItem.js +1 -1
  33. package/dist/tagsInput/TagInputList.js +112 -160
  34. package/dist/themes/ishTheme.js +13 -3
  35. package/dist/utils/DOM/index.d.ts +0 -1
  36. package/dist/utils/DOM/index.js +0 -1
  37. package/dist/utils/dates/formatTimezone.js +3 -3
  38. package/dist/utils/formatting/index.d.ts +1 -1
  39. package/dist/utils/hooks/index.d.ts +16 -0
  40. package/dist/utils/hooks/index.js +31 -1
  41. package/dist/utils/tags/index.d.ts +1 -1
  42. package/dist/utils/tags/index.js +1 -1
  43. package/package.json +85 -72
  44. package/tsconfig.prod.json +3 -0
  45. package/dist/utils/DOM/getCaretCoordinates.d.ts +0 -6
  46. package/dist/utils/DOM/getCaretCoordinates.js +0 -109
@@ -16,18 +16,16 @@ var __rest = (this && this.__rest) || function (s, e) {
16
16
  }
17
17
  return t;
18
18
  };
19
- import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
20
- import { InputAdornment, MenuItem, Select } from '@mui/material';
21
- import { ClickAwayListener } from '@mui/base';
19
+ import { Edit, Tag } from '@mui/icons-material';
20
+ import { Chip } from '@mui/material';
22
21
  import Autocomplete from '@mui/material/Autocomplete';
23
- import { Edit } from '@mui/icons-material';
24
- import { useSelectStyles } from '../formFields';
25
- import AddTagMenu from './AddTagMenu';
26
- import EditInPlaceFieldBase from '../formFields/EditInPlaceFieldBase';
27
- import { getAllMenuTags, getHighlightedPartLabel, getMenuTags, stubComponent } from '../utils';
28
- import getCaretCoordinates from '../utils/DOM/getCaretCoordinates';
22
+ import React, { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
29
23
  import { makeAppStyles } from '../styles';
30
- const useStyles = makeAppStyles()((theme) => ({
24
+ import { EditInPlaceFieldBase, useSelectStyles } from '../formFields';
25
+ import { getAllMenuTags, getHighlightedPartLabel, getMenuTags, stubComponent } from '../utils';
26
+ import { AddTagMenu } from '../tagsInput/index';
27
+ import { useAppTheme } from '../themes';
28
+ const useStyles = makeAppStyles()(theme => ({
31
29
  tagColorDotSmall: {
32
30
  width: theme.spacing(2),
33
31
  minWidth: theme.spacing(2),
@@ -44,19 +42,44 @@ const useStyles = makeAppStyles()((theme) => ({
44
42
  },
45
43
  invalid: {},
46
44
  }));
47
- const endTagRegex = /#\s*[^\w\d]*$/;
48
- const getCurrentInputString = (input, formTagIds = [], allMenuTags = []) => {
49
- let substr = input;
50
- formTagIds === null || formTagIds === void 0 ? void 0 : formTagIds.forEach(id => {
51
- const tag = allMenuTags.find(t => t.tagBody.id === id);
52
- if (tag) {
53
- substr = substr.replace('#' + tag.tagBody.name, '').trim();
54
- }
55
- });
56
- if (substr) {
57
- return substr.trim().replace(/#/g, '');
58
- }
59
- return substr;
45
+ // The menu is positioned against the text input, which the rendered Chips both
46
+ // shorten and push along. Measuring while rendering reads the layout of the
47
+ // previous commit -- before those Chips, and before the menu itself, are in the
48
+ // DOM -- so the offsets are taken in a layout effect instead, once the real
49
+ // geometry exists, and re-taken while tags are added and removed
50
+ const TagMenuPopper = ({ inputRef, menuRef, children }) => {
51
+ const [position, setPosition] = useState(undefined);
52
+ const measure = useCallback(() => {
53
+ var _a;
54
+ var _b;
55
+ const inputEl = inputRef.current;
56
+ if (!inputEl)
57
+ return;
58
+ const coords = inputEl.getBoundingClientRect();
59
+ const coordsLeft = coords.left - 32;
60
+ const fullWidth = coords.left + coords.width;
61
+ const menuWidth = (_b = (_a = menuRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 0;
62
+ setPosition(menuWidth && coordsLeft > (fullWidth - menuWidth)
63
+ ? { right: 0 }
64
+ : { left: coordsLeft });
65
+ }, [inputRef, menuRef]);
66
+ // Layout effect, so the menu is placed in the same frame it appears in
67
+ useLayoutEffect(measure, [measure]);
68
+ useEffect(() => {
69
+ var _a;
70
+ if (typeof ResizeObserver === 'undefined')
71
+ return;
72
+ const observer = new ResizeObserver(measure);
73
+ // the input resizes as Chips come and go, the wrapper reflows when they wrap
74
+ // onto another line, and the menu's own width decides which side it hangs on
75
+ [inputRef.current, (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.parentElement, menuRef.current]
76
+ .forEach(node => node && observer.observe(node));
77
+ return () => observer.disconnect();
78
+ }, [measure, inputRef, menuRef]);
79
+ return (React.createElement("div", { id: "popper",
80
+ // kept invisible rather than unmounted until measured, so that the menu
81
+ // can be measured at all, and never flashes at the pre Chip offset
82
+ style: Object.assign({ position: 'absolute', zIndex: 2, visibility: position ? undefined : 'hidden' }, position) }, children));
60
83
  };
61
84
  const getFullTag = (tagId, tags) => {
62
85
  let i = 0;
@@ -75,60 +98,18 @@ const getFullTag = (tagId, tags) => {
75
98
  ++i;
76
99
  }
77
100
  };
78
- const getInputString = (tagIds, allTags) => ((tagIds === null || tagIds === void 0 ? void 0 : tagIds.length) && (allTags === null || allTags === void 0 ? void 0 : allTags.length)
79
- ? tagIds.reduce((acc, id) => {
80
- const tag = getFullTag(id, allTags);
81
- return (tag ? `${acc}#${tag.name} ` : acc);
82
- }, '')
83
- : '');
84
101
  function TagInputList({ input, tags, placeholder, labelAdornment, meta, label = 'Tags', disabled, className, warning, allowParentSelect, fieldClasses = {}, editIcon, variant, hideColor }) {
85
102
  const [menuIsOpen, setMenuIsOpen] = useState(false);
86
103
  const [activeTag, setActiveTag] = useState(null);
87
104
  const [isEditing, setIsEditing] = useState(false);
88
105
  const [inputValue, setInputValue] = useState('');
89
- const [currentInputString, setCurrentInputString] = useState('');
90
106
  const { classes: ownClasses, cx } = useStyles();
91
107
  const { classes: selectClasses } = useSelectStyles();
92
108
  const classes = Object.assign(Object.assign({}, ownClasses), selectClasses);
93
- const InputValueForRender = useMemo(() => {
94
- var _a;
95
- if (!inputValue || !tags || !tags.length)
96
- return '';
97
- const arrayOfTags = ((_a = input === null || input === void 0 ? void 0 : input.value) === null || _a === void 0 ? void 0 : _a.length)
98
- && input.value.map(id => getFullTag(id, tags)).filter(t => t);
99
- if (!(arrayOfTags === null || arrayOfTags === void 0 ? void 0 : arrayOfTags.length))
100
- return '';
101
- return arrayOfTags.map((tag, index) => (React.createElement("span", { key: tag.id, className: cx('centeredFlex', index !== arrayOfTags.length - 1 ? 'pr-1' : '') },
102
- !hideColor && React.createElement("div", { key: tag.id, className: cx(classes.tagColorDotSmall, 'mr-0-5'), style: { background: '#' + tag.color } }),
103
- React.createElement("span", { className: "text-nowrap" }, `#${tag.name} `))));
104
- }, [tags, input.value, inputValue, hideColor]);
105
- const inputNode = useRef();
106
- const tagMenuNode = useRef();
107
- const menuTags = useMemo(() => (tags && input.value ? getMenuTags(tags.filter(t => allowParentSelect || t.childrenCount > 0), input.value.map(id => getFullTag(id, tags)).filter(t => t)) : []), [tags, input.value, allowParentSelect]);
109
+ const inputNode = useRef(undefined);
110
+ const tagMenuNode = useRef(undefined);
111
+ const menuTags = useMemo(() => (tags && input.value ? getMenuTags(tags.filter(t => allowParentSelect || t.childrenCount > 0), input.value) : []), [tags, input.value, allowParentSelect]);
108
112
  const allMenuTags = useMemo(() => getAllMenuTags(menuTags), [menuTags]);
109
- const synchronizeTags = () => {
110
- const inputString = getInputString(input.value, tags);
111
- if (inputString.trim() === inputValue.replace(endTagRegex, '').trim()) {
112
- return;
113
- }
114
- const updated = [];
115
- const current = inputValue.split('#').filter(i => i).map(i => i.trim());
116
- allMenuTags.forEach(t => {
117
- if (!t.children.length) {
118
- const index = current.findIndex(c => c === t.tagBody.name);
119
- if (index !== -1) {
120
- const addedTagsMatch = input.value.find(id => { var _a; return ((_a = getFullTag(id, tags)) === null || _a === void 0 ? void 0 : _a.name) === t.tagBody.name; });
121
- if (addedTagsMatch && addedTagsMatch !== t.tagBody.id) {
122
- return;
123
- }
124
- updated.push(t.tagBody.id);
125
- current.splice(index, 1);
126
- }
127
- }
128
- });
129
- input.onChange(updated);
130
- setInputValue(getInputString(updated, tags));
131
- };
132
113
  const onTagAdd = (tag) => {
133
114
  const updated = [...input.value];
134
115
  const index = updated.findIndex(id => id === tag.tagBody.id);
@@ -139,15 +120,11 @@ function TagInputList({ input, tags, placeholder, labelAdornment, meta, label =
139
120
  updated.push(tag.tagBody.id);
140
121
  }
141
122
  input.onChange(updated);
142
- setTimeout(() => {
143
- var _a;
144
- (_a = inputNode === null || inputNode === void 0 ? void 0 : inputNode.current) === null || _a === void 0 ? void 0 : _a.focus();
145
- }, 100);
146
123
  };
147
- const filterOptions = item => !item.children.length && !input.value.some(id => id === item.tagBody.id) && item.tagBody.name
124
+ const filterOptions = item => !input.value.some(id => id === item.tagBody.id) && item.tagBody.name
148
125
  .toLowerCase()
149
126
  .trim()
150
- .startsWith(currentInputString.toLowerCase().trim());
127
+ .startsWith(inputValue.toLowerCase().trim());
151
128
  const filterOptionsInner = options => options.filter(filterOptions);
152
129
  const filteredOptions = allMenuTags.filter(filterOptions);
153
130
  const getOptionText = (option, optionProps, label) => (React.createElement("div", Object.assign({}, optionProps),
@@ -158,126 +135,101 @@ function TagInputList({ input, tags, placeholder, labelAdornment, meta, label =
158
135
  const renderOption = (optionProps, option) => {
159
136
  var _a;
160
137
  const label = (_a = option === null || option === void 0 ? void 0 : option.tagBody) === null || _a === void 0 ? void 0 : _a.name;
161
- const highlightedLabel = getHighlightedPartLabel(label, currentInputString);
138
+ const highlightedLabel = getHighlightedPartLabel(label, inputValue);
162
139
  return getOptionText(option, optionProps, highlightedLabel);
163
140
  };
164
141
  const handleInputChange = e => {
165
142
  setInputValue(e.target.value);
166
- };
167
- const edit = () => {
168
- setIsEditing(true);
169
- setTimeout(() => {
170
- var _a;
171
- (_a = inputNode === null || inputNode === void 0 ? void 0 : inputNode.current) === null || _a === void 0 ? void 0 : _a.focus();
172
- }, 50);
143
+ setMenuIsOpen(true);
173
144
  };
174
145
  const exit = () => {
175
146
  setMenuIsOpen(false);
176
147
  setIsEditing(false);
177
148
  setActiveTag(null);
178
- if (endTagRegex.test(inputValue)) {
179
- setTimeout(() => {
180
- setInputValue(inputValue.replace(endTagRegex, '').trim());
181
- }, 100);
182
- }
183
149
  };
184
150
  const onFocus = () => {
185
151
  setMenuIsOpen(true);
186
- if (!endTagRegex.test(inputValue)) {
187
- setInputValue(inputValue + ' #');
188
- }
189
- };
190
- const onBlur = () => {
191
- if (currentInputString) {
192
- exit();
193
- synchronizeTags();
194
- }
195
- };
196
- const onTagListBlur = () => {
197
- setTimeout(() => {
198
- if (document.activeElement !== inputNode.current) {
199
- exit();
200
- synchronizeTags();
201
- }
202
- }, 60);
203
152
  };
204
153
  const handleChange = (e, value, action) => {
205
154
  if (action === 'selectOption') {
206
- onTagAdd(value);
155
+ input.onChange(value.map(v => typeof v === 'number' ? v : v.tagBody.id));
207
156
  }
208
- };
209
- useEffect(() => {
210
- let inputString = getInputString(input.value, tags);
211
- if (document.activeElement === inputNode.current && !endTagRegex.test(inputString)) {
212
- inputString += ' #';
157
+ if (action === 'removeOption') {
158
+ input.onChange(value);
213
159
  }
214
- setInputValue(inputString);
215
- }, [input.value, tags]);
216
- useEffect(() => {
217
- setCurrentInputString(getCurrentInputString(inputValue, input.value || [], allMenuTags));
218
- }, [inputValue, input.value, allMenuTags]);
160
+ setInputValue('');
161
+ };
219
162
  useEffect(() => {
220
163
  if (meta.invalid && !isEditing) {
221
164
  setIsEditing(true);
222
165
  }
223
166
  }, [meta.invalid]);
224
- useEffect(() => {
225
- if (activeTag) {
226
- const activeUpdated = allMenuTags.find(t => t.tagBody.id === activeTag.tagBody.id);
227
- if (activeUpdated) {
228
- setActiveTag(activeUpdated);
229
- }
230
- }
231
- }, [allMenuTags]);
167
+ // The ref contents are deliberately not dependencies: this callback is the
168
+ // popper slot component, so a new identity remounts the menu and throws away
169
+ // the measured position. TagMenuPopper reads the refs after it has mounted
232
170
  const popperAdapter = useCallback(params => {
233
- if (currentInputString || !inputNode.current) {
171
+ if (!params.open)
172
+ return;
173
+ if (inputValue || !inputNode.current) {
234
174
  return React.createElement("div", Object.assign({}, params));
235
175
  }
236
- const coords = getCaretCoordinates(inputNode.current, inputValue.length);
237
- const position = tagMenuNode.current
238
- && coords.left > inputNode.current.clientWidth - tagMenuNode.current.clientWidth
239
- ? { right: 0 }
240
- : { left: coords.left };
241
- return (React.createElement("div", { id: "popper", style: Object.assign(Object.assign({}, position), { position: 'absolute', zIndex: 2 }) },
242
- React.createElement(ClickAwayListener, { onClickAway: onTagListBlur }, params.children)));
243
- }, [allMenuTags, inputValue, currentInputString, inputNode.current, tagMenuNode.current, input.value]);
176
+ return (React.createElement(TagMenuPopper, { inputRef: inputNode, menuRef: tagMenuNode }, params.children));
177
+ }, [inputValue]);
244
178
  const listboxAdapter = forwardRef((params, ref) => {
245
179
  const setRef = refToSet => {
246
180
  tagMenuNode.current = refToSet;
247
181
  ref = refToSet;
248
182
  };
249
- return React.createElement("div", { ref: setRef },
183
+ // `params` (MUI's listbox props) are deliberately unused, so MUI's own
184
+ // blur-prevention mousedown handler has to be reinstated by hand
185
+ return React.createElement("div", { ref: setRef, onMouseDown: e => e.preventDefault() },
250
186
  React.createElement(AddTagMenu, { tags: menuTags, handleAdd: onTagAdd, hideColor: hideColor, activeTag: activeTag, setActiveTag: setActiveTag, allowParentSelect: allowParentSelect }));
251
187
  });
188
+ const theme = useAppTheme();
252
189
  return (React.createElement("div", { className: className, id: input.name },
253
190
  React.createElement("div", { className: cx('relative', {
254
191
  'pointer-events-none': disabled
255
- }) },
256
- React.createElement(Autocomplete, { value: null, open: menuIsOpen, options: filteredOptions, onChange: handleChange, renderOption: renderOption, filterOptions: filterOptionsInner, getOptionLabel: getOptionLabel, PopperComponent: currentInputString ? undefined : popperAdapter, ListboxComponent: currentInputString ? undefined : listboxAdapter, classes: {
257
- root: classes.root,
258
- hasPopupIcon: classes.hasPopup,
259
- hasClearIcon: classes.hasClear,
260
- listbox: cx(classes.listbox, fieldClasses.listbox),
261
- inputRoot: classes.inputWrapper,
262
- noOptions: 'd-none'
263
- }, renderInput: (_a) => {
264
- var { InputProps, inputProps } = _a, params = __rest(_a, ["InputProps", "inputProps"]);
265
- return (React.createElement(EditInPlaceFieldBase, Object.assign({}, params, { variant: variant, name: input.name, value: input.value, error: meta.error, invalid: meta === null || meta === void 0 ? void 0 : meta.invalid, label: label, warning: warning, disabled: disabled, fieldClasses: fieldClasses, shrink: Boolean(label || input.value), labelAdornment: labelAdornment, placeholder: placeholder, editIcon: editIcon === undefined ? React.createElement(Edit, null) : editIcon, InputProps: Object.assign(Object.assign({}, InputProps), { onChange: handleInputChange, onFocus,
266
- onBlur, classes: {
267
- underline: fieldClasses.underline,
268
- input: cx(disabled && classes.readonly, fieldClasses.text),
269
- }, inputProps: Object.assign(Object.assign({}, inputProps), { ref: ref => {
270
- inputProps.ref.current = ref;
271
- inputNode.current = ref;
272
- }, value: inputValue, className: cx(inputProps.className, fieldClasses.text) }) }), CustomInput: !isEditing
273
- ? React.createElement(Select, { inputRef: ref => {
274
- inputProps.ref.current = ref === null || ref === void 0 ? void 0 : ref.node;
275
- }, onFocus: edit, value: "stub", className: classes.inputWrapper, classes: {
276
- select: cx('d-flex flex-wrap cursor-text', fieldClasses.text)
277
- }, endAdornment: React.createElement(InputAdornment, { position: "end", className: cx(classes.editIcon, 'invisible') },
278
- React.createElement(Edit, { className: "test" })), IconComponent: stubComponent },
279
- React.createElement(MenuItem, { value: "stub" }, InputValueForRender || React.createElement("span", { className: "placeholderContent" }, placeholder)))
280
- : null })));
281
- }, popupIcon: stubComponent(), disableListWrap: true, openOnFocus: true }))));
192
+ }) }, React.createElement(Autocomplete, { value: input.value, multiple: true, open: menuIsOpen, options: filteredOptions, onBlur: exit, onChange: handleChange, renderOption: renderOption, filterOptions: filterOptionsInner, getOptionLabel: getOptionLabel, slots: {
193
+ popper: inputValue ? undefined : popperAdapter,
194
+ listbox: inputValue ? undefined : listboxAdapter
195
+ }, classes: {
196
+ root: classes.root,
197
+ hasPopupIcon: classes.hasPopup,
198
+ hasClearIcon: classes.hasClear,
199
+ listbox: cx(classes.listbox, fieldClasses.listbox),
200
+ inputRoot: cx(classes.inputWrapper, classes.multiple),
201
+ noOptions: 'd-none'
202
+ }, renderValue: (tagValue, getItemProps) => tagValue.map((option, index) => {
203
+ const tagProps = __rest(getItemProps({ index }), []);
204
+ const menuTag = allMenuTags.find(t => t.tagBody.id === option) || {
205
+ tagBody: {
206
+ id: option,
207
+ color: theme.palette.error.main.replace('#', ''),
208
+ name: 'Error: Tag not found!'
209
+ }
210
+ };
211
+ const tagColors = theme.palette.augmentColor({
212
+ color: { main: `#${menuTag.tagBody.color}` }
213
+ });
214
+ const textColors = theme.palette.augmentColor({
215
+ color: { main: tagColors.contrastText }
216
+ });
217
+ return (React.createElement(Chip, Object.assign({}, tagProps, { icon: React.createElement(Tag, null), key: menuTag.tagBody.id, label: menuTag.tagBody.name, sx: {
218
+ backgroundColor: tagColors.main,
219
+ color: tagColors.contrastText,
220
+ '& .MuiChip-icon, & .MuiChip-deleteIcon': {
221
+ color: textColors.light
222
+ },
223
+ '& .MuiChip-deleteIcon:hover': {
224
+ color: textColors.dark
225
+ }
226
+ } })));
227
+ }), renderInput: (_a) => { var _b; var { slotProps: { input: InputProps, htmlInput: inputProps } } = _a, params = __rest(_a, ["slotProps"]); return (React.createElement(EditInPlaceFieldBase, Object.assign({}, params, { variant: variant, name: input.name, value: input.value, error: meta.error, invalid: meta === null || meta === void 0 ? void 0 : meta.invalid, label: label, warning: warning, disabled: disabled, fieldClasses: fieldClasses, shrink: Boolean(label || input.value), labelAdornment: labelAdornment, placeholder: ((_b = input === null || input === void 0 ? void 0 : input.value) === null || _b === void 0 ? void 0 : _b.length) ? '' : placeholder, editIcon: editIcon === undefined ? React.createElement(Edit, { className: classes.expandIcon }) : editIcon, InputProps: Object.assign(Object.assign({}, InputProps), { endAdornment: null, onChange: handleInputChange, onFocus, classes: {
228
+ underline: fieldClasses.underline,
229
+ input: cx(disabled && classes.readonly, fieldClasses.text),
230
+ }, inputProps: Object.assign(Object.assign({}, inputProps), { ref: ref => {
231
+ inputProps.ref.current = ref;
232
+ inputNode.current = ref;
233
+ }, className: cx(inputProps.className, fieldClasses.text, 'mr-auto') }) }) }))); }, popupIcon: stubComponent(), disableListWrap: true, openOnFocus: true }))));
282
234
  }
283
235
  export default TagInputList;
@@ -7,8 +7,14 @@
7
7
  */
8
8
  import { alpha, createTheme, darken } from '@mui/material/styles';
9
9
  import { DarkThemeKey, DefaultThemeKey, HighcontrastThemeKey, MonochromeThemeKey, } from '../model';
10
- import createPalette from '@mui/material/styles/createPalette';
11
10
  import { grey, yellow } from '@mui/material/colors';
11
+ /**
12
+ * MUI 9 no longer exports `createPalette` from any public entry point.
13
+ * `createTheme` resolves palette options into a full `Palette` the same way,
14
+ * which the override helpers below depend on — they read resolved values such
15
+ * as `palette.text.primary` that plain `PaletteOptions` does not carry.
16
+ */
17
+ const createPalette = (palette) => createTheme({ palette: palette }).palette;
12
18
  const createOverrides = (palette) => ({
13
19
  components: {
14
20
  MuiButtonBase: {
@@ -133,8 +139,12 @@ const createOverrides = (palette) => ({
133
139
  },
134
140
  },
135
141
  },
136
- inputMultiline: {
137
- lineHeight: '1.5em'
142
+ // MUI 9 dropped the compound `inputMultiline` class key; the same
143
+ // element is now reached as the input inside a multiline root.
144
+ multiline: {
145
+ '& .MuiInputBase-input': {
146
+ lineHeight: '1.5em'
147
+ }
138
148
  }
139
149
  }
140
150
  },
@@ -1,5 +1,4 @@
1
1
  export declare const countLines: (el: HTMLElement) => number;
2
2
  export declare const countWidth: (el: string, container: Element) => number;
3
- export * from './getCaretCoordinates';
4
3
  export * from './getOS';
5
4
  export * from './Links';
@@ -27,6 +27,5 @@ export const countWidth = (el, container) => {
27
27
  document.body.removeChild(testContainer);
28
28
  return width;
29
29
  };
30
- export * from './getCaretCoordinates';
31
30
  export * from './getOS';
32
31
  export * from './Links';
@@ -8,11 +8,11 @@
8
8
  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
9
  * See the GNU Affero General Public License for more details.
10
10
  */
11
- import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
11
+ import { fromZonedTime, toZonedTime } from 'date-fns-tz';
12
12
  export const appendTimezone = (date, timezone) => {
13
13
  let result = date;
14
14
  try {
15
- result = utcToZonedTime(date, timezone);
15
+ result = toZonedTime(date, timezone);
16
16
  }
17
17
  catch (e) {
18
18
  // eslint-disable-next-line no-console
@@ -24,7 +24,7 @@ export const appendTimezone = (date, timezone) => {
24
24
  export const prependTimezone = (date, timezone) => {
25
25
  let result = date;
26
26
  try {
27
- result = zonedTimeToUtc(date, timezone);
27
+ result = fromZonedTime(date, timezone);
28
28
  }
29
29
  catch (e) {
30
30
  // eslint-disable-next-line no-console
@@ -1,6 +1,6 @@
1
1
  import { SelectItemDefault } from '../../model';
2
2
  export declare const getHighlightedPartLabel: (label: string, highlighted: string, options?: any) => any;
3
- export declare const sortDefaultSelectItems: (a: SelectItemDefault, b: SelectItemDefault) => 1 | -1;
3
+ export declare const sortDefaultSelectItems: (a: SelectItemDefault, b: SelectItemDefault) => -1 | 1;
4
4
  export declare const mapSelectItems: (i: any) => SelectItemDefault;
5
5
  export declare const stringArrayToSelectItems: (arr: string[]) => SelectItemDefault[];
6
6
  export * from './PhoneMasks';
@@ -1 +1,17 @@
1
+ import { FocusEvent } from 'react';
1
2
  export declare const usePrevious: <T = any>(value: any, initial?: any) => T;
3
+ /**
4
+ * Tracks focus for a whole widget instead of a single field.
5
+ *
6
+ * `focusout` bubbles, so one handler on the container sees focus leaving any
7
+ * descendant and `relatedTarget` says where it went. Focus moving between
8
+ * descendants — an input to a menu item, for instance — is not a leave, so
9
+ * popups rendered inside the container stay open on interaction and close
10
+ * only when focus really leaves (tab away, another field, click on the page).
11
+ *
12
+ * Spread the returned handler as `onBlur` on the element holding `containerRef`.
13
+ */
14
+ export declare const useFocusWithin: <T extends HTMLElement = HTMLDivElement>(onFocusLeave: () => void) => {
15
+ containerRef: import("react").RefObject<T>;
16
+ onBlurWithin: (e: FocusEvent) => void;
17
+ };
@@ -5,7 +5,7 @@
5
5
  *
6
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
7
  */
8
- import { useEffect, useRef } from 'react';
8
+ import { useCallback, useEffect, useRef } from 'react';
9
9
  export const usePrevious = (value, initial) => {
10
10
  const ref = useRef(initial);
11
11
  useEffect(() => {
@@ -13,3 +13,33 @@ export const usePrevious = (value, initial) => {
13
13
  }, [value]);
14
14
  return ref.current;
15
15
  };
16
+ /**
17
+ * Tracks focus for a whole widget instead of a single field.
18
+ *
19
+ * `focusout` bubbles, so one handler on the container sees focus leaving any
20
+ * descendant and `relatedTarget` says where it went. Focus moving between
21
+ * descendants — an input to a menu item, for instance — is not a leave, so
22
+ * popups rendered inside the container stay open on interaction and close
23
+ * only when focus really leaves (tab away, another field, click on the page).
24
+ *
25
+ * Spread the returned handler as `onBlur` on the element holding `containerRef`.
26
+ */
27
+ export const useFocusWithin = (onFocusLeave) => {
28
+ const containerRef = useRef(null);
29
+ const onFocusLeaveRef = useRef(onFocusLeave);
30
+ onFocusLeaveRef.current = onFocusLeave;
31
+ const onBlurWithin = useCallback((e) => {
32
+ var _a;
33
+ if (e.relatedTarget && ((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.relatedTarget))) {
34
+ return;
35
+ }
36
+ // relatedTarget is null whenever the next focus target isn't focusable
37
+ // (and in Safari for buttons), so re-check where focus actually landed
38
+ requestAnimationFrame(() => {
39
+ if (containerRef.current && !containerRef.current.contains(document.activeElement)) {
40
+ onFocusLeaveRef.current();
41
+ }
42
+ });
43
+ }, []);
44
+ return { containerRef, onBlurWithin };
45
+ };
@@ -1,3 +1,3 @@
1
1
  import { MenuTag, TagLike } from '../../model';
2
2
  export declare const getAllMenuTags: (tags: MenuTag<TagLike>[], res?: MenuTag<TagLike>[], path?: string) => MenuTag<TagLike>[];
3
- export declare const getMenuTags: (allTags: TagLike[], addedTags: TagLike[], prefix?: string, queryPrefix?: string, entity?: string, path?: string, parent?: MenuTag<TagLike>) => MenuTag<TagLike>[];
3
+ export declare const getMenuTags: (allTags: TagLike[], addedTags: number[], prefix?: string, queryPrefix?: string, entity?: string, path?: string, parent?: MenuTag<TagLike>) => MenuTag<TagLike>[];
@@ -18,7 +18,7 @@ export const getAllMenuTags = (tags, res, path = '') => {
18
18
  return result;
19
19
  };
20
20
  export const getMenuTags = (allTags, addedTags, prefix, queryPrefix, entity, path, parent) => allTags.map(t => {
21
- const active = addedTags.find(i => i.id === t.id);
21
+ const active = addedTags.find(id => id === t.id);
22
22
  const tag = {
23
23
  active: Boolean(active),
24
24
  tagBody: t,