@react-fabric/form 1.0.0-beta.9 → 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 (215) hide show
  1. package/{src/input/ArrayInput.module.css → css/_arrayInput.css} +8 -8
  2. package/css/_colorPicker.css +95 -0
  3. package/{src/internal/Field.module.css → css/_field.css} +94 -33
  4. package/{src/input/File.module.css → css/_file.css} +21 -6
  5. package/css/styles.css +27 -0
  6. package/dist/cjs/builder/FileField.js +107 -78
  7. package/dist/cjs/builder/FormField.js +60 -39
  8. package/dist/cjs/builder/SchemaEditor.js +72 -0
  9. package/dist/cjs/builder/components/AddSchemaFields.js +64 -0
  10. package/dist/cjs/builder/components/SchemaArrayForm.js +146 -0
  11. package/dist/cjs/builder/components/SchemaField.js +423 -0
  12. package/dist/cjs/form/Controller.js +66 -19
  13. package/dist/cjs/form/Form.js +97 -39
  14. package/dist/cjs/form/useForm.js +32 -0
  15. package/dist/cjs/hooks/useFileUploader.js +74 -45
  16. package/dist/cjs/hooks/useFormBuilder.js +85 -31
  17. package/dist/cjs/i18n/{ar.json.js → ar/form.json.js} +5 -4
  18. package/dist/cjs/i18n/{en.json.js → en/form.json.js} +5 -5
  19. package/dist/cjs/i18n/index.js +4 -4
  20. package/dist/cjs/index.js +41 -2
  21. package/dist/cjs/input/ArrayInput.js +259 -135
  22. package/dist/cjs/input/AvatarInput.js +99 -63
  23. package/dist/cjs/input/Checkbox.js +24 -25
  24. package/dist/cjs/input/ColorInput.js +138 -81
  25. package/dist/cjs/input/ColorPicker.js +78 -95
  26. package/dist/cjs/input/DateInput.js +192 -0
  27. package/dist/cjs/input/Field.js +21 -20
  28. package/dist/cjs/input/File.js +85 -80
  29. package/dist/cjs/input/Hidden.js +27 -13
  30. package/dist/cjs/input/Input.js +49 -48
  31. package/dist/cjs/input/Number.js +46 -44
  32. package/dist/cjs/input/Password.js +51 -49
  33. package/dist/cjs/input/Radio.js +26 -16
  34. package/dist/cjs/input/RadioGroup.js +26 -11
  35. package/dist/cjs/input/{Range.js → RangeSlider.js} +61 -66
  36. package/dist/cjs/input/Search.js +58 -64
  37. package/dist/cjs/input/Slider.js +57 -66
  38. package/dist/cjs/input/Switch.js +29 -27
  39. package/dist/cjs/input/Textarea.js +65 -53
  40. package/dist/cjs/internal/ErrorIcon.js +9 -9
  41. package/dist/cjs/internal/FieldWrapper.js +23 -13
  42. package/dist/cjs/internal/InputWrapper.js +85 -87
  43. package/dist/cjs/ranger/index.js +7 -7
  44. package/dist/cjs/ranger/utils.js +9 -4
  45. package/dist/cjs/select/AutoComplete.js +93 -76
  46. package/dist/cjs/select/DualList.js +235 -154
  47. package/dist/cjs/select/List.js +122 -89
  48. package/dist/cjs/select/Options.js +58 -54
  49. package/dist/cjs/select/Select.js +119 -89
  50. package/dist/cjs/select/helpers.js +5 -5
  51. package/dist/cjs/select/useSelect.js +83 -58
  52. package/dist/cjs/types/schema.js +43 -0
  53. package/dist/cjs/utils/eventHandlers.js +16 -6
  54. package/dist/cjs/utils/getColor.js +6 -6
  55. package/dist/cjs/utils/getProps.js +49 -0
  56. package/dist/esm/builder/FileField.js +109 -80
  57. package/dist/esm/builder/FormField.js +60 -39
  58. package/dist/esm/builder/SchemaEditor.js +72 -0
  59. package/dist/esm/builder/components/AddSchemaFields.js +64 -0
  60. package/dist/esm/builder/components/SchemaArrayForm.js +146 -0
  61. package/dist/esm/builder/components/SchemaField.js +423 -0
  62. package/dist/esm/form/Controller.js +66 -19
  63. package/dist/esm/form/Form.js +97 -39
  64. package/dist/esm/form/useForm.js +32 -0
  65. package/dist/esm/hooks/useFileUploader.js +75 -46
  66. package/dist/esm/hooks/useFormBuilder.js +84 -30
  67. package/dist/esm/i18n/{ar.json.js → ar/form.json.js} +5 -4
  68. package/dist/esm/i18n/{en.json.js → en/form.json.js} +5 -5
  69. package/dist/esm/i18n/index.js +2 -2
  70. package/dist/esm/index.js +36 -1
  71. package/dist/esm/input/ArrayInput.js +260 -136
  72. package/dist/esm/input/AvatarInput.js +99 -63
  73. package/dist/esm/input/Checkbox.js +26 -27
  74. package/dist/esm/input/ColorInput.js +141 -84
  75. package/dist/esm/input/ColorPicker.js +80 -97
  76. package/dist/esm/input/DateInput.js +192 -0
  77. package/dist/esm/input/Field.js +21 -20
  78. package/dist/esm/input/File.js +89 -84
  79. package/dist/esm/input/Hidden.js +28 -14
  80. package/dist/esm/input/Input.js +51 -50
  81. package/dist/esm/input/Number.js +48 -46
  82. package/dist/esm/input/Password.js +54 -52
  83. package/dist/esm/input/Radio.js +27 -17
  84. package/dist/esm/input/RadioGroup.js +27 -12
  85. package/dist/esm/input/{Range.js → RangeSlider.js} +62 -67
  86. package/dist/esm/input/Search.js +61 -67
  87. package/dist/esm/input/Slider.js +58 -67
  88. package/dist/esm/input/Switch.js +31 -29
  89. package/dist/esm/input/Textarea.js +67 -55
  90. package/dist/esm/internal/ErrorIcon.js +11 -11
  91. package/dist/esm/internal/FieldWrapper.js +23 -13
  92. package/dist/esm/internal/InputWrapper.js +88 -90
  93. package/dist/esm/ranger/index.js +7 -7
  94. package/dist/esm/ranger/utils.js +9 -4
  95. package/dist/esm/select/AutoComplete.js +95 -78
  96. package/dist/esm/select/DualList.js +233 -152
  97. package/dist/esm/select/List.js +125 -92
  98. package/dist/esm/select/Options.js +58 -54
  99. package/dist/esm/select/Select.js +122 -92
  100. package/dist/esm/select/helpers.js +5 -5
  101. package/dist/esm/select/useSelect.js +84 -59
  102. package/{src/input/ColorPicker.module.css → dist/esm/types/schema.js} +18 -27
  103. package/dist/esm/utils/eventHandlers.js +16 -6
  104. package/dist/esm/utils/getColor.js +6 -6
  105. package/dist/esm/utils/getProps.js +49 -0
  106. package/dist/types/builder/FileField.d.ts +7 -4
  107. package/dist/types/builder/FormField.d.ts +4 -2
  108. package/dist/types/builder/SchemaEditor.d.ts +6 -0
  109. package/dist/types/builder/components/AddSchemaFields.d.ts +2 -0
  110. package/dist/types/builder/components/SchemaArrayForm.d.ts +2 -0
  111. package/dist/types/builder/components/SchemaField.d.ts +12 -0
  112. package/dist/types/form/Controller.d.ts +45 -3
  113. package/dist/types/form/Form.d.ts +38 -7
  114. package/dist/types/form/useForm.d.ts +6 -0
  115. package/dist/types/hooks/useFileUploader.d.ts +24 -3
  116. package/dist/types/hooks/useFormBuilder.d.ts +41 -3
  117. package/dist/types/index.d.ts +32 -2
  118. package/dist/types/input/ArrayInput.d.ts +68 -13
  119. package/dist/types/input/AvatarInput.d.ts +23 -6
  120. package/dist/types/input/Checkbox.d.ts +12 -10
  121. package/dist/types/input/ColorInput.d.ts +15 -5
  122. package/dist/types/input/ColorPicker.d.ts +10 -2
  123. package/dist/types/input/DateInput.d.ts +21 -0
  124. package/dist/types/input/Field.d.ts +9 -36
  125. package/dist/types/input/File.d.ts +16 -4
  126. package/dist/types/input/Hidden.d.ts +7 -6
  127. package/dist/types/input/Input.d.ts +11 -4
  128. package/dist/types/input/Number.d.ts +9 -4
  129. package/dist/types/input/Password.d.ts +9 -4
  130. package/dist/types/input/Radio.d.ts +9 -4
  131. package/dist/types/input/RadioGroup.d.ts +14 -6
  132. package/dist/types/input/{Range.d.ts → RangeSlider.d.ts} +11 -6
  133. package/dist/types/input/Search.d.ts +14 -4
  134. package/dist/types/input/Slider.d.ts +9 -6
  135. package/dist/types/input/Switch.d.ts +13 -8
  136. package/dist/types/input/Textarea.d.ts +15 -4
  137. package/dist/types/internal/ErrorIcon.d.ts +4 -1
  138. package/dist/types/internal/FieldWrapper.d.ts +10 -1
  139. package/dist/types/internal/InputWrapper.d.ts +19 -1
  140. package/dist/types/ranger/index.d.ts +7 -3
  141. package/dist/types/ranger/utils.d.ts +2 -2
  142. package/dist/types/select/AutoComplete.d.ts +7 -2
  143. package/dist/types/select/DualList.d.ts +11 -37
  144. package/dist/types/select/List.d.ts +7 -2
  145. package/dist/types/select/Options.d.ts +8 -7
  146. package/dist/types/select/Select.d.ts +7 -2
  147. package/dist/types/select/helpers.d.ts +2 -2
  148. package/dist/types/select/useSelect.d.ts +10 -28
  149. package/dist/types/types/index.d.ts +73 -39
  150. package/dist/types/types/schema.d.ts +78 -32
  151. package/dist/types/utils/eventHandlers.d.ts +4 -4
  152. package/dist/types/utils/getColor.d.ts +4 -4
  153. package/dist/types/utils/getProps.d.ts +26 -0
  154. package/dist/types/utils/index.d.ts +1 -0
  155. package/package.json +25 -25
  156. package/src/builder/FileField.tsx +73 -56
  157. package/src/builder/FormField.tsx +66 -58
  158. package/src/builder/SchemaEditor.tsx +78 -0
  159. package/src/builder/components/AddSchemaFields.tsx +64 -0
  160. package/src/builder/components/SchemaArrayForm.tsx +146 -0
  161. package/src/builder/components/SchemaField.tsx +405 -0
  162. package/src/form/Controller.tsx +61 -24
  163. package/src/form/Form.tsx +74 -50
  164. package/src/form/useForm.ts +53 -0
  165. package/src/hooks/useFileUploader.ts +115 -84
  166. package/src/hooks/useFormBuilder.tsx +80 -41
  167. package/src/i18n/{ar.json → ar/form.json} +5 -4
  168. package/src/i18n/{en.json → en/form.json} +5 -5
  169. package/src/i18n/index.ts +2 -2
  170. package/src/index.ts +34 -20
  171. package/src/input/ArrayInput.tsx +255 -173
  172. package/src/input/AvatarInput.tsx +82 -39
  173. package/src/input/Checkbox.tsx +30 -54
  174. package/src/input/ColorInput.tsx +146 -105
  175. package/src/input/ColorPicker.tsx +76 -102
  176. package/src/input/DateInput.tsx +225 -0
  177. package/src/input/Field.tsx +23 -65
  178. package/src/input/File.tsx +76 -98
  179. package/src/input/Hidden.tsx +36 -24
  180. package/src/input/Input.tsx +55 -53
  181. package/src/input/Number.tsx +49 -54
  182. package/src/input/Password.tsx +61 -66
  183. package/src/input/Radio.tsx +28 -37
  184. package/src/input/RadioGroup.tsx +30 -25
  185. package/src/input/{Range.tsx → RangeSlider.tsx} +81 -109
  186. package/src/input/Search.tsx +64 -77
  187. package/src/input/Slider.tsx +80 -109
  188. package/src/input/Switch.tsx +39 -53
  189. package/src/input/Textarea.tsx +72 -59
  190. package/src/internal/ErrorIcon.tsx +22 -21
  191. package/src/internal/FieldWrapper.tsx +33 -23
  192. package/src/internal/InputWrapper.tsx +132 -143
  193. package/src/ranger/index.tsx +37 -117
  194. package/src/ranger/utils.ts +12 -11
  195. package/src/select/AutoComplete.tsx +113 -123
  196. package/src/select/DualList.tsx +218 -204
  197. package/src/select/List.tsx +130 -97
  198. package/src/select/Options.tsx +59 -57
  199. package/src/select/Select.tsx +148 -122
  200. package/src/select/helpers.ts +13 -8
  201. package/src/select/useSelect.tsx +110 -127
  202. package/src/types/index.ts +76 -45
  203. package/src/types/schema.ts +97 -40
  204. package/src/utils/eventHandlers.ts +24 -26
  205. package/src/utils/getColor.ts +9 -8
  206. package/src/utils/getProps.ts +47 -0
  207. package/src/utils/index.ts +1 -0
  208. package/dist/cjs/input/ArrayInput.module.css +0 -1
  209. package/dist/cjs/input/ColorPicker.module.css +0 -1
  210. package/dist/cjs/input/File.module.css +0 -1
  211. package/dist/cjs/internal/Field.module.css +0 -1
  212. package/dist/esm/input/ArrayInput.module.css +0 -1
  213. package/dist/esm/input/ColorPicker.module.css +0 -1
  214. package/dist/esm/input/File.module.css +0 -1
  215. package/dist/esm/internal/Field.module.css +0 -1
@@ -2,73 +2,83 @@
2
2
 
3
3
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
4
4
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
5
- import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
6
- import { useState, useDeferredValue, useRef, useEffect, useCallback } from 'react';
5
+ import { useControlledValue } from '@react-fabric/core';
6
+ import { useId, useState, useRef, useCallback, useEffect } from 'react';
7
7
  import { InputWrapper } from '../internal/InputWrapper.js';
8
- import { handleEnter } from '../utils/eventHandlers.js';
8
+ import { useHandleEnter } from '../utils/eventHandlers.js';
9
9
  import '@react-fabric/utilities';
10
+ import { getWrapperProps } from '../utils/getProps.js';
10
11
  import { jsx } from 'react/jsx-runtime';
11
12
 
12
- var _excluded = ["ref", "value", "name", "invalid", "readOnly", "disabled", "required", "placeholder", "autoFocus", "rows", "maxRows", "variableRows", "onChange", "onEnterPressed"];
13
13
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15
- var Textarea = function Textarea(_ref) {
16
- var ref = _ref.ref,
17
- value = _ref.value,
18
- name = _ref.name,
19
- invalid = _ref.invalid,
20
- readOnly = _ref.readOnly,
21
- disabled = _ref.disabled,
22
- required = _ref.required,
23
- placeholder = _ref.placeholder,
24
- autoFocus = _ref.autoFocus,
25
- _ref$rows = _ref.rows,
26
- rows = _ref$rows === void 0 ? 5 : _ref$rows,
27
- _ref$maxRows = _ref.maxRows,
28
- maxRows = _ref$maxRows === void 0 ? 12 : _ref$maxRows,
29
- variableRows = _ref.variableRows,
30
- onChange = _ref.onChange,
31
- onEnterPressed = _ref.onEnterPressed,
32
- rest = _objectWithoutProperties(_ref, _excluded);
33
- var _useState = useState(""),
14
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15
+ /**
16
+ * Textarea component that allows users to input multi-line text.
17
+ * It supports features like variable row count, expandable behavior,
18
+ * and handling of focus, blur, and change events.
19
+ * This component is designed to be flexible and user-friendly,
20
+ * providing a clear interface for text input with optional validation and accessibility features.
21
+ */
22
+ function Textarea(props) {
23
+ var value = props.value,
24
+ defaultValue = props.defaultValue,
25
+ onEnterPressed = props.onEnterPressed,
26
+ onChange = props.onChange,
27
+ onBlur = props.onBlur,
28
+ onFocus = props.onFocus,
29
+ disabled = props.disabled,
30
+ readOnly = props.readOnly,
31
+ invalid = props.invalid,
32
+ _props$rows = props.rows,
33
+ rows = _props$rows === void 0 ? 5 : _props$rows,
34
+ _props$maxRows = props.maxRows,
35
+ maxRows = _props$maxRows === void 0 ? 12 : _props$maxRows,
36
+ placeholder = props.placeholder,
37
+ name = props.name,
38
+ autoFocus = props.autoFocus,
39
+ expandable = props.expandable,
40
+ variableRows = props.variableRows,
41
+ allowClear = props.allowClear,
42
+ ref = props.ref;
43
+ var id = useId();
44
+ var _useControlledValue = useControlledValue(value, defaultValue),
45
+ currentValue = _useControlledValue.currentValue,
46
+ updateValue = _useControlledValue.updateValue;
47
+ var _useState = useState(function () {
48
+ return rows;
49
+ }),
34
50
  _useState2 = _slicedToArray(_useState, 2),
35
- actualValue = _useState2[0],
36
- setActualValue = _useState2[1];
37
- var _useState3 = useState(rows),
38
- _useState4 = _slicedToArray(_useState3, 2),
39
- fixedRows = _useState4[0],
40
- setFixedRows = _useState4[1];
41
- var deferred = useDeferredValue(value);
51
+ fixedRows = _useState2[0],
52
+ setFixedRows = _useState2[1];
42
53
  var wrapperRef = useRef(null);
43
- useEffect(function () {
44
- setActualValue(deferred !== null && deferred !== void 0 ? deferred : "");
45
- }, [deferred]);
46
54
  var handleChange = useCallback(function (e) {
47
- var value = e === null || e === void 0 ? void 0 : e.target.value;
48
- setActualValue(value !== null && value !== void 0 ? value : "");
49
- onChange === null || onChange === void 0 || onChange(value !== null && value !== void 0 ? value : undefined);
50
- }, [onChange]);
55
+ var _e$target$value;
56
+ var value = (_e$target$value = e === null || e === void 0 ? void 0 : e.target.value) !== null && _e$target$value !== void 0 ? _e$target$value : defaultValue;
57
+ updateValue(value);
58
+ onChange === null || onChange === void 0 || onChange(value);
59
+ }, [defaultValue, updateValue, onChange]);
51
60
  useEffect(function () {
52
61
  if (variableRows) {
53
- var _actualValue$split$le;
54
- var lines = (_actualValue$split$le = actualValue === null || actualValue === void 0 ? void 0 : actualValue.split("\n").length) !== null && _actualValue$split$le !== void 0 ? _actualValue$split$le : 1;
62
+ var _currentValue$split$l;
63
+ var lines = (_currentValue$split$l = currentValue === null || currentValue === void 0 ? void 0 : currentValue.split("\n").length) !== null && _currentValue$split$l !== void 0 ? _currentValue$split$l : 1;
64
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
55
65
  setFixedRows(Math.max(rows, Math.min(maxRows, lines)));
56
- } else {
57
- setFixedRows(rows);
58
66
  }
59
- }, [actualValue, variableRows, rows, maxRows]);
67
+ }, [currentValue, variableRows, rows, maxRows]);
68
+ var handleEnterPressed = useHandleEnter(onEnterPressed, {
69
+ preventDefault: !!onEnterPressed
70
+ });
60
71
  return /*#__PURE__*/jsx(InputWrapper, _objectSpread(_objectSpread({
61
- showClear: !!actualValue,
72
+ id: id,
73
+ textarea: true,
74
+ textareaExpandable: expandable,
75
+ showClear: allowClear && (currentValue !== null && currentValue !== void 0 ? currentValue : "") !== (defaultValue !== null && defaultValue !== void 0 ? defaultValue : "") && !disabled && !readOnly,
62
76
  onClear: handleChange,
63
- invalid: invalid,
64
- readOnly: readOnly,
65
- disabled: disabled,
66
- required: required,
67
- wrapperRef: wrapperRef,
68
- textarea: true
69
- }, rest), {}, {
77
+ ref: wrapperRef
78
+ }, getWrapperProps(props)), {}, {
70
79
  children: /*#__PURE__*/jsx("textarea", {
71
- className: "appearance-none self-stretch bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 min-h-20 resize-none",
80
+ id: id,
81
+ className: "appearance-none self-stretch bg-transparent py-1 px-4 not-first:ps-2 not-last:pe-2 flex-1 border-none outline-none ring-0 min-h-12 resize-none",
72
82
  "aria-invalid": invalid,
73
83
  "aria-disabled": disabled,
74
84
  "aria-readonly": readOnly,
@@ -81,11 +91,13 @@ var Textarea = function Textarea(_ref) {
81
91
  ref: ref,
82
92
  autoComplete: "off",
83
93
  autoFocus: autoFocus,
84
- value: actualValue,
94
+ value: currentValue !== null && currentValue !== void 0 ? currentValue : "",
95
+ onBlur: onBlur,
96
+ onFocus: onFocus,
85
97
  onChange: handleChange,
86
- onKeyDown: handleEnter(onEnterPressed)
98
+ onKeyDown: handleEnterPressed
87
99
  })
88
100
  }));
89
- };
101
+ }
90
102
 
91
103
  export { Textarea };
@@ -1,32 +1,32 @@
1
1
  'use client';
2
2
 
3
3
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
4
- import { Tooltip, Icon, CoreIcons } from '@react-fabric/core';
5
- import { isString } from '@react-fabric/utilities';
4
+ import { Tooltip, Icon } from '@react-fabric/core';
5
+ import { isObject, isString } from '@react-fabric/utilities';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { jsx } from 'react/jsx-runtime';
8
8
 
9
9
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
10
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
11
- var ErrorIcon = function ErrorIcon(_ref) {
12
- var _error$values$label, _error$values, _error$values2;
10
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
11
+ function ErrorIcon(_ref) {
12
+ var _error$values$label;
13
13
  var _ref$error = _ref.error,
14
14
  error = _ref$error === void 0 ? "" : _ref$error,
15
15
  invalid = _ref.invalid;
16
16
  var _useTranslation = useTranslation(),
17
17
  t = _useTranslation.t;
18
- return invalid && /*#__PURE__*/jsx(Tooltip, {
19
- content: isString(error) ? "".concat(error) : t(error === null || error === void 0 ? void 0 : error.key, error.values ? _objectSpread(_objectSpread({}, error === null || error === void 0 ? void 0 : error.values), {}, {
20
- label: t((_error$values$label = (_error$values = error.values) === null || _error$values === void 0 ? void 0 : _error$values.label) !== null && _error$values$label !== void 0 ? _error$values$label : "form:badkey", (_error$values2 = error.values) === null || _error$values2 === void 0 ? void 0 : _error$values2.path)
21
- }) : {}),
18
+ return invalid && error && /*#__PURE__*/jsx(Tooltip, {
19
+ content: isObject(error) ? t(error.key, isObject(error.values) ? _objectSpread(_objectSpread({}, error.values), {}, {
20
+ label: t((_error$values$label = error.values.label) !== null && _error$values$label !== void 0 ? _error$values$label : "form:badkey", error.values.path)
21
+ }) : {}) : isString(error) ? error : "",
22
22
  placement: "bottom",
23
23
  color: "danger",
24
24
  children: /*#__PURE__*/jsx(Icon, {
25
25
  className: "flex-content first:ms-2 self-center",
26
26
  color: "danger",
27
- icon: CoreIcons.alert
27
+ icon: "icon-[mdi--alert-circle-outline]"
28
28
  })
29
29
  });
30
- };
30
+ }
31
31
 
32
32
  export { ErrorIcon };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import classNames from 'classnames';
4
- import classes from './Field.module.css';
3
+ import { cn } from '@react-fabric/utilities';
4
+ import { ErrorIcon } from './ErrorIcon.js';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
 
7
7
  /*
@@ -27,43 +27,53 @@ import { jsxs, jsx } from 'react/jsx-runtime';
27
27
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
28
  */
29
29
 
30
- var FieldWrapper = function FieldWrapper(_ref) {
31
- var label = _ref.label,
30
+ function FieldWrapper(_ref) {
31
+ var id = _ref.id,
32
+ name = _ref.name,
33
+ label = _ref.label,
32
34
  appendLabel = _ref.appendLabel,
33
35
  width = _ref.width,
34
36
  _ref$labelWidth = _ref.labelWidth,
35
37
  labelWidth = _ref$labelWidth === void 0 ? 192 : _ref$labelWidth,
36
38
  inline = _ref.inline,
37
39
  info = _ref.info,
38
- disabled = _ref.disabled,
39
40
  required = _ref.required,
40
41
  children = _ref.children,
41
42
  listing = _ref.listing,
42
- className = _ref.className,
43
- isInner = _ref["data-inner"];
43
+ invalid = _ref.invalid,
44
+ disabled = _ref.disabled,
45
+ error = _ref.error,
46
+ showError = _ref.showError;
44
47
  return /*#__PURE__*/jsxs("div", {
45
- className: classNames(classes.fieldWrapper, className, inline ? "flex flex-nowrap gap-1" : "block"),
48
+ className: cn("fabric-fieldWrapper peer group relative", width ? "flex-content" : "flex-1", inline ? "flex flex-nowrap" : "flex flex-col"),
46
49
  "data-ref": "fieldWrapper",
50
+ "data-testid": "field_".concat(name),
47
51
  style: {
48
52
  width: width,
49
53
  flexBasis: width
50
54
  },
51
- children: [!isInner && /*#__PURE__*/jsxs("div", {
52
- className: "flex flex-nowrap flex-content gap-1 py-0.5 px-2",
55
+ children: [label && /*#__PURE__*/jsxs("div", {
56
+ className: "fabric-fieldLabel flex flex-nowrap flex-content items-end gap-1 py-0.5 px-2",
53
57
  style: {
54
58
  width: inline ? labelWidth : undefined
55
59
  },
56
60
  children: [/*#__PURE__*/jsx("label", {
57
61
  role: "none",
58
- className: classNames("flex-initial truncate text-sm", disabled && "text-muted"),
62
+ htmlFor: id,
63
+ className: cn("flex-initial truncate text-sm", disabled && "text-muted"),
59
64
  onClick: function onClick(e) {
60
65
  var _e$currentTarget$clos;
61
66
  return (_e$currentTarget$clos = e.currentTarget.closest('[data-ref="fieldWrapper"]')) === null || _e$currentTarget$clos === void 0 || (_e$currentTarget$clos = _e$currentTarget$clos.querySelector("input,textarea")) === null || _e$currentTarget$clos === void 0 ? void 0 : _e$currentTarget$clos.focus();
62
67
  },
63
68
  children: label
64
69
  }), required && /*#__PURE__*/jsx("span", {
65
- className: "text-danger-500 select-none flex-content text-[10px]",
70
+ className: "text-danger-500 select-none self-center flex-content text-[10px]",
66
71
  children: "\u273D"
72
+ }), showError && /*#__PURE__*/jsx(ErrorIcon, {
73
+ invalid: invalid,
74
+ error: error
75
+ }), /*#__PURE__*/jsx("div", {
76
+ className: "flex-1"
67
77
  }), appendLabel && /*#__PURE__*/jsx("span", {
68
78
  className: "flex-content",
69
79
  children: appendLabel
@@ -73,6 +83,6 @@ var FieldWrapper = function FieldWrapper(_ref) {
73
83
  children: info
74
84
  })]
75
85
  });
76
- };
86
+ }
77
87
 
78
88
  export { FieldWrapper };
@@ -2,112 +2,110 @@
2
2
 
3
3
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
4
4
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
5
- import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
6
- import { FloatingPortal } from '@floating-ui/react';
7
- import { Icon, CoreIcons } from '@react-fabric/core';
8
- import { isString } from '@react-fabric/utilities';
9
- import classNames from 'classnames';
10
- import { useRef, useState, useMemo, useCallback, useEffect } from 'react';
11
- import { Fragment, jsxs, jsx } from 'react/jsx-runtime';
5
+ import { Icon } from '@react-fabric/core';
6
+ import { cn, isString, mergeRefs } from '@react-fabric/utilities';
7
+ import { useRef, useState, useCallback, useEffect } from 'react';
12
8
  import { ErrorIcon } from './ErrorIcon.js';
13
- import classes from './Field.module.css';
14
9
  import { FieldWrapper } from './FieldWrapper.js';
10
+ import { jsx, jsxs } from 'react/jsx-runtime';
15
11
 
16
- var _excluded = ["invalid", "error", "allowClear", "showClear", "decorateStart", "decorateEnd", "children", "onClear", "noOutline", "noBorder", "textarea", "data-inner", "data-open", "floatingExpand", "wrapperRef", "width"];
17
12
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
18
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
19
- var InputWrapper = function InputWrapper(_ref) {
20
- var _elRef$current;
21
- var invalid = _ref.invalid,
22
- error = _ref.error,
23
- allowClear = _ref.allowClear,
24
- showClear = _ref.showClear,
25
- decorateStart = _ref.decorateStart,
26
- decorateEnd = _ref.decorateEnd,
27
- children = _ref.children,
28
- onClear = _ref.onClear,
29
- noOutline = _ref.noOutline,
30
- noBorder = _ref.noBorder,
31
- _ref$textarea = _ref.textarea,
32
- textarea = _ref$textarea === void 0 ? false : _ref$textarea,
33
- isInner = _ref["data-inner"];
34
- _ref["data-open"];
35
- var floatingExpand = _ref.floatingExpand,
36
- wrapperRef = _ref.wrapperRef,
37
- width = _ref.width,
38
- rest = _objectWithoutProperties(_ref, _excluded);
39
- var elRef = useRef(null);
13
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
14
+ function InputWrapper(props) {
15
+ var invalid = props.invalid,
16
+ error = props.error,
17
+ showClear = props.showClear,
18
+ decorateStart = props.decorateStart,
19
+ decorateEnd = props.decorateEnd,
20
+ decorateEndShowWhenEmpty = props.decorateEndShowWhenEmpty,
21
+ decorateEndHideWhenEmpty = props.decorateEndHideWhenEmpty,
22
+ children = props.children,
23
+ isOpen = props.isOpen,
24
+ className = props.className,
25
+ focusRing = props.focusRing,
26
+ onClear = props.onClear,
27
+ borderless = props.borderless,
28
+ hasStepper = props.hasStepper,
29
+ actionButton = props.actionButton,
30
+ _props$textarea = props.textarea,
31
+ textarea = _props$textarea === void 0 ? false : _props$textarea,
32
+ _props$textareaExpand = props.textareaExpandable,
33
+ textareaExpandable = _props$textareaExpand === void 0 ? false : _props$textareaExpand,
34
+ floatingExpand = props.floatingExpand,
35
+ ref = props.ref;
36
+ var wrapperRef = useRef(null);
40
37
  var _useState = useState(false),
41
38
  _useState2 = _slicedToArray(_useState, 2),
42
39
  isExpanded = _useState2[0],
43
- setExpanded = _useState2[1];
44
- var Wrapper = useMemo(function () {
45
- return isInner || !rest.label ? Fragment : FieldWrapper;
46
- }, [isInner, rest.label]);
47
- var wrapperProps = useMemo(function () {
48
- return isInner || !rest.label ? {} : _objectSpread(_objectSpread({}, rest), {}, {
49
- width: width
50
- });
51
- }, [isInner, rest, width]);
40
+ setIsExpanded = _useState2[1];
52
41
  var handleClear = useCallback(function (e) {
53
- var _e$currentTarget$pare;
54
- (_e$currentTarget$pare = e.currentTarget.parentElement) === null || _e$currentTarget$pare === void 0 || (_e$currentTarget$pare = _e$currentTarget$pare.querySelector("input,textarea,button")) === null || _e$currentTarget$pare === void 0 || _e$currentTarget$pare.focus();
42
+ var _e$currentTarget$pare, _parent$querySelector;
43
+ var parent = (_e$currentTarget$pare = e.currentTarget.parentElement) === null || _e$currentTarget$pare === void 0 ? void 0 : _e$currentTarget$pare.querySelector("& > :not(.fabric-decorate)");
44
+ var el = (_parent$querySelector = parent === null || parent === void 0 ? void 0 : parent.querySelector("input,textarea,button")) !== null && _parent$querySelector !== void 0 ? _parent$querySelector : parent;
45
+ el === null || el === void 0 || el.focus();
55
46
  onClear === null || onClear === void 0 || onClear();
56
47
  }, [onClear]);
57
48
  var doFocus = useCallback(function (e) {
58
- var _e$currentTarget$pare2;
59
- (_e$currentTarget$pare2 = e.currentTarget.parentElement) === null || _e$currentTarget$pare2 === void 0 || (_e$currentTarget$pare2 = _e$currentTarget$pare2.querySelector("input,textarea,button")) === null || _e$currentTarget$pare2 === void 0 || _e$currentTarget$pare2.focus();
60
- }, [onClear]);
49
+ var _e$currentTarget$pare2, _parent$querySelector2;
50
+ var parent = (_e$currentTarget$pare2 = e.currentTarget.parentElement) === null || _e$currentTarget$pare2 === void 0 ? void 0 : _e$currentTarget$pare2.querySelector("& > :not(.fabric-decorate)");
51
+ var el = (_parent$querySelector2 = parent === null || parent === void 0 ? void 0 : parent.querySelector("input,textarea,button")) !== null && _parent$querySelector2 !== void 0 ? _parent$querySelector2 : parent;
52
+ el === null || el === void 0 || el.focus();
53
+ }, []);
61
54
  useEffect(function () {
62
- isExpanded && setTimeout(function () {
63
- var _wrapperRef$current;
64
- return wrapperRef === null || wrapperRef === void 0 || (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 || (_wrapperRef$current = _wrapperRef$current.querySelector("textarea")) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.focus();
65
- }, 50);
66
- }, [isExpanded]);
67
- var el = /*#__PURE__*/jsxs("div", {
68
- role: "none",
69
- ref: wrapperRef,
70
- className: classNames(classes.inputWrapper, invalid && classes.invalidWrapper, !noOutline && !noBorder && "bg-[var(--fabric-input)] outline overflow-hidden", "flex flex-nowrap items-center", textarea ? "rounded-capped group" : "rounded", isExpanded ? "fixed inset-4 z-[99]" : floatingExpand ? "absolute inset-x-0" : "relative", !noOutline && "after:absolute after:bottom-0 after:h-px after:bg-primary-500 after:mx-auto"),
71
- onKeyDown: function onKeyDown(e) {
72
- return e.key === "Escape" && setExpanded(false);
73
- },
74
- children: [decorateStart && /*#__PURE__*/jsx("div", {
75
- onClickCapture: doFocus,
76
- className: classNames(classes.decorate, "text-muted leading-none flex-content", isString(decorateStart) && "px-1"),
77
- children: decorateStart
78
- }), /*#__PURE__*/jsx(ErrorIcon, {
79
- invalid: invalid,
80
- error: error
81
- }), children, allowClear && showClear && /*#__PURE__*/jsx(Icon, {
82
- icon: CoreIcons.close,
83
- onClick: handleClear,
84
- className: classNames("text-tint-500 hover:text-tint-700 p-1 z-1 flex-content", classes.inputClear, textarea && "self-start mt-1 me-1")
85
- }), decorateEnd && /*#__PURE__*/jsx("div", {
86
- onClickCapture: doFocus,
87
- className: classNames(classes.decorate, "text-muted leading-none flex-content", isString(decorateEnd) && "px-1"),
88
- children: decorateEnd
89
- }), textarea && /*#__PURE__*/jsx(Icon, {
90
- onClick: function onClick() {
91
- return setExpanded(!isExpanded);
92
- },
93
- icon: isExpanded ? CoreIcons.arrowCollapse : CoreIcons.arrowExpand,
94
- className: "hidden group-hover:block absolute top-0 end-0 bg-base hover:bg-tint-200 p-1 opacity-65 rounded-capped"
95
- })]
96
- });
97
- return /*#__PURE__*/jsx(Wrapper, _objectSpread(_objectSpread({}, wrapperProps), {}, {
55
+ if (isExpanded) {
56
+ var tmr = setTimeout(function () {
57
+ var _wrapperRef$current;
58
+ return wrapperRef === null || wrapperRef === void 0 || (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 || (_wrapperRef$current = _wrapperRef$current.querySelector("textarea")) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.focus();
59
+ }, 50);
60
+ return function () {
61
+ clearTimeout(tmr);
62
+ };
63
+ }
64
+ }, [isExpanded, wrapperRef]);
65
+ return /*#__PURE__*/jsx(FieldWrapper, _objectSpread(_objectSpread({}, props), {}, {
98
66
  children: /*#__PURE__*/jsxs("div", {
99
- ref: elRef,
100
- className: "flex-1 relative min-h-8",
101
- style: {
102
- width: width,
103
- flexBasis: width
67
+ role: "none",
68
+ ref: mergeRefs(ref, wrapperRef),
69
+ className: cn("fabric-inputWrapper", invalid && "fabric-invalidWrapper", !borderless && "outline", textarea ? "rounded-capped" : "rounded", className, "flex flex-1 flex-nowrap items-center bg-(--fabric-input)", isExpanded ? "fixed inset-4 z-99" : floatingExpand ? "absolute inset-x-0" : "relative", focusRing ? "focus-within:ring ring-primary-500" : "after:absolute after:bottom-0 after:h-px after:bg-primary-500 after:mx-auto", isOpen && "outline-primary-500"),
70
+ onKeyDown: function onKeyDown(e) {
71
+ return e.key === "Escape" && isExpanded && (setIsExpanded(false), e.stopPropagation());
104
72
  },
105
- children: [!isExpanded && el, isExpanded && /*#__PURE__*/jsx(FloatingPortal, {
106
- root: (_elRef$current = elRef.current) === null || _elRef$current === void 0 ? void 0 : _elRef$current.closest(".theme-base"),
107
- children: el
73
+ children: [decorateStart && /*#__PURE__*/jsx("div", {
74
+ onClickCapture: doFocus,
75
+ className: cn("fabric-decorate", "text-muted leading-none flex-content", isString(decorateStart) && "px-1"),
76
+ children: decorateStart
77
+ }), /*#__PURE__*/jsx(ErrorIcon, {
78
+ invalid: invalid,
79
+ error: error
80
+ }), children, showClear && /*#__PURE__*/jsx("button", {
81
+ type: "button",
82
+ className: cn("size-fit leading-0 cursor-pointer", hasStepper && "relative -ms-12"),
83
+ onClick: handleClear,
84
+ children: /*#__PURE__*/jsx(Icon, {
85
+ icon: "icon-[mdi--close]",
86
+ className: cn("text-tint-500 hover:text-tint-700 p-1 z-1 flex-content", "fabric-inputClear")
87
+ })
88
+ }), decorateEnd && /*#__PURE__*/jsx("div", {
89
+ onClickCapture: doFocus,
90
+ className: cn("fabric-decorate", "text-muted leading-none flex-content", isString(decorateEnd) && "px-1", decorateEndHideWhenEmpty && "peer-placeholder-shown:hidden", decorateEndShowWhenEmpty && "hidden peer-placeholder-shown:inline-block"),
91
+ children: decorateEnd
92
+ }), actionButton && /*#__PURE__*/jsx("div", {
93
+ onClickCapture: doFocus,
94
+ className: cn("fabric-decorate", "text-muted leading-none flex-content"),
95
+ children: actionButton
96
+ }), textareaExpandable && /*#__PURE__*/jsx("button", {
97
+ type: "button",
98
+ className: "size-fit cursor-pointer leading-0 absolute top-0 inset-e-0",
99
+ onClick: function onClick() {
100
+ return setIsExpanded(!isExpanded);
101
+ },
102
+ children: /*#__PURE__*/jsx(Icon, {
103
+ icon: isExpanded ? "icon-[mdi--arrow-collapse]" : "icon-[mdi--arrow-expand]",
104
+ className: "hidden group-hover:block bg-default hover:bg-tint-200 p-1 opacity-65 rounded-capped"
105
+ })
108
106
  })]
109
107
  })
110
108
  }));
111
- };
109
+ }
112
110
 
113
111
  export { InputWrapper };
@@ -14,7 +14,7 @@ var useGetLatest = function useGetLatest(val) {
14
14
  return ref.current;
15
15
  }, []);
16
16
  };
17
- var useRanger = function useRanger(opts) {
17
+ function useRanger(opts) {
18
18
  var _opts$tickSize;
19
19
  var tickSize = (_opts$tickSize = opts.tickSize) !== null && _opts$tickSize !== void 0 ? _opts$tickSize : 10;
20
20
  var values = opts.values;
@@ -195,17 +195,17 @@ var useRanger = function useRanger(opts) {
195
195
  } else {
196
196
  setActiveHandleIndex(i);
197
197
  }
198
- var handleRelease = function handleRelease(e) {
198
+ var _handleRelease = function handleRelease() {
199
199
  var _getLatest5 = getLatest(),
200
200
  tempValues = _getLatest5.tempValues,
201
201
  onChange = _getLatest5.onChange,
202
202
  onDragEnd = _getLatest5.onDragEnd;
203
203
  document.removeEventListener("mousemove", i === -1 ? handleSegmentDrag : handleDrag);
204
204
  document.removeEventListener("touchmove", i === -1 ? handleSegmentDrag : handleDrag);
205
- document.removeEventListener("mouseup", handleRelease, {
205
+ document.removeEventListener("mouseup", _handleRelease, {
206
206
  capture: true
207
207
  });
208
- document.removeEventListener("touchend", handleRelease, {
208
+ document.removeEventListener("touchend", _handleRelease, {
209
209
  capture: true
210
210
  });
211
211
  var sortedValues = sortNumList(tempValues || values);
@@ -218,10 +218,10 @@ var useRanger = function useRanger(opts) {
218
218
  onDragStart === null || onDragStart === void 0 || onDragStart();
219
219
  document.addEventListener("mousemove", i === -1 ? handleSegmentDrag : handleDrag);
220
220
  document.addEventListener("touchmove", i === -1 ? handleSegmentDrag : handleDrag);
221
- document.addEventListener("mouseup", handleRelease, {
221
+ document.addEventListener("mouseup", _handleRelease, {
222
222
  capture: true
223
223
  });
224
- document.addEventListener("touchend", handleRelease, {
224
+ document.addEventListener("touchend", _handleRelease, {
225
225
  capture: true
226
226
  });
227
227
  }, [getLatest, handleDrag]);
@@ -347,6 +347,6 @@ var useRanger = function useRanger(opts) {
347
347
  segments: segments,
348
348
  handles: handles
349
349
  };
350
- };
350
+ }
351
351
 
352
352
  export { useRanger };
@@ -7,7 +7,12 @@ import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
7
7
  * https://github.com/TanStack/ranger
8
8
  */
9
9
 
10
- var getBoundingClientRect = function getBoundingClientRect(element) {
10
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
11
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
12
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
13
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
14
+
15
+ function getBoundingClientRect(element) {
11
16
  var rect = element.getBoundingClientRect();
12
17
  return {
13
18
  top: Math.ceil(rect.top),
@@ -17,12 +22,12 @@ var getBoundingClientRect = function getBoundingClientRect(element) {
17
22
  bottom: Math.ceil(rect.bottom),
18
23
  height: Math.ceil(rect.height)
19
24
  };
20
- };
21
- var sortNumList = function sortNumList(arr) {
25
+ }
26
+ function sortNumList(arr) {
22
27
  return _toConsumableArray(arr).map(Number).sort(function (a, b) {
23
28
  return a - b;
24
29
  });
25
- };
30
+ }
26
31
  var linearInterpolator = {
27
32
  getPercentageForValue: function getPercentageForValue(val, min, max) {
28
33
  return Math.max(0, Math.min(100, (val - min) / (max - min) * 100));