ish-ui 1.3.4 → 1.3.6

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ish UI
2
2
 
3
- UI elements for onCourse and other ish apps
3
+ UI elements for onCourse apps
4
4
 
5
5
  [![npm latest package](https://img.shields.io/npm/v/ish-ui/latest.svg)](https://www.npmjs.com/package/ish-ui)
6
6
 
@@ -77,7 +77,7 @@ const useStyles = makeAppStyles()(({ spacing, palette, zIndex, transitions }, p,
77
77
  marginBottom: spacing(1.25)
78
78
  },
79
79
  customAlfa: {
80
- '& > div > div:first-child > div': {
80
+ '& > div > div:first-of-type > div': {
81
81
  background: 'none !important'
82
82
  }
83
83
  },
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
- interface AutosizeInputProps extends React.ComponentPropsWithRef<"input"> {
2
+ import { InputBaseComponentProps } from '@mui/material';
3
+ type InputElementProps = React.ComponentPropsWithRef<"input">;
4
+ interface AutosizeInputProps extends InputElementProps {
3
5
  placeholder?: string;
4
6
  /**
5
7
  * Space kept free after the text, in px. Defaults to the advance width of a
@@ -9,6 +11,7 @@ interface AutosizeInputProps extends React.ComponentPropsWithRef<"input"> {
9
11
  reserveWidth?: number;
10
12
  /** MUI InputBase hands the DOM node over through this prop instead of `ref`. */
11
13
  inputRef?: React.Ref<HTMLInputElement>;
14
+ inputComponent?: React.ElementType<InputBaseComponentProps> | undefined;
12
15
  }
13
16
  export declare const AutosizeInput: React.FC<AutosizeInputProps>;
14
17
  export {};
@@ -86,7 +86,7 @@ const getSpaceWidth = (probe, typographySignature) => {
86
86
  return spaceWidth;
87
87
  };
88
88
  export const AutosizeInput = (_a) => {
89
- var { placeholder, reserveWidth, inputRef, ref, value } = _a, props = __rest(_a, ["placeholder", "reserveWidth", "inputRef", "ref", "value"]);
89
+ var { placeholder, inputComponent, reserveWidth, inputRef, ref, value } = _a, props = __rest(_a, ["placeholder", "inputComponent", "reserveWidth", "inputRef", "ref", "value"]);
90
90
  const [width, setWidth] = useState(0);
91
91
  const spanRef = useRef(null);
92
92
  const probeRef = useRef(null);
@@ -145,10 +145,17 @@ export const AutosizeInput = (_a) => {
145
145
  if (props.onChange)
146
146
  props.onChange(e);
147
147
  };
148
+ // `React.ElementType<InputBaseComponentProps>` widens to every intrinsic tag,
149
+ // because InputBaseComponentProps carries an `[arbitrary: string]: any` index
150
+ // signature. The prop keeps that type for compatibility with MUI's InputBase,
151
+ // but rendering needs the narrower, input shaped contract this component
152
+ // actually passes. Falling back to the `input` tag rather than a wrapper
153
+ // component also keeps the element identity stable across renders.
154
+ const InputElement = (inputComponent !== null && inputComponent !== void 0 ? inputComponent : "input");
148
155
  return (React.createElement("div", { style: { display: "inline-block", position: "relative" } },
149
156
  React.createElement("span", { ref: spanRef, style: measureStyle, "aria-hidden": true }, value || placeholder || ""),
150
157
  React.createElement("span", { ref: probeRef, style: measureStyle, "aria-hidden": true }),
151
- React.createElement("input", Object.assign({}, props, { ref: setInputNode, type: "text", value: value, onChange: handleChange, placeholder: placeholder, style: Object.assign(Object.assign({}, props.style), { letterSpacing: "inherit", textTransform: "inherit", width: `${width}px`, boxSizing: "border-box",
158
+ React.createElement(InputElement, Object.assign({}, props, { ref: setInputNode, type: "text", value: value, onChange: handleChange, placeholder: placeholder, style: Object.assign(Object.assign({}, props.style), { letterSpacing: "inherit", textTransform: "inherit", width: `${width}px`, boxSizing: "border-box",
152
159
  // CRITICAL: Padding and borders must be 0, the measuring span has none either
153
160
  paddingLeft: 0, paddingRight: 0, borderLeft: "none", borderRight: "none", margin: 0, textOverflow: "unset" }) }))));
154
161
  };
@@ -37,14 +37,14 @@ const useStyles = makeAppStyles()((theme, p, classes) => ({
37
37
  },
38
38
  hideArrows: {
39
39
  '&::-webkit-outer-spin-button': {
40
- '-webkit-appearance': 'none',
40
+ 'WebkitAppearance': 'none',
41
41
  margin: 0
42
42
  },
43
43
  '&::-webkit-inner-spin-button': {
44
- '-webkit-appearance': 'none',
44
+ 'WebkitAppearance': 'none',
45
45
  margin: 0
46
46
  },
47
- '-moz-appearance': 'textfield'
47
+ 'MozAppearance': 'textfield'
48
48
  },
49
49
  inlineInput: {
50
50
  padding: 0,
@@ -73,6 +73,11 @@ const EditInPlaceFieldBase = ({ invalid, className, inline, label, fieldClasses
73
73
  return OutlinedInput;
74
74
  }
75
75
  }, [variant]);
76
+ const inputComponent = useMemo((props) => inline
77
+ ? (InputProps === null || InputProps === void 0 ? void 0 : InputProps.inputComponent)
78
+ ? props => React.createElement(AutosizeInput, Object.assign({}, props, { inputComponent: InputProps.inputComponent }))
79
+ : AutosizeInput
80
+ : InputProps === null || InputProps === void 0 ? void 0 : InputProps.inputComponent, [inline, InputProps === null || InputProps === void 0 ? void 0 : InputProps.inputComponent]);
76
81
  return (React.createElement(FormControl, { fullWidth: true, error: invalid, variant: variant, margin: "none", className: cx(className, classes.inputWrapper) },
77
82
  label && (React.createElement(InputLabel, { classes: {
78
83
  root: cx(fieldClasses.label, 'd-flex', 'overflow-visible', rightAligned && classes.rightLabel),
@@ -80,7 +85,7 @@ const EditInPlaceFieldBase = ({ invalid, className, inline, label, fieldClasses
80
85
  React.createElement("span", { className: classes.label }, label),
81
86
  labelAdornment)),
82
87
  CustomInput ||
83
- React.createElement(InputComp, Object.assign({}, InputProps || {}, { inputComponent: inline ? AutosizeInput : undefined, inputRef: inputNode, inputProps: Object.assign({ placeholder,
88
+ React.createElement(InputComp, Object.assign({}, InputProps || {}, { inputComponent: inputComponent, inputRef: inputNode, inputProps: Object.assign({ placeholder,
84
89
  disabled, className: cx(fieldClasses.text, {
85
90
  [classes.inlineInput]: inline,
86
91
  [classes.readonly]: disabled,
@@ -34,7 +34,7 @@ function EditInPlaceMoneyField(_a) {
34
34
  var _b;
35
35
  var { InputProps, currencySymbol, className } = _a, restProps = __rest(_a, ["InputProps", "currencySymbol", "className"]);
36
36
  const { cx } = useStyles();
37
- return (React.createElement(EditInPlaceField, Object.assign({}, restProps, { preformatDisplayValue: preformatDisplayValue, InputProps: Object.assign(Object.assign({}, InputProps), { startAdornment: React.createElement(InputAdornment, { position: "start", className: (_b = restProps.fieldClasses) === null || _b === void 0 ? void 0 : _b.text },
37
+ return (React.createElement(EditInPlaceField, Object.assign({}, restProps, { preformatDisplayValue: preformatDisplayValue, InputProps: Object.assign(Object.assign({}, InputProps), { startAdornment: React.createElement(InputAdornment, { position: "start", className: cx((_b = restProps.fieldClasses) === null || _b === void 0 ? void 0 : _b.text, restProps.inline && 'mr-0-5') },
38
38
  React.createElement("span", null, currencySymbol)), inputComponent: NumberFormatCustom }), className: cx('money', className) })));
39
39
  }
40
40
  export default EditInPlaceMoneyField;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright ish group pty ltd 2023.
2
+ * Copyright ish group pty ltd 2026.
3
3
  *
4
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
5
  *
@@ -23,10 +23,9 @@ import React, { useCallback, useContext, useEffect, useMemo, useRef, useState }
23
23
  import CloseIcon from '@mui/icons-material/Close';
24
24
  import ExpandMore from '@mui/icons-material/ExpandMore';
25
25
  import { ListboxComponent, useSelectStyles } from './SelectCustomComponents';
26
- import EditInPlaceFieldBase from './EditInPlaceFieldBase';
26
+ import EditInPlaceFieldBase from '../formFields/EditInPlaceFieldBase';
27
27
  import { makeAppStyles } from '../styles';
28
- import { getHighlightedPartLabel, stubComponent } from '../utils';
29
- import { usePrevious } from '../utils/hooks';
28
+ import { getHighlightedPartLabel, stubComponent, usePrevious } from '../utils';
30
29
  const useStyles = makeAppStyles()({
31
30
  popper: {
32
31
  zIndex: 1400
@@ -52,6 +51,7 @@ function EditInPlaceSearchSelect({ label, disabled, className, labelAdornment, i
52
51
  const { classes: ownClasses, cx } = useStyles();
53
52
  const { classes: selectClasses } = useSelectStyles();
54
53
  const classes = Object.assign(Object.assign({}, selectClasses), ownClasses);
54
+ const hasCustomListbox = !(categoryKey || inline);
55
55
  const sortedItems = useMemo(() => {
56
56
  const sorted = items && (sort
57
57
  ? [...items].sort(typeof sort === 'function'
@@ -216,13 +216,16 @@ function EditInPlaceSearchSelect({ label, disabled, className, labelAdornment, i
216
216
  return option[selectValueMark] === value;
217
217
  };
218
218
  const renderOption = (optionProps, data) => {
219
- if (typeof itemRenderer === 'function') {
220
- return [itemRenderer(getHighlightedPartLabel(getOptionLabel(data), searchValue), data, searchValue, optionProps), optionProps];
219
+ const optionLabel = getOptionLabel(data);
220
+ const hasItemRenderrer = typeof itemRenderer === 'function';
221
+ const highlightedPart = getHighlightedPartLabel(optionLabel, searchValue, hasCustomListbox || hasItemRenderrer ? undefined : optionProps);
222
+ if (!hasCustomListbox) {
223
+ return hasItemRenderrer ? itemRenderer(highlightedPart, data, searchValue, optionProps) : highlightedPart;
221
224
  }
222
- if (inline || categoryKey) {
223
- return getHighlightedPartLabel(getOptionLabel(data), searchValue, optionProps);
225
+ if (hasItemRenderrer) {
226
+ return [itemRenderer(highlightedPart, data, searchValue, optionProps), optionProps];
224
227
  }
225
- return [getHighlightedPartLabel(getOptionLabel(data), searchValue), optionProps];
228
+ return [highlightedPart, optionProps];
226
229
  };
227
230
  const selectedOption = useMemo(() => sortedItems.find(i => multiple
228
231
  ? (input.value || []).includes(i[selectValueMark])
@@ -266,13 +269,12 @@ function EditInPlaceSearchSelect({ label, disabled, className, labelAdornment, i
266
269
  ? valueRenderer(displayedValue, valueForRender, searchValue, { value: valueForRender && valueForRender[selectValueMark] })
267
270
  : null;
268
271
  }, [selectedOption, searchValue, displayedValue, selectValueMark, returnType, valueRenderer, input.value]);
269
- const renderIcons = useMemo(() => !disabled && (loading
270
- ? React.createElement(CircularProgress, { size: 24, thickness: 4, className: fieldClasses.loading })
271
- : (React.createElement("div", null,
272
- allowEmpty && input.value && (React.createElement(IconButton, { size: "small", onClick: onClear, color: "inherit" },
273
- React.createElement(CloseIcon, { fontSize: "inherit" }))),
274
- editIcon === undefined ? React.createElement(IconButton, { size: "small", color: "inherit", className: cx(classes.expandIcon, 'pl-0 pr-0', inline && 'p-0'), disableRipple: true },
275
- React.createElement(ExpandMore, { className: inline && 'fsInherit' })) : editIcon))), [disabled, loading, inline, allowEmpty, input.value]);
272
+ const renderLoading = useMemo(() => React.createElement(CircularProgress, Object.assign({}, inline ? { sx: { padding: 0.5 } } : {}, { size: 24, thickness: 4, className: fieldClasses.loading })), [fieldClasses.loading, inline]);
273
+ const renderIcons = useMemo(() => !disabled && (React.createElement("div", null,
274
+ allowEmpty && input.value && (React.createElement(IconButton, { size: "small", onClick: onClear, color: "inherit" },
275
+ React.createElement(CloseIcon, { fontSize: "inherit" }))),
276
+ editIcon === undefined ? React.createElement(IconButton, { size: "small", color: "inherit", className: cx(classes.expandIcon, 'pl-0 pr-0', inline && 'p-0'), disableRipple: true },
277
+ React.createElement(ExpandMore, { className: inline && 'fsInherit' })) : editIcon)), [disabled, inline, allowEmpty, input.value]);
276
278
  const inputValue = useMemo(() => {
277
279
  if (multiple) {
278
280
  return (input.value || []).map(v => sortedItems.find(s => s[selectValueMark] === v));
@@ -285,7 +287,7 @@ function EditInPlaceSearchSelect({ label, disabled, className, labelAdornment, i
285
287
  const renderInput = useCallback((_a) => {
286
288
  var { slotProps: { input: InputProps, htmlInput: inputProps } } = _a, params = __rest(_a, ["slotProps"]);
287
289
  const value = (isEditing ? searchValue : (typeof displayedValue === 'string' ? displayedValue : ''));
288
- return (React.createElement(EditInPlaceFieldBase, Object.assign({}, params, { variant: variant, key: input.name, name: input.name, value: value, error: error, invalid: hasError || invalid, inline: inline, label: label, warning: warning, fieldClasses: fieldClasses, rightAligned: rightAligned, shrink: Boolean(label || input.value), labelAdornment: labelAdornment, placeholder: renderedPlaceholder, editIcon: !hideEditIcon && renderIcons, InputProps: Object.assign(Object.assign({}, InputProps), { endAdornment: null, onChange: handleInputChange, onFocus: edit, onClick: onEditButtonFocus, onBlur,
290
+ return (React.createElement(EditInPlaceFieldBase, Object.assign({}, params, { variant: variant, key: input.name, name: input.name, value: value, error: error, invalid: hasError || invalid, inline: inline, label: label, warning: warning, fieldClasses: fieldClasses, rightAligned: rightAligned, shrink: Boolean(label || input.value), labelAdornment: labelAdornment, placeholder: renderedPlaceholder, editIcon: loading ? renderLoading : (!hideEditIcon && renderIcons), InputProps: Object.assign(Object.assign({}, InputProps), { endAdornment: null, onChange: handleInputChange, onFocus: edit, onClick: onEditButtonFocus, onBlur,
289
291
  disableUnderline, classes: {
290
292
  underline: fieldClasses.underline,
291
293
  input: cx(disabled && classes.readonly, fieldClasses.text),
@@ -300,7 +302,7 @@ function EditInPlaceSearchSelect({ label, disabled, className, labelAdornment, i
300
302
  inputProps.ref.current = ref === null || ref === void 0 ? void 0 : ref.node;
301
303
  }, classes: {
302
304
  select: cx('cursor-text', fieldClasses.text)
303
- }, onFocus: edit, value: (returnType === 'object' ? input.value[selectValueMark] : input.value) || '', endAdornment: React.createElement(InputAdornment, { position: "end", className: 'd-none' }, renderIcons), IconComponent: stubComponent }), renderValue)
305
+ }, onFocus: edit, value: (returnType === 'object' ? input.value[selectValueMark] : input.value) || '', endAdornment: loading ? renderLoading : React.createElement(InputAdornment, { position: "end", className: 'd-none' }, renderIcons), IconComponent: stubComponent }), renderValue)
304
306
  : null })));
305
307
  }, [
306
308
  variant,
@@ -354,7 +356,7 @@ function EditInPlaceSearchSelect({ label, disabled, className, labelAdornment, i
354
356
  popper: classes.popper,
355
357
  groupUl: 'm-0'
356
358
  }, groupBy: categoryKey && (option => option[categoryKey]), renderOption: renderOption, getOptionLabel: getOptionLabel, filterOptions: filterItems, slots: {
357
- listbox: inline || categoryKey ? undefined : ListBoxAdapter,
359
+ listbox: hasCustomListbox ? ListBoxAdapter : undefined,
358
360
  popper: categoryKey ? undefined : PopperAdapter,
359
361
  }, renderInput: renderInput, disableListWrap: true, openOnFocus: true, fullWidth: true }))));
360
362
  }
@@ -11,12 +11,12 @@ export declare const useSelectStyles: (params: void, muiStyleOverridesParams?: {
11
11
  };
12
12
  interface ListRowProps {
13
13
  data: {
14
- children: [
14
+ children: Array<[
15
15
  [
16
16
  React.ReactNode
17
17
  ],
18
18
  React.HTMLAttributes<HTMLLIElement>
19
- ];
19
+ ]>;
20
20
  selectAdornment: EditInPlaceSearchSelectFieldProps<any, any>['selectAdornment'];
21
21
  };
22
22
  index: number;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright ish group pty ltd 2023.
2
+ * Copyright ish group pty ltd 2026.
3
3
  *
4
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
5
  *
@@ -34,8 +34,8 @@ import { List } from 'react-window';
34
34
  import { green } from '@mui/material/colors';
35
35
  import { ListItemButton } from '@mui/material';
36
36
  import Tooltip from '@mui/material/Tooltip';
37
- import { makeAppStyles } from '../styles';
38
37
  import { useStyles } from 'tss-react/mui';
38
+ import { makeAppStyles } from '../styles';
39
39
  export const useSelectStyles = makeAppStyles()((theme, p, classes) => ({
40
40
  bottomMargin: {
41
41
  marginBottom: `${theme.spacing(1) + 1}`
@@ -132,10 +132,15 @@ export const useSelectStyles = makeAppStyles()((theme, p, classes) => ({
132
132
  }
133
133
  },
134
134
  }));
135
- export const ListRow = React.memo(({ data: { children, selectAdornment }, index, style }) => React.createElement(ListItemButton, Object.assign({}, children[index][1], { selected: children[index][1]['aria-selected'], style: style, key: index }), (((selectAdornment === null || selectAdornment === void 0 ? void 0 : selectAdornment.position) === 'start' && index === 0)
136
- || ((selectAdornment === null || selectAdornment === void 0 ? void 0 : selectAdornment.position) === 'end' && index === children.length))
137
- ? selectAdornment.content
138
- : children[index][0]));
135
+ export const ListRow = React.memo(({ data: { children, selectAdornment }, index, style }) => {
136
+ if (!children[index])
137
+ return null;
138
+ const [content, props] = children[index];
139
+ return React.createElement(ListItemButton, Object.assign({ component: 'li' }, props, { selected: Boolean(props['aria-selected']), style: style, key: index }), (((selectAdornment === null || selectAdornment === void 0 ? void 0 : selectAdornment.position) === 'start' && index === 0)
140
+ || ((selectAdornment === null || selectAdornment === void 0 ? void 0 : selectAdornment.position) === 'end' && index === children.length))
141
+ ? selectAdornment.content
142
+ : content);
143
+ });
139
144
  export const ListboxComponent = React.forwardRef((props, ref) => {
140
145
  const { children, rowHeight, remoteRowCount, loadMoreRows, classes, loading, selectAdornment, fieldClasses, ownerState } = props, other = __rest(props, ["children", "rowHeight", "remoteRowCount", "loadMoreRows", "classes", "loading", "selectAdornment", "fieldClasses", "ownerState"]);
141
146
  const { cx } = useStyles();
@@ -23,8 +23,8 @@ export const animateStyles = Object.assign(Object.assign(Object.assign(Object.as
23
23
  from: Object.assign(Object.assign({}, transform('translate3d(80px, 0, 0)')), prefixer('animationDuration', '0.5s', ['webkit', 'spec'])),
24
24
  to: Object.assign(Object.assign({}, transform('translate3d(0px, 0, 0)')), prefixer('animationDuration', '0s', ['webkit', 'spec']))
25
25
  })), keyframes('rotateOutCustom', {
26
- from: Object.assign({}, prefixer('transform-origin', 'center', ['webkit', 'spec'])),
27
- to: Object.assign(Object.assign({}, transform('rotate3d(0, 0, 1, 720deg)')), prefixer('transform-origin', 'center', ['webkit', 'spec']))
26
+ from: Object.assign({}, prefixer('transformOrigin', 'center', ['webkit', 'spec'])),
27
+ to: Object.assign(Object.assign({}, transform('rotate3d(0, 0, 1, 720deg)')), prefixer('transformOrigin', 'center', ['webkit', 'spec']))
28
28
  }, Object.assign({}, prefixer('animationDuration', '2.5s', ['webkit', 'spec'])))), keyframes('shake', {
29
29
  '10%, 90%': Object.assign({}, transform('translate3d(-2px, 0, 0)')),
30
30
  '20%, 80%': Object.assign({}, transform('translate3d(4px, 0, 0)')),
@@ -189,7 +189,7 @@ function TagInputList({ input, tags, placeholder, labelAdornment, meta, label =
189
189
  return (React.createElement("div", { className: className, id: input.name },
190
190
  React.createElement("div", { className: cx('relative', {
191
191
  'pointer-events-none': disabled
192
- }) }, React.createElement(Autocomplete, { value: input.value, multiple: true, open: menuIsOpen, options: filteredOptions, onBlur: exit, onChange: handleChange, renderOption: renderOption, filterOptions: filterOptionsInner, getOptionLabel: getOptionLabel, slots: {
192
+ }) }, React.createElement(Autocomplete, { value: (input === null || input === void 0 ? void 0 : input.value) || [], multiple: true, open: menuIsOpen, options: filteredOptions, onBlur: exit, onChange: handleChange, renderOption: renderOption, filterOptions: filterOptionsInner, getOptionLabel: getOptionLabel, slots: {
193
193
  popper: inputValue || !trackCarretPosition ? undefined : popperAdapter,
194
194
  listbox: inputValue ? undefined : listboxAdapter
195
195
  }, classes: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ish-ui",
3
3
  "sideEffects": false,
4
- "version": "1.3.4",
4
+ "version": "1.3.6",
5
5
  "description": "UI elements for onCourse and other ish apps",
6
6
  "main": "main.ts",
7
7
  "devDependencies": {