@homecode/ui 5.11.0 → 5.12.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.
- package/dist/esm/src/components/FormattedText/FormattedText.js +17 -6
- package/dist/esm/src/components/Input/Input.js +73 -2
- package/dist/esm/src/components/PromptComposer/PromptComposer.styl.js +2 -2
- package/dist/esm/src/components/Select/Select2.js +1 -1
- package/dist/esm/src/components/TextWithDeferTooltip/TextWithDeferTooltip.js +34 -3
- package/dist/esm/types/src/components/FormattedText/FormattedText.test.d.ts +1 -0
- package/dist/esm/types/src/components/Input/Input.d.ts +1 -0
- package/dist/esm/types/src/components/Input/Input.test.d.ts +1 -0
- package/dist/esm/types/src/components/Input/Input.types.d.ts +2 -0
- package/dist/esm/types/src/components/Select/Select.d.ts +1 -0
- package/package.json +1 -1
|
@@ -80,7 +80,7 @@ function applyInjectors(text, injectors) {
|
|
|
80
80
|
}
|
|
81
81
|
function FormattedText({ text, className, onButtonClick, }) {
|
|
82
82
|
const injectLines = (content) => {
|
|
83
|
-
const matches = content.match(
|
|
83
|
+
const matches = content.match(/(^|\n)---[ \t]*(?:\n|$)/);
|
|
84
84
|
if (!matches)
|
|
85
85
|
return null;
|
|
86
86
|
return {
|
|
@@ -204,9 +204,17 @@ function FormattedText({ text, className, onButtonClick, }) {
|
|
|
204
204
|
length: matches[0].length,
|
|
205
205
|
};
|
|
206
206
|
};
|
|
207
|
-
const
|
|
208
|
-
|
|
207
|
+
const injectBreaks = (content) => {
|
|
208
|
+
const index = content.indexOf('\n');
|
|
209
|
+
if (index < 0)
|
|
209
210
|
return null;
|
|
211
|
+
return {
|
|
212
|
+
elem: jsx("br", {}),
|
|
213
|
+
index,
|
|
214
|
+
length: 1,
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
const injectButtons = (content) => {
|
|
210
218
|
const matches = content.match(/\[(.*?):(.*?)\|([^\]]+)\]/);
|
|
211
219
|
if (!matches)
|
|
212
220
|
return null;
|
|
@@ -216,7 +224,9 @@ function FormattedText({ text, className, onButtonClick, }) {
|
|
|
216
224
|
.split('|');
|
|
217
225
|
const [varName, value] = data.split(':');
|
|
218
226
|
return {
|
|
219
|
-
elem: (jsx(Button, { variant: "default", size: "s", round: true, onClick:
|
|
227
|
+
elem: (jsx(Button, { variant: "default", size: "s", round: true, onClick: onButtonClick
|
|
228
|
+
? () => onButtonClick({ text: label, [varName]: value })
|
|
229
|
+
: undefined, children: label })),
|
|
220
230
|
index: matches.index,
|
|
221
231
|
length: matches[0].length,
|
|
222
232
|
};
|
|
@@ -260,10 +270,10 @@ function FormattedText({ text, className, onButtonClick, }) {
|
|
|
260
270
|
};
|
|
261
271
|
};
|
|
262
272
|
const injectTables = (content) => {
|
|
263
|
-
const withSep = content.match(/(\n|^)(\|[^\n]
|
|
273
|
+
const withSep = content.match(/(\n|^)(\|[^\n]+\|[ \t]*\n)(\|[\t :-]+(?:\|[\t :-]+)*\|[ \t]*\n)((?:\|[^\n]+\|[ \t]*\n?)+)/);
|
|
264
274
|
const withoutSep = withSep
|
|
265
275
|
? null
|
|
266
|
-
: content.match(/(\n|^)(\|[^\n]
|
|
276
|
+
: content.match(/(\n|^)(\|[^\n]+\|[ \t]*\n)((?:\|[^\n]+\|[ \t]*\n?)+)/);
|
|
267
277
|
const matches = withSep || withoutSep;
|
|
268
278
|
if (!matches)
|
|
269
279
|
return null;
|
|
@@ -317,6 +327,7 @@ function FormattedText({ text, className, onButtonClick, }) {
|
|
|
317
327
|
injectBullet,
|
|
318
328
|
injectNumbered,
|
|
319
329
|
injectButtons,
|
|
330
|
+
injectBreaks,
|
|
320
331
|
]) }));
|
|
321
332
|
}
|
|
322
333
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { forwardRef, useRef, useState, useMemo, useEffect, createElement } from 'react';
|
|
2
|
+
import { forwardRef, useRef, useState, useMemo, useEffect, useLayoutEffect, createElement } from 'react';
|
|
3
3
|
import cn from 'classnames';
|
|
4
4
|
import { Label } from '../Label/Label.js';
|
|
5
5
|
import { RequiredStar } from '../RequiredStar/RequiredStar.js';
|
|
@@ -16,6 +16,38 @@ const TEXTAREA_SCROLL_TOP_OFFSET = {
|
|
|
16
16
|
m: 40,
|
|
17
17
|
l: 50,
|
|
18
18
|
};
|
|
19
|
+
function isNativeTextField(el) {
|
|
20
|
+
return el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement;
|
|
21
|
+
}
|
|
22
|
+
function resolveNativeTextField(el) {
|
|
23
|
+
if (isNativeTextField(el))
|
|
24
|
+
return el;
|
|
25
|
+
if (el instanceof Element) {
|
|
26
|
+
const nested = el.querySelector('input, textarea');
|
|
27
|
+
if (isNativeTextField(nested))
|
|
28
|
+
return nested;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
function selectContentEditable(el) {
|
|
33
|
+
const selection = window.getSelection();
|
|
34
|
+
if (!selection)
|
|
35
|
+
return;
|
|
36
|
+
const range = document.createRange();
|
|
37
|
+
range.selectNodeContents(el);
|
|
38
|
+
selection.removeAllRanges();
|
|
39
|
+
selection.addRange(range);
|
|
40
|
+
}
|
|
41
|
+
function selectAllOnElement(el) {
|
|
42
|
+
const field = resolveNativeTextField(el);
|
|
43
|
+
if (field && typeof field.select === 'function') {
|
|
44
|
+
field.select();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (el instanceof HTMLElement && el.isContentEditable) {
|
|
48
|
+
selectContentEditable(el);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
19
51
|
const Input = forwardRef((props, ref) => {
|
|
20
52
|
const inputRef = useRef(null);
|
|
21
53
|
const setRef = (element) => {
|
|
@@ -27,7 +59,7 @@ const Input = forwardRef((props, ref) => {
|
|
|
27
59
|
ref.current = element;
|
|
28
60
|
}
|
|
29
61
|
};
|
|
30
|
-
const { type = 'text', size = 'm', variant = 'default', value, defaultValue = '', onChange, onFocus, onBlur, onClear, onInput, changeOnEnd, checkAutofill, hasClear, required, hideRequiredStar, disabled, error, label, placeholder, addonLeft, addonRight, clearPaddingLeft, clearPaddingRight, forceLabelOnTop, scrollProps, step = 1, round, autoFocus, className, fitContentWidth, } = props;
|
|
62
|
+
const { type = 'text', size = 'm', variant = 'default', value, defaultValue = '', onChange, onFocus, onBlur, onClear, onInput, changeOnEnd, checkAutofill, hasClear, required, hideRequiredStar, disabled, error, label, placeholder, addonLeft, addonRight, clearPaddingLeft, clearPaddingRight, forceLabelOnTop, scrollProps, step = 1, round, autoFocus, className, fitContentWidth, selectAllOnFocus, } = props;
|
|
31
63
|
const updateAutoComplete = () => {
|
|
32
64
|
const form = inputRef.current?.closest('form');
|
|
33
65
|
const val = form?.getAttribute('autocomplete');
|
|
@@ -39,6 +71,8 @@ const Input = forwardRef((props, ref) => {
|
|
|
39
71
|
const elem = inputRef.current;
|
|
40
72
|
if (!isFocused || !elem || type !== 'text')
|
|
41
73
|
return;
|
|
74
|
+
if (elem.selectionStart !== elem.selectionEnd)
|
|
75
|
+
return;
|
|
42
76
|
elem.selectionStart = cursorPos.current;
|
|
43
77
|
elem.selectionEnd = cursorPos.current;
|
|
44
78
|
};
|
|
@@ -57,6 +91,7 @@ const Input = forwardRef((props, ref) => {
|
|
|
57
91
|
const hasValue = isNumber || Boolean(value || inputValue || defaultValue);
|
|
58
92
|
const isLabelOnTop = forceLabelOnTop || Boolean(addonLeft) || hasValue || isFocused;
|
|
59
93
|
const cursorPos = useRef(0);
|
|
94
|
+
const selectAllOnFocusPending = useRef(false);
|
|
60
95
|
const getValue = (val = inputValue) => {
|
|
61
96
|
if (type === 'number') {
|
|
62
97
|
if (val)
|
|
@@ -136,6 +171,7 @@ const Input = forwardRef((props, ref) => {
|
|
|
136
171
|
onChangeValue(val, e);
|
|
137
172
|
};
|
|
138
173
|
const onChangeValue = (value, e) => {
|
|
174
|
+
selectAllOnFocusPending.current = false;
|
|
139
175
|
if (!isNumber && inputRef.current) {
|
|
140
176
|
cursorPos.current = inputRef.current.selectionStart;
|
|
141
177
|
}
|
|
@@ -159,8 +195,23 @@ const Input = forwardRef((props, ref) => {
|
|
|
159
195
|
const handleFocus = e => {
|
|
160
196
|
setIsFocused(true);
|
|
161
197
|
onFocus?.(e);
|
|
198
|
+
if (!selectAllOnFocus)
|
|
199
|
+
return;
|
|
200
|
+
selectAllOnFocusPending.current = true;
|
|
201
|
+
selectAllOnElement(inputRef.current ?? e.target);
|
|
202
|
+
};
|
|
203
|
+
const handleMouseDown = e => {
|
|
204
|
+
props.controlProps?.onMouseDown?.(e);
|
|
205
|
+
if (!selectAllOnFocus || isFocusedRef.current)
|
|
206
|
+
return;
|
|
207
|
+
e.preventDefault();
|
|
208
|
+
const el = e.currentTarget;
|
|
209
|
+
if (el instanceof HTMLElement)
|
|
210
|
+
el.focus();
|
|
211
|
+
selectAllOnElement(el);
|
|
162
212
|
};
|
|
163
213
|
const handleBlur = e => {
|
|
214
|
+
selectAllOnFocusPending.current = false;
|
|
164
215
|
if (changeOnEnd)
|
|
165
216
|
onTypingEnd();
|
|
166
217
|
const val = getValue(e?.target?.value);
|
|
@@ -181,6 +232,7 @@ const Input = forwardRef((props, ref) => {
|
|
|
181
232
|
onChange: handleChange,
|
|
182
233
|
onFocus: handleFocus,
|
|
183
234
|
onBlur: handleBlur,
|
|
235
|
+
...(selectAllOnFocus ? { onMouseDown: handleMouseDown } : null),
|
|
184
236
|
};
|
|
185
237
|
if (isTextArea) {
|
|
186
238
|
controlProps.contentEditable = true;
|
|
@@ -208,8 +260,10 @@ const Input = forwardRef((props, ref) => {
|
|
|
208
260
|
disabled,
|
|
209
261
|
props.controlProps,
|
|
210
262
|
handleChange,
|
|
263
|
+
handleMouseDown,
|
|
211
264
|
onFocus,
|
|
212
265
|
onBlur,
|
|
266
|
+
selectAllOnFocus,
|
|
213
267
|
isTextArea,
|
|
214
268
|
onTextAreaInput,
|
|
215
269
|
placeholder,
|
|
@@ -271,6 +325,23 @@ const Input = forwardRef((props, ref) => {
|
|
|
271
325
|
if (isTextArea)
|
|
272
326
|
setTextareaValue(String(value ?? ''));
|
|
273
327
|
}, []);
|
|
328
|
+
useLayoutEffect(() => {
|
|
329
|
+
if (!selectAllOnFocus || !selectAllOnFocusPending.current)
|
|
330
|
+
return;
|
|
331
|
+
if (!isFocused)
|
|
332
|
+
return;
|
|
333
|
+
const field = resolveNativeTextField(inputRef.current);
|
|
334
|
+
if (field) {
|
|
335
|
+
if (!field.value || typeof field.select !== 'function')
|
|
336
|
+
return;
|
|
337
|
+
field.select();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const node = inputRef.current;
|
|
341
|
+
if (node instanceof HTMLElement && node.isContentEditable && node.innerText) {
|
|
342
|
+
selectContentEditable(node);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
274
345
|
const Control = isTextArea ? 'span' : 'input';
|
|
275
346
|
const classes = cn(S.root, isTextArea && S.isTextArea, S[`size-${size}`], S[`variant-${variant}`], isFocused && S.isFocused, error && S.hasError, hasClear && S.hasClear, disabled && S.isDisabled, round && S.round, fitContentWidth && !isTextArea && S.fitContentWidth, className);
|
|
276
347
|
return (jsxs("div", { className: classes, children: [jsxs("label", { className: cn(S.main, clearPaddingLeft && S.clearPaddingLeft, clearPaddingRight && S.clearPaddingRight), children: [jsx("div", { className: S.border, suppressHydrationWarning: true, style: { clipPath: labelClipPath } }, "border"), renderAddon('left'), wrapControl(jsxs(Fragment, { children: [createElement(Control, { ...controlProps, className: cn(S.control, controlProps?.className), ref: setRef, key: "control" }), isTextArea &&
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from '../../../node_modules/style-inject/dist/style-inject.es.js';
|
|
2
2
|
|
|
3
|
-
var css_248z = ".PromptComposer_root__gfdXN{width:100%}.PromptComposer_scroller__Sueif{flex:1;max-height:200px;min-height:40px;width:100%}.PromptComposer_editorMount__B9G-o{background:transparent;border:none;border-radius:0!important;box-shadow:none!important;display:flex;flex:1;flex-direction:column;min-height:40px;min-width:0;padding:0!important}.PromptComposer_editorMount__B9G-o:focus-within{box-shadow:none!important}.PromptComposer_editorMount__B9G-o .PromptComposer_promptComposerEditor__yQIpq{border:none!important;box-shadow:none!important;flex:1;margin:0;min-height:40px!important;outline:none!important;overflow:hidden!important;padding:var(--p-2) 0 0!important;resize:none!important;transition:opacity .1s ease-out;white-space:pre-wrap;word-break:break-word}.PromptComposer_editorMount__B9G-o .PromptComposer_promptComposerEditor__yQIpq a{color:var(--link-color)}.PromptComposer_editorMount__B9G-o .PromptComposer_promptComposerEditor__yQIpq a:hover{cursor:pointer;opacity:.8}.PromptComposer_editorMount__B9G-o .
|
|
4
|
-
var S = {"root":"PromptComposer_root__gfdXN","scroller":"PromptComposer_scroller__Sueif","editorMount":"PromptComposer_editorMount__B9G-o","promptComposerEditor":"PromptComposer_promptComposerEditor__yQIpq","
|
|
3
|
+
var css_248z = ".PromptComposer_root__gfdXN{width:100%}.PromptComposer_scroller__Sueif{flex:1;max-height:200px;min-height:40px;width:100%}.PromptComposer_editorMount__B9G-o{background:transparent;border:none;border-radius:0!important;box-shadow:none!important;display:flex;flex:1;flex-direction:column;min-height:40px;min-width:0;padding:0!important}.PromptComposer_editorMount__B9G-o:focus-within{box-shadow:none!important}.PromptComposer_editorMount__B9G-o .PromptComposer_promptComposerEditor__yQIpq{border:none!important;box-shadow:none!important;flex:1;margin:0;min-height:40px!important;outline:none!important;overflow:hidden!important;padding:var(--p-2) 0 0!important;resize:none!important;transition:opacity .1s ease-out;white-space:pre-wrap;word-break:break-word}.PromptComposer_editorMount__B9G-o .PromptComposer_promptComposerEditor__yQIpq a{color:var(--link-color)}.PromptComposer_editorMount__B9G-o .PromptComposer_promptComposerEditor__yQIpq a:hover{cursor:pointer;opacity:.8}.PromptComposer_editorMount__B9G-o .PromptComposer_promptComposerEmptyNode__62Ylm:before{color:var(--muted-foreground);content:attr(data-placeholder);float:left;height:0;pointer-events:none}";
|
|
4
|
+
var S = {"root":"PromptComposer_root__gfdXN","scroller":"PromptComposer_scroller__Sueif","editorMount":"PromptComposer_editorMount__B9G-o","promptComposerEditor":"PromptComposer_promptComposerEditor__yQIpq","promptComposerEmptyNode":"PromptComposer_promptComposerEmptyNode__62Ylm"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { S as default };
|
|
@@ -245,7 +245,7 @@ function Select2(props) {
|
|
|
245
245
|
const inputValue = isSearching ? searchVal : isMultiple$1 ? '' : selectedLabel;
|
|
246
246
|
return (jsx(Input, { ...triggerProps, ...inputProps,
|
|
247
247
|
// TODO: autoComplete
|
|
248
|
-
addonRight: triggerArrow, error: isErrorVisible, value: inputValue, onChange: handleSearchChange, label: getFieldLabel(label), placeholder: hasChips && !inputValue ? '' : inputProps?.placeholder }));
|
|
248
|
+
addonRight: triggerArrow, error: isErrorVisible, value: inputValue, onChange: handleSearchChange, selectAllOnFocus: true, label: getFieldLabel(label), placeholder: hasChips && !inputValue ? '' : inputProps?.placeholder }));
|
|
249
249
|
};
|
|
250
250
|
const renderTriggerButton = () => {
|
|
251
251
|
const { label, className, ...rest } = triggerProps;
|
|
@@ -3,6 +3,39 @@ import { useState, useRef } from 'react';
|
|
|
3
3
|
import { Tooltip } from '../Tooltip/Tooltip.js';
|
|
4
4
|
import S from './TextWithDeferTooltip.styl.js';
|
|
5
5
|
|
|
6
|
+
function clipsOverflow(style, axis) {
|
|
7
|
+
const overflow = axis === 'x' ? style.overflowX : style.overflowY;
|
|
8
|
+
if (overflow === 'hidden' || overflow === 'auto' || overflow === 'scroll') {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
if (axis === 'x' && style.textOverflow === 'ellipsis')
|
|
12
|
+
return true;
|
|
13
|
+
if (axis === 'y' &&
|
|
14
|
+
style.webkitLineClamp &&
|
|
15
|
+
style.webkitLineClamp !== 'none') {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
function isNodeOverflowing(node) {
|
|
21
|
+
const style = getComputedStyle(node);
|
|
22
|
+
if (clipsOverflow(style, 'x') && node.scrollWidth - node.clientWidth > 1) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (clipsOverflow(style, 'y') && node.scrollHeight - node.clientHeight > 1) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
function isTextOverflowing(root) {
|
|
31
|
+
if (isNodeOverflowing(root))
|
|
32
|
+
return true;
|
|
33
|
+
for (const child of root.querySelectorAll('*')) {
|
|
34
|
+
if (isNodeOverflowing(child))
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
6
39
|
function TextWithDeferTooltip({ className, children, width, maxWidth, side = 'bottom', overTrigger = false, ...props }) {
|
|
7
40
|
const [withTooltip, setWithTooltip] = useState(false);
|
|
8
41
|
const [tooltipWidth, setTooltipWidth] = useState();
|
|
@@ -11,9 +44,7 @@ function TextWithDeferTooltip({ className, children, width, maxWidth, side = 'bo
|
|
|
11
44
|
const handleMouseEnter = () => {
|
|
12
45
|
if (!ref.current)
|
|
13
46
|
return;
|
|
14
|
-
|
|
15
|
-
const isOverflowingVertically = ref.current.scrollHeight - ref.current.clientHeight > 3;
|
|
16
|
-
if (isOverflowingHorizontally || isOverflowingVertically) {
|
|
47
|
+
if (isTextOverflowing(ref.current)) {
|
|
17
48
|
if (width != null) {
|
|
18
49
|
setTooltipWidth(width);
|
|
19
50
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -24,4 +24,5 @@ export declare const Input: import("react").ForwardRefExoticComponent<Omit<impor
|
|
|
24
24
|
checkAutofill?: boolean;
|
|
25
25
|
scrollProps?: Partial<import("../Scroll/Scroll.types").Props>;
|
|
26
26
|
fitContentWidth?: boolean;
|
|
27
|
+
selectAllOnFocus?: boolean;
|
|
27
28
|
} & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -28,4 +28,6 @@ export type Props = Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange'> & Om
|
|
|
28
28
|
scrollProps?: Partial<ScrollProps>;
|
|
29
29
|
/** When true, width follows text via CSS `field-sizing: content` (non-textarea only). */
|
|
30
30
|
fitContentWidth?: boolean;
|
|
31
|
+
/** Select the current value when the field is focused. */
|
|
32
|
+
selectAllOnFocus?: boolean;
|
|
31
33
|
};
|
|
@@ -28,6 +28,7 @@ export declare class Select extends Component<T.Props, T.State> {
|
|
|
28
28
|
checkAutofill?: boolean;
|
|
29
29
|
scrollProps?: Partial<import("../Scroll/Scroll.types").Props>;
|
|
30
30
|
fitContentWidth?: boolean;
|
|
31
|
+
selectAllOnFocus?: boolean;
|
|
31
32
|
} & import("react").RefAttributes<HTMLInputElement>>>;
|
|
32
33
|
triggerInputRef: import("react").RefObject<HTMLDivElement>;
|
|
33
34
|
contentRef: import("react").RefObject<HTMLDivElement>;
|