ish-ui 1.2.8 → 1.3.0

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 +10 -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 +102 -155
  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 +81 -71
  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,17 +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 React, { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
20
+ import { Chip } from '@mui/material';
22
21
  import Autocomplete from '@mui/material/Autocomplete';
23
- import { Edit } from '@mui/icons-material';
22
+ import { Edit, Tag } from '@mui/icons-material';
24
23
  import { useSelectStyles } from '../formFields';
25
24
  import AddTagMenu from './AddTagMenu';
26
25
  import EditInPlaceFieldBase from '../formFields/EditInPlaceFieldBase';
27
26
  import { getAllMenuTags, getHighlightedPartLabel, getMenuTags, stubComponent } from '../utils';
28
- import getCaretCoordinates from '../utils/DOM/getCaretCoordinates';
29
27
  import { makeAppStyles } from '../styles';
28
+ import { useAppTheme } from '../themes';
30
29
  const useStyles = makeAppStyles()((theme) => ({
31
30
  tagColorDotSmall: {
32
31
  width: theme.spacing(2),
@@ -44,19 +43,44 @@ const useStyles = makeAppStyles()((theme) => ({
44
43
  },
45
44
  invalid: {},
46
45
  }));
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;
46
+ // The menu is positioned against the text input, which the rendered Chips both
47
+ // shorten and push along. Measuring while rendering reads the layout of the
48
+ // previous commit -- before those Chips, and before the menu itself, are in the
49
+ // DOM -- so the offsets are taken in a layout effect instead, once the real
50
+ // geometry exists, and re-taken while tags are added and removed
51
+ const TagMenuPopper = ({ inputRef, menuRef, children }) => {
52
+ const [position, setPosition] = useState(undefined);
53
+ const measure = useCallback(() => {
54
+ var _a;
55
+ var _b;
56
+ const inputEl = inputRef.current;
57
+ if (!inputEl)
58
+ return;
59
+ const coords = inputEl.getBoundingClientRect();
60
+ const coordsLeft = coords.left - 32;
61
+ const fullWidth = coords.left + coords.width;
62
+ const menuWidth = (_b = (_a = menuRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 0;
63
+ setPosition(menuWidth && coordsLeft > (fullWidth - menuWidth)
64
+ ? { right: 0 }
65
+ : { left: coordsLeft });
66
+ }, [inputRef, menuRef]);
67
+ // Layout effect, so the menu is placed in the same frame it appears in
68
+ useLayoutEffect(measure, [measure]);
69
+ useEffect(() => {
70
+ var _a;
71
+ if (typeof ResizeObserver === 'undefined')
72
+ return;
73
+ const observer = new ResizeObserver(measure);
74
+ // the input resizes as Chips come and go, the wrapper reflows when they wrap
75
+ // onto another line, and the menu's own width decides which side it hangs on
76
+ [inputRef.current, (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.parentElement, menuRef.current]
77
+ .forEach(node => node && observer.observe(node));
78
+ return () => observer.disconnect();
79
+ }, [measure, inputRef, menuRef]);
80
+ return (React.createElement("div", { id: "popper",
81
+ // kept invisible rather than unmounted until measured, so that the menu
82
+ // can be measured at all, and never flashes at the pre Chip offset
83
+ style: Object.assign({ position: 'absolute', zIndex: 2, visibility: position ? undefined : 'hidden' }, position) }, children));
60
84
  };
61
85
  const getFullTag = (tagId, tags) => {
62
86
  let i = 0;
@@ -75,60 +99,18 @@ const getFullTag = (tagId, tags) => {
75
99
  ++i;
76
100
  }
77
101
  };
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
102
  function TagInputList({ input, tags, placeholder, labelAdornment, meta, label = 'Tags', disabled, className, warning, allowParentSelect, fieldClasses = {}, editIcon, variant, hideColor }) {
85
103
  const [menuIsOpen, setMenuIsOpen] = useState(false);
86
104
  const [activeTag, setActiveTag] = useState(null);
87
105
  const [isEditing, setIsEditing] = useState(false);
88
106
  const [inputValue, setInputValue] = useState('');
89
- const [currentInputString, setCurrentInputString] = useState('');
90
107
  const { classes: ownClasses, cx } = useStyles();
91
108
  const { classes: selectClasses } = useSelectStyles();
92
109
  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]);
110
+ const inputNode = useRef(undefined);
111
+ const tagMenuNode = useRef(undefined);
112
+ const menuTags = useMemo(() => (tags && input.value ? getMenuTags(tags.filter(t => allowParentSelect || t.childrenCount > 0), input.value) : []), [tags, input.value, allowParentSelect]);
108
113
  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
114
  const onTagAdd = (tag) => {
133
115
  const updated = [...input.value];
134
116
  const index = updated.findIndex(id => id === tag.tagBody.id);
@@ -139,15 +121,11 @@ function TagInputList({ input, tags, placeholder, labelAdornment, meta, label =
139
121
  updated.push(tag.tagBody.id);
140
122
  }
141
123
  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
124
  };
147
- const filterOptions = item => !item.children.length && !input.value.some(id => id === item.tagBody.id) && item.tagBody.name
125
+ const filterOptions = item => !input.value.some(id => id === item.tagBody.id) && item.tagBody.name
148
126
  .toLowerCase()
149
127
  .trim()
150
- .startsWith(currentInputString.toLowerCase().trim());
128
+ .startsWith(inputValue.toLowerCase().trim());
151
129
  const filterOptionsInner = options => options.filter(filterOptions);
152
130
  const filteredOptions = allMenuTags.filter(filterOptions);
153
131
  const getOptionText = (option, optionProps, label) => (React.createElement("div", Object.assign({}, optionProps),
@@ -158,126 +136,95 @@ function TagInputList({ input, tags, placeholder, labelAdornment, meta, label =
158
136
  const renderOption = (optionProps, option) => {
159
137
  var _a;
160
138
  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);
139
+ const highlightedLabel = getHighlightedPartLabel(label, inputValue);
162
140
  return getOptionText(option, optionProps, highlightedLabel);
163
141
  };
164
142
  const handleInputChange = e => {
165
143
  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);
144
+ setMenuIsOpen(true);
173
145
  };
174
146
  const exit = () => {
175
147
  setMenuIsOpen(false);
176
148
  setIsEditing(false);
177
149
  setActiveTag(null);
178
- if (endTagRegex.test(inputValue)) {
179
- setTimeout(() => {
180
- setInputValue(inputValue.replace(endTagRegex, '').trim());
181
- }, 100);
182
- }
183
150
  };
184
151
  const onFocus = () => {
185
152
  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
153
  };
204
154
  const handleChange = (e, value, action) => {
205
155
  if (action === 'selectOption') {
206
- onTagAdd(value);
156
+ input.onChange(value.map(v => typeof v === 'number' ? v : v.tagBody.id));
207
157
  }
208
- };
209
- useEffect(() => {
210
- let inputString = getInputString(input.value, tags);
211
- if (document.activeElement === inputNode.current && !endTagRegex.test(inputString)) {
212
- inputString += ' #';
158
+ if (action === 'removeOption') {
159
+ input.onChange(value);
213
160
  }
214
- setInputValue(inputString);
215
- }, [input.value, tags]);
216
- useEffect(() => {
217
- setCurrentInputString(getCurrentInputString(inputValue, input.value || [], allMenuTags));
218
- }, [inputValue, input.value, allMenuTags]);
161
+ setInputValue('');
162
+ };
219
163
  useEffect(() => {
220
164
  if (meta.invalid && !isEditing) {
221
165
  setIsEditing(true);
222
166
  }
223
167
  }, [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]);
168
+ // The ref contents are deliberately not dependencies: this callback is the
169
+ // popper slot component, so a new identity remounts the menu and throws away
170
+ // the measured position. TagMenuPopper reads the refs after it has mounted
232
171
  const popperAdapter = useCallback(params => {
233
- if (currentInputString || !inputNode.current) {
172
+ if (!params.open)
173
+ return;
174
+ if (inputValue || !inputNode.current) {
234
175
  return React.createElement("div", Object.assign({}, params));
235
176
  }
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]);
177
+ return (React.createElement(TagMenuPopper, { inputRef: inputNode, menuRef: tagMenuNode }, params.children));
178
+ }, [inputValue]);
244
179
  const listboxAdapter = forwardRef((params, ref) => {
245
180
  const setRef = refToSet => {
246
181
  tagMenuNode.current = refToSet;
247
182
  ref = refToSet;
248
183
  };
249
- return React.createElement("div", { ref: setRef },
184
+ // `params` (MUI's listbox props) are deliberately unused, so MUI's own
185
+ // blur-prevention mousedown handler has to be reinstated by hand
186
+ return React.createElement("div", { ref: setRef, onMouseDown: e => e.preventDefault() },
250
187
  React.createElement(AddTagMenu, { tags: menuTags, handleAdd: onTagAdd, hideColor: hideColor, activeTag: activeTag, setActiveTag: setActiveTag, allowParentSelect: allowParentSelect }));
251
188
  });
189
+ const theme = useAppTheme();
252
190
  return (React.createElement("div", { className: className, id: input.name },
253
191
  React.createElement("div", { className: cx('relative', {
254
192
  '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 }))));
193
+ }) }, React.createElement(Autocomplete, { value: input.value, multiple: true, open: menuIsOpen, options: filteredOptions, onBlur: exit, onChange: handleChange, renderOption: renderOption, filterOptions: filterOptionsInner, getOptionLabel: getOptionLabel, slots: {
194
+ popper: inputValue ? undefined : popperAdapter,
195
+ listbox: inputValue ? undefined : listboxAdapter
196
+ }, classes: {
197
+ root: classes.root,
198
+ hasPopupIcon: classes.hasPopup,
199
+ hasClearIcon: classes.hasClear,
200
+ listbox: cx(classes.listbox, fieldClasses.listbox),
201
+ inputRoot: cx(classes.inputWrapper, classes.multiple),
202
+ noOptions: 'd-none'
203
+ }, renderValue: (tagValue, getItemProps) => tagValue.map((option, index) => {
204
+ const tagProps = __rest(getItemProps({ index }), []);
205
+ const menuTag = allMenuTags.find(t => t.tagBody.id === option);
206
+ const tagColors = theme.palette.augmentColor({
207
+ color: { main: `#${menuTag.tagBody.color}` }
208
+ });
209
+ const textColors = theme.palette.augmentColor({
210
+ color: { main: tagColors.contrastText }
211
+ });
212
+ return (React.createElement(Chip, Object.assign({}, tagProps, { icon: React.createElement(Tag, null), key: menuTag.tagBody.id, label: menuTag.tagBody.name, sx: {
213
+ backgroundColor: tagColors.main,
214
+ color: tagColors.contrastText,
215
+ '& .MuiChip-icon, & .MuiChip-deleteIcon': {
216
+ color: textColors.light
217
+ },
218
+ '& .MuiChip-deleteIcon:hover': {
219
+ color: textColors.dark
220
+ }
221
+ } })));
222
+ }), 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: {
223
+ underline: fieldClasses.underline,
224
+ input: cx(disabled && classes.readonly, fieldClasses.text),
225
+ }, inputProps: Object.assign(Object.assign({}, inputProps), { ref: ref => {
226
+ inputProps.ref.current = ref;
227
+ inputNode.current = ref;
228
+ }, className: cx(inputProps.className, fieldClasses.text, 'mr-auto') }) }) }))); }, popupIcon: stubComponent(), disableListWrap: true, openOnFocus: true }))));
282
229
  }
283
230
  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,