@seedgrid/fe-components 2026.3.31 → 2026.4.8
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/commons/SgWhistle.d.ts +63 -0
- package/dist/commons/SgWhistle.d.ts.map +1 -0
- package/dist/commons/SgWhistle.js +157 -0
- package/dist/commons/SgWhistleHost.d.ts +16 -0
- package/dist/commons/SgWhistleHost.d.ts.map +1 -0
- package/dist/commons/SgWhistleHost.js +154 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/inputs/SgAutocomplete.d.ts +1 -0
- package/dist/inputs/SgAutocomplete.d.ts.map +1 -1
- package/dist/inputs/SgAutocomplete.js +94 -3
- package/dist/inputs/SgCombobox.d.ts +1 -0
- package/dist/inputs/SgCombobox.d.ts.map +1 -1
- package/dist/inputs/SgCombobox.js +72 -15
- package/dist/inputs/SgDatatable.d.ts +6 -0
- package/dist/inputs/SgDatatable.d.ts.map +1 -1
- package/dist/inputs/SgDatatable.js +237 -56
- package/dist/inputs/SgInputPassword.d.ts.map +1 -1
- package/dist/inputs/SgInputPassword.js +5 -3
- package/dist/inputs/SgInputText.d.ts.map +1 -1
- package/dist/inputs/SgInputText.js +47 -10
- package/dist/layout/SgMenu.d.ts.map +1 -1
- package/dist/layout/SgMenu.js +3 -2
- package/dist/layout/SgScreen.d.ts.map +1 -1
- package/dist/layout/SgScreen.js +5 -32
- package/dist/layout/sgDocking.js +2 -2
- package/dist/overlay/SgConfirmationDialog.d.ts.map +1 -1
- package/dist/overlay/SgConfirmationDialog.js +15 -1
- package/dist/overlay/SgDialog.d.ts.map +1 -1
- package/dist/overlay/SgDialog.js +7 -1
- package/dist/sandbox.cjs +65 -55
- package/package.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
4
5
|
import { ChevronDown } from "lucide-react";
|
|
5
6
|
import { Controller } from "react-hook-form";
|
|
6
7
|
import { mergeRequiredRule, resolveFieldError } from "../rhf";
|
|
@@ -19,7 +20,7 @@ export function SgAutocomplete(props) {
|
|
|
19
20
|
return (_jsx(SgAutocompleteBase, { ...rest, name: name, register: register, rules: resolvedRules }));
|
|
20
21
|
}
|
|
21
22
|
function SgAutocompleteBase(props) {
|
|
22
|
-
const { value, onChange, source, mapItem, minLengthForSearch = 2, delay = 300, maxResult = 50, cacheEnabled = true, cacheTTL = 5 * 60 * 1000, showDropDownButton = false, openOnFocus = false, clearOnSelect = false, allowCustomValue = false, grouped, groupped, placeholderEmpty: placeholderEmptyProp, loadingText: loadingTextProp, onSelect, onSearch, onOpenChange, renderItem, renderGroupHeader, renderFooter, renderEmpty, formatSelection, itemTooltip, inputProps, iconButtons, enabled, readOnly, borderRadius, ...rest } = props;
|
|
23
|
+
const { value, onChange, source, mapItem, minLengthForSearch = 2, delay = 300, maxResult = 50, cacheEnabled = true, cacheTTL = 5 * 60 * 1000, showDropDownButton = false, openOnFocus = false, clearOnSelect = false, allowCustomValue = false, grouped, groupped, placeholderEmpty: placeholderEmptyProp, loadingText: loadingTextProp, onSelect, onSearch, onOpenChange, renderItem, renderGroupHeader, renderFooter, renderEmpty, formatSelection, renderValue, itemTooltip, inputProps, iconButtons, enabled, readOnly, borderRadius, ...rest } = props;
|
|
23
24
|
const i18n = useComponentsI18n();
|
|
24
25
|
const placeholderEmpty = placeholderEmptyProp ?? t(i18n, "components.autocomplete.empty");
|
|
25
26
|
const loadingText = loadingTextProp ?? t(i18n, "components.autocomplete.loading");
|
|
@@ -29,13 +30,21 @@ function SgAutocompleteBase(props) {
|
|
|
29
30
|
const [loading, setLoading] = React.useState(false);
|
|
30
31
|
const [open, setOpen] = React.useState(false);
|
|
31
32
|
const wrapperRef = React.useRef(null);
|
|
33
|
+
const inputRef = React.useRef(null);
|
|
34
|
+
const dropdownRef = React.useRef(null);
|
|
32
35
|
const ignoreBlurRef = React.useRef(false);
|
|
33
36
|
const [activeIndex, setActiveIndex] = React.useState(-1);
|
|
34
37
|
const [lastSelected, setLastSelected] = React.useState("");
|
|
38
|
+
const [selectedItem, setSelectedItem] = React.useState(null);
|
|
39
|
+
const [dropdownStyle, setDropdownStyle] = React.useState({});
|
|
35
40
|
const cacheRef = React.useRef(new Map());
|
|
36
41
|
const requestIdRef = React.useRef(0);
|
|
37
42
|
const openRef = React.useRef(false);
|
|
38
43
|
openRef.current = open;
|
|
44
|
+
// While the user is focused (typing), every incoming `value` prop change is
|
|
45
|
+
// just the parent echoing back our own onChange. Overwriting inputValue in
|
|
46
|
+
// that window drops characters typed faster than one render cycle.
|
|
47
|
+
const isFocusedRef = React.useRef(false);
|
|
39
48
|
const resolvedBorderRadius = React.useMemo(() => {
|
|
40
49
|
if (borderRadius === undefined)
|
|
41
50
|
return undefined;
|
|
@@ -44,8 +53,23 @@ function SgAutocompleteBase(props) {
|
|
|
44
53
|
React.useEffect(() => {
|
|
45
54
|
if (value === undefined)
|
|
46
55
|
return;
|
|
47
|
-
setInputValue(value);
|
|
48
56
|
setLastSelected(value);
|
|
57
|
+
// Never clobber what the user is actively typing.
|
|
58
|
+
if (isFocusedRef.current)
|
|
59
|
+
return;
|
|
60
|
+
setInputValue(value);
|
|
61
|
+
}, [value]);
|
|
62
|
+
React.useEffect(() => {
|
|
63
|
+
if (!value) {
|
|
64
|
+
setSelectedItem(null);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
setSelectedItem((current) => {
|
|
68
|
+
if (current && (current.value === value || current.label === value)) {
|
|
69
|
+
return current;
|
|
70
|
+
}
|
|
71
|
+
return current;
|
|
72
|
+
});
|
|
49
73
|
}, [value]);
|
|
50
74
|
const toItem = React.useCallback((raw) => {
|
|
51
75
|
if (mapItem)
|
|
@@ -112,6 +136,7 @@ function SgAutocompleteBase(props) {
|
|
|
112
136
|
if (item.disabled)
|
|
113
137
|
return;
|
|
114
138
|
const selection = formatSelection ? formatSelection(item) : item.label;
|
|
139
|
+
setSelectedItem(item);
|
|
115
140
|
setInputValue(selection);
|
|
116
141
|
onChange?.(selection);
|
|
117
142
|
setLastSelected(selection);
|
|
@@ -124,6 +149,9 @@ function SgAutocompleteBase(props) {
|
|
|
124
149
|
}
|
|
125
150
|
};
|
|
126
151
|
const handleInputChange = (next) => {
|
|
152
|
+
if (selectedItem && next !== (formatSelection ? formatSelection(selectedItem) : selectedItem.label)) {
|
|
153
|
+
setSelectedItem(null);
|
|
154
|
+
}
|
|
127
155
|
setInputValue(next);
|
|
128
156
|
onChange?.(next);
|
|
129
157
|
if (next.length === 0) {
|
|
@@ -149,6 +177,7 @@ function SgAutocompleteBase(props) {
|
|
|
149
177
|
}, 0);
|
|
150
178
|
return;
|
|
151
179
|
}
|
|
180
|
+
isFocusedRef.current = false;
|
|
152
181
|
if (!allowCustomValue && lastSelected && inputValue !== lastSelected) {
|
|
153
182
|
setInputValue(lastSelected);
|
|
154
183
|
onChange?.(lastSelected);
|
|
@@ -157,6 +186,11 @@ function SgAutocompleteBase(props) {
|
|
|
157
186
|
onOpenChange?.(false);
|
|
158
187
|
};
|
|
159
188
|
const handleFocus = () => {
|
|
189
|
+
isFocusedRef.current = true;
|
|
190
|
+
if (renderValue && selectedItem) {
|
|
191
|
+
const selection = formatSelection ? formatSelection(selectedItem) : selectedItem.label;
|
|
192
|
+
setInputValue(selection);
|
|
193
|
+
}
|
|
160
194
|
if (openOnFocus) {
|
|
161
195
|
setOpen(true);
|
|
162
196
|
onOpenChange?.(true);
|
|
@@ -185,6 +219,21 @@ function SgAutocompleteBase(props) {
|
|
|
185
219
|
onOpenChange?.(false);
|
|
186
220
|
}
|
|
187
221
|
};
|
|
222
|
+
const syncDropdownPosition = React.useCallback(() => {
|
|
223
|
+
const anchor = wrapperRef.current?.querySelector("input") ?? inputRef.current ?? wrapperRef.current;
|
|
224
|
+
if (!anchor)
|
|
225
|
+
return;
|
|
226
|
+
const rect = anchor.getBoundingClientRect();
|
|
227
|
+
setDropdownStyle({
|
|
228
|
+
position: "fixed",
|
|
229
|
+
top: rect.bottom + 4,
|
|
230
|
+
left: rect.left,
|
|
231
|
+
minWidth: rect.width,
|
|
232
|
+
width: "max-content",
|
|
233
|
+
maxWidth: "min(32rem, calc(100vw - 24px))",
|
|
234
|
+
borderRadius: resolvedBorderRadius
|
|
235
|
+
});
|
|
236
|
+
}, [resolvedBorderRadius]);
|
|
188
237
|
const dropdownButton = showDropDownButton ? (_jsx("button", { type: "button", className: "text-foreground/60 hover:text-foreground", onMouseDown: (event) => {
|
|
189
238
|
event.preventDefault();
|
|
190
239
|
ignoreBlurRef.current = true;
|
|
@@ -236,13 +285,53 @@ function SgAutocompleteBase(props) {
|
|
|
236
285
|
}, onClick: () => selectItem(item), children: [renderItem ? renderItem(item, isActive) : item.label, itemTooltip ? (_jsx("div", { className: "pointer-events-none absolute left-full top-1/2 z-20 ml-2 -translate-y-1/2 rounded border border-border bg-[rgb(var(--sg-surface,var(--sg-bg)))] px-2 py-1 text-xs text-[rgb(var(--sg-text,var(--sg-fg)))] shadow-md opacity-0 transition-opacity group-hover:opacity-100", children: itemTooltip(item) })) : null] }, item.id));
|
|
237
286
|
});
|
|
238
287
|
};
|
|
288
|
+
React.useEffect(() => {
|
|
289
|
+
if (!open)
|
|
290
|
+
return;
|
|
291
|
+
const handleOutside = (event) => {
|
|
292
|
+
if (wrapperRef.current?.contains(event.target))
|
|
293
|
+
return;
|
|
294
|
+
if (dropdownRef.current?.contains(event.target))
|
|
295
|
+
return;
|
|
296
|
+
if (!allowCustomValue && lastSelected && inputValue !== lastSelected) {
|
|
297
|
+
setInputValue(lastSelected);
|
|
298
|
+
onChange?.(lastSelected);
|
|
299
|
+
}
|
|
300
|
+
setOpen(false);
|
|
301
|
+
onOpenChange?.(false);
|
|
302
|
+
};
|
|
303
|
+
document.addEventListener("mousedown", handleOutside);
|
|
304
|
+
return () => document.removeEventListener("mousedown", handleOutside);
|
|
305
|
+
}, [allowCustomValue, inputValue, lastSelected, onChange, onOpenChange, open]);
|
|
306
|
+
React.useEffect(() => {
|
|
307
|
+
if (!open)
|
|
308
|
+
return;
|
|
309
|
+
syncDropdownPosition();
|
|
310
|
+
const handleLayoutChange = () => {
|
|
311
|
+
syncDropdownPosition();
|
|
312
|
+
};
|
|
313
|
+
window.addEventListener("resize", handleLayoutChange);
|
|
314
|
+
window.addEventListener("scroll", handleLayoutChange, true);
|
|
315
|
+
return () => {
|
|
316
|
+
window.removeEventListener("resize", handleLayoutChange);
|
|
317
|
+
window.removeEventListener("scroll", handleLayoutChange, true);
|
|
318
|
+
};
|
|
319
|
+
}, [open, syncDropdownPosition]);
|
|
239
320
|
return (_jsxs("div", { className: "relative", ref: wrapperRef, children: [_jsx(SgInputText, { ...rest, enabled: enabled, readOnly: readOnly, borderRadius: borderRadius, iconButtons: mergedIconButtons, inputProps: {
|
|
240
321
|
...inputProps,
|
|
322
|
+
ref: inputRef,
|
|
241
323
|
autoComplete: "off",
|
|
242
324
|
autoCorrect: "off",
|
|
243
325
|
autoCapitalize: "off",
|
|
244
326
|
spellCheck: false,
|
|
245
327
|
value: inputValue,
|
|
328
|
+
style: renderValue && selectedItem && !open
|
|
329
|
+
? {
|
|
330
|
+
...(inputProps?.style ?? {}),
|
|
331
|
+
color: "transparent",
|
|
332
|
+
textShadow: "none"
|
|
333
|
+
}
|
|
334
|
+
: inputProps?.style,
|
|
246
335
|
onChange: (event) => handleInputChange(event.currentTarget.value),
|
|
247
336
|
onBlur: (event) => {
|
|
248
337
|
inputProps?.onBlur?.(event);
|
|
@@ -256,5 +345,7 @@ function SgAutocompleteBase(props) {
|
|
|
256
345
|
inputProps?.onKeyDown?.(event);
|
|
257
346
|
handleKeyDown(event);
|
|
258
347
|
}
|
|
259
|
-
} }),
|
|
348
|
+
} }), renderValue && selectedItem && !open ? (_jsx("div", { className: "pointer-events-none absolute inset-y-0 left-3 right-10 z-10 flex items-center overflow-hidden", children: renderValue(selectedItem) })) : null, open && !(enabled === false || readOnly) && typeof document !== "undefined"
|
|
349
|
+
? createPortal(_jsxs("div", { ref: dropdownRef, className: "z-[1100] overflow-hidden rounded-md border border-border bg-[rgb(var(--sg-surface,var(--sg-bg)))] text-[rgb(var(--sg-text,var(--sg-fg)))] shadow-lg", style: dropdownStyle, children: [_jsx("div", { className: "max-h-64 overflow-auto", children: listContent() }), renderFooter ? (_jsx("div", { className: "border-t border-border bg-[rgb(var(--sg-surface,var(--sg-bg)))] px-3 py-2", children: renderFooter(inputValue, items.length > 0) })) : null] }), document.body)
|
|
350
|
+
: null] }));
|
|
260
351
|
}
|
|
@@ -16,6 +16,7 @@ export type SgComboboxProps<T = SgAutocompleteItem> = SgComboboxBaseProps & {
|
|
|
16
16
|
openOnFocus?: boolean;
|
|
17
17
|
onSelect?: (value: T) => void;
|
|
18
18
|
renderItem?: (item: SgAutocompleteItem, isActive: boolean) => React.ReactNode;
|
|
19
|
+
renderValue?: (item: SgAutocompleteItem | null) => React.ReactNode;
|
|
19
20
|
renderGroupHeader?: (group: string) => React.ReactNode;
|
|
20
21
|
renderFooter?: (query: string, hasResults: boolean) => React.ReactNode;
|
|
21
22
|
itemTooltip?: (item: SgAutocompleteItem) => React.ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SgCombobox.d.ts","sourceRoot":"","sources":["../../src/inputs/SgCombobox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SgCombobox.d.ts","sourceRoot":"","sources":["../../src/inputs/SgCombobox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGtF,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAE5C,KAAK,mBAAmB,GAAG,IAAI,CAC7B,gBAAgB,EAChB,UAAU,GAAG,aAAa,GAAG,aAAa,GAAG,YAAY,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAC1F,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,CAAC,GAAG,kBAAkB,IAAI,CAAC,EAAE,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;AAErF,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,kBAAkB,IAAI,mBAAmB,GAAG;IAC1E,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC5B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,kBAAkB,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,OAAO,KAAK,KAAK,CAAC,SAAS,CAAC;IAC9E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,KAAK,KAAK,CAAC,SAAS,CAAC;IACnE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IACvD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK,KAAK,CAAC,SAAS,CAAC;IACvE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,KAAK,CAAC,SAAS,CAAC;IAC5D,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAC;CAC7C,CAAC;AA8jBF,wBAAgB,UAAU,CAAC,CAAC,GAAG,kBAAkB,EAAE,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,2CAsBrF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
4
5
|
import { ChevronDown } from "lucide-react";
|
|
5
6
|
import { Controller } from "react-hook-form";
|
|
6
7
|
import { resolveFieldError } from "../rhf";
|
|
@@ -51,13 +52,15 @@ function findTypeAheadMatchIndex(entries, query, startIndex) {
|
|
|
51
52
|
return -1;
|
|
52
53
|
}
|
|
53
54
|
function SgComboboxBase(props) {
|
|
54
|
-
const { source, mapItem: mapItemProp, value, onValueChange, grouped, groupped, loadingText: loadingTextProp, emptyText: emptyTextProp, openOnFocus = false, onSelect, renderItem, renderGroupHeader, renderFooter, itemTooltip, inputProps, enabled, borderRadius, ...rest } = props;
|
|
55
|
+
const { source, mapItem: mapItemProp, value, onValueChange, grouped, groupped, loadingText: loadingTextProp, emptyText: emptyTextProp, openOnFocus = false, onSelect, renderItem, renderValue, renderGroupHeader, renderFooter, itemTooltip, inputProps, enabled, borderRadius, ...rest } = props;
|
|
55
56
|
const i18n = useComponentsI18n();
|
|
56
57
|
const loadingText = loadingTextProp ?? t(i18n, "components.autocomplete.loading");
|
|
57
58
|
const emptyText = emptyTextProp ?? t(i18n, "components.autocomplete.empty");
|
|
58
59
|
const effectiveGrouped = grouped ?? groupped ?? false;
|
|
59
60
|
const isControlled = value !== undefined;
|
|
60
61
|
const wrapperRef = React.useRef(null);
|
|
62
|
+
const inputRef = React.useRef(null);
|
|
63
|
+
const dropdownRef = React.useRef(null);
|
|
61
64
|
const ignoreBlurRef = React.useRef(false);
|
|
62
65
|
const requestIdRef = React.useRef(0);
|
|
63
66
|
const typeAheadRef = React.useRef({
|
|
@@ -70,6 +73,7 @@ function SgComboboxBase(props) {
|
|
|
70
73
|
const [activeIndex, setActiveIndex] = React.useState(-1);
|
|
71
74
|
const [internalValue, setInternalValue] = React.useState(null);
|
|
72
75
|
const [lastSelectedLabel, setLastSelectedLabel] = React.useState("");
|
|
76
|
+
const [dropdownStyle, setDropdownStyle] = React.useState({});
|
|
73
77
|
const resolvedBorderRadius = React.useMemo(() => {
|
|
74
78
|
if (borderRadius === undefined)
|
|
75
79
|
return undefined;
|
|
@@ -149,6 +153,18 @@ function SgComboboxBase(props) {
|
|
|
149
153
|
const displayedValue = resolvedValue == null || resolvedValue === ""
|
|
150
154
|
? ""
|
|
151
155
|
: selectedEntry?.item.label ?? lastSelectedLabel;
|
|
156
|
+
const selectedVisualItem = React.useMemo(() => {
|
|
157
|
+
if (selectedEntry?.item)
|
|
158
|
+
return selectedEntry.item;
|
|
159
|
+
if (resolvedValue == null || resolvedValue === "")
|
|
160
|
+
return null;
|
|
161
|
+
if (!lastSelectedLabel)
|
|
162
|
+
return null;
|
|
163
|
+
return {
|
|
164
|
+
id: resolvedValue,
|
|
165
|
+
label: lastSelectedLabel
|
|
166
|
+
};
|
|
167
|
+
}, [lastSelectedLabel, resolvedValue, selectedEntry]);
|
|
152
168
|
const setSelectedValue = React.useCallback((nextValue) => {
|
|
153
169
|
if (!isControlled) {
|
|
154
170
|
setInternalValue(nextValue);
|
|
@@ -166,6 +182,21 @@ function SgComboboxBase(props) {
|
|
|
166
182
|
setOpen(true);
|
|
167
183
|
void refreshFromSource();
|
|
168
184
|
}, [isDisabled, refreshFromSource]);
|
|
185
|
+
const syncDropdownPosition = React.useCallback(() => {
|
|
186
|
+
const anchor = wrapperRef.current?.querySelector("input") ?? inputRef.current ?? wrapperRef.current;
|
|
187
|
+
if (!anchor)
|
|
188
|
+
return;
|
|
189
|
+
const rect = anchor.getBoundingClientRect();
|
|
190
|
+
setDropdownStyle({
|
|
191
|
+
position: "fixed",
|
|
192
|
+
top: rect.bottom + 4,
|
|
193
|
+
left: rect.left,
|
|
194
|
+
minWidth: rect.width,
|
|
195
|
+
width: "max-content",
|
|
196
|
+
maxWidth: "min(32rem, calc(100vw - 24px))",
|
|
197
|
+
borderRadius: resolvedBorderRadius
|
|
198
|
+
});
|
|
199
|
+
}, [resolvedBorderRadius]);
|
|
169
200
|
const selectIndex = React.useCallback((index) => {
|
|
170
201
|
const entry = entries[index];
|
|
171
202
|
if (!entry || entry.item.disabled)
|
|
@@ -217,11 +248,27 @@ function SgComboboxBase(props) {
|
|
|
217
248
|
const handleOutside = (event) => {
|
|
218
249
|
if (wrapperRef.current?.contains(event.target))
|
|
219
250
|
return;
|
|
251
|
+
if (dropdownRef.current?.contains(event.target))
|
|
252
|
+
return;
|
|
220
253
|
closeDropdown();
|
|
221
254
|
};
|
|
222
255
|
document.addEventListener("mousedown", handleOutside);
|
|
223
256
|
return () => document.removeEventListener("mousedown", handleOutside);
|
|
224
257
|
}, [closeDropdown, open]);
|
|
258
|
+
React.useEffect(() => {
|
|
259
|
+
if (!open)
|
|
260
|
+
return;
|
|
261
|
+
syncDropdownPosition();
|
|
262
|
+
const handleLayoutChange = () => {
|
|
263
|
+
syncDropdownPosition();
|
|
264
|
+
};
|
|
265
|
+
window.addEventListener("resize", handleLayoutChange);
|
|
266
|
+
window.addEventListener("scroll", handleLayoutChange, true);
|
|
267
|
+
return () => {
|
|
268
|
+
window.removeEventListener("resize", handleLayoutChange);
|
|
269
|
+
window.removeEventListener("scroll", handleLayoutChange, true);
|
|
270
|
+
};
|
|
271
|
+
}, [open, syncDropdownPosition]);
|
|
225
272
|
React.useEffect(() => {
|
|
226
273
|
if (!open)
|
|
227
274
|
return;
|
|
@@ -261,9 +308,17 @@ function SgComboboxBase(props) {
|
|
|
261
308
|
}
|
|
262
309
|
openDropdown();
|
|
263
310
|
}, "aria-label": t(i18n, "components.actions.openList"), children: _jsx(ChevronDown, { size: 16 }) }));
|
|
264
|
-
return (_jsxs("div", { className: "relative", ref: wrapperRef, children: [_jsx(SgInputText, { ...rest, enabled: enabled, borderRadius: borderRadius, clearButton: false, readOnly: true, iconButtons: [dropdownButton], inputProps: {
|
|
311
|
+
return (_jsxs("div", { className: open ? "relative z-[1100]" : "relative", ref: wrapperRef, children: [_jsx(SgInputText, { ...rest, enabled: enabled, borderRadius: borderRadius, clearButton: false, readOnly: true, iconButtons: [dropdownButton], inputProps: {
|
|
265
312
|
...inputProps,
|
|
313
|
+
ref: inputRef,
|
|
266
314
|
value: displayedValue,
|
|
315
|
+
style: renderValue && displayedValue
|
|
316
|
+
? {
|
|
317
|
+
...(inputProps?.style ?? {}),
|
|
318
|
+
color: "transparent",
|
|
319
|
+
textShadow: "none"
|
|
320
|
+
}
|
|
321
|
+
: inputProps?.style,
|
|
267
322
|
onMouseDown: (event) => {
|
|
268
323
|
inputProps?.onMouseDown?.(event);
|
|
269
324
|
if (isDisabled)
|
|
@@ -340,19 +395,21 @@ function SgComboboxBase(props) {
|
|
|
340
395
|
}
|
|
341
396
|
}
|
|
342
397
|
}
|
|
343
|
-
} }),
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
398
|
+
} }), renderValue && displayedValue ? (_jsx("div", { className: "pointer-events-none absolute inset-y-0 left-3 right-10 z-10 flex items-center overflow-hidden", children: renderValue(selectedVisualItem) })) : null, open && !isDisabled && typeof document !== "undefined"
|
|
399
|
+
? createPortal(_jsxs("div", { ref: dropdownRef, className: "z-[1100] overflow-hidden rounded-md border border-border bg-[rgb(var(--sg-surface,var(--sg-bg)))] text-[rgb(var(--sg-text,var(--sg-fg)))] shadow-lg", style: dropdownStyle, children: [_jsx("div", { className: "max-h-64 overflow-auto", children: loading ? (_jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: loadingText })) : entries.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: emptyText })) : groupedEntries ? (groupedEntries.map(({ group, list }) => (_jsxs("div", { className: "border-b border-border last:border-b-0", children: [_jsx("div", { className: "px-3 py-1 text-xs font-semibold text-muted-foreground", children: renderGroupHeader ? renderGroupHeader(group) : group || " " }), list.map(({ entry, index }) => {
|
|
400
|
+
const isActive = activeIndex === index;
|
|
401
|
+
return (_jsxs("div", { className: `group relative cursor-pointer px-3 py-2 text-sm ${isActive ? "bg-muted/60" : ""} ${entry.item.disabled ? "cursor-not-allowed opacity-50" : "hover:bg-muted/40"}`, onMouseEnter: () => setActiveIndex(index), onMouseDown: (event) => {
|
|
402
|
+
event.preventDefault();
|
|
403
|
+
ignoreBlurRef.current = true;
|
|
404
|
+
}, onClick: () => selectIndex(index), children: [renderItem ? renderItem(entry.item, isActive) : entry.item.label, itemTooltip ? (_jsx("div", { className: "pointer-events-none absolute left-full top-1/2 z-20 ml-2 -translate-y-1/2 rounded border border-border bg-[rgb(var(--sg-surface,var(--sg-bg)))] px-2 py-1 text-xs text-[rgb(var(--sg-text,var(--sg-fg)))] shadow-md opacity-0 transition-opacity group-hover:opacity-100", children: itemTooltip(entry.item) })) : null] }, entry.item.id));
|
|
405
|
+
})] }, group || "default")))) : (entries.map((entry, index) => {
|
|
406
|
+
const isActive = activeIndex === index;
|
|
407
|
+
return (_jsxs("div", { className: `group relative cursor-pointer px-3 py-2 text-sm ${isActive ? "bg-muted/60" : ""} ${entry.item.disabled ? "cursor-not-allowed opacity-50" : "hover:bg-muted/40"}`, onMouseEnter: () => setActiveIndex(index), onMouseDown: (event) => {
|
|
408
|
+
event.preventDefault();
|
|
409
|
+
ignoreBlurRef.current = true;
|
|
410
|
+
}, onClick: () => selectIndex(index), children: [renderItem ? renderItem(entry.item, isActive) : entry.item.label, itemTooltip ? (_jsx("div", { className: "pointer-events-none absolute left-full top-1/2 z-20 ml-2 -translate-y-1/2 rounded border border-border bg-[rgb(var(--sg-surface,var(--sg-bg)))] px-2 py-1 text-xs text-[rgb(var(--sg-text,var(--sg-fg)))] shadow-md opacity-0 transition-opacity group-hover:opacity-100", children: itemTooltip(entry.item) })) : null] }, entry.item.id));
|
|
411
|
+
})) }), renderFooter ? (_jsx("div", { className: "border-t border-border bg-[rgb(var(--sg-surface,var(--sg-bg)))] px-3 py-2", children: renderFooter("", entries.length > 0) })) : null] }), document.body)
|
|
412
|
+
: null] }));
|
|
356
413
|
}
|
|
357
414
|
export function SgCombobox(props) {
|
|
358
415
|
const { control, name, rules, ...rest } = props;
|
|
@@ -27,6 +27,7 @@ export type SgDatatableCellMeta<T extends SgDatatableRow> = {
|
|
|
27
27
|
rowData: T;
|
|
28
28
|
};
|
|
29
29
|
export type SgDatatableColumn<T extends SgDatatableRow = SgDatatableRow> = {
|
|
30
|
+
columnId?: string;
|
|
30
31
|
field?: string;
|
|
31
32
|
header: React.ReactNode;
|
|
32
33
|
body?: (rowData: T, meta: SgDatatableCellMeta<T>) => React.ReactNode;
|
|
@@ -43,6 +44,8 @@ export type SgDatatableColumn<T extends SgDatatableRow = SgDatatableRow> = {
|
|
|
43
44
|
width?: number | string;
|
|
44
45
|
minWidth?: number | string;
|
|
45
46
|
hidden?: boolean;
|
|
47
|
+
hideable?: boolean;
|
|
48
|
+
reorderable?: boolean;
|
|
46
49
|
className?: string;
|
|
47
50
|
headerClassName?: string;
|
|
48
51
|
bodyClassName?: string | ((rowData: T, rowIndex: number) => string | undefined);
|
|
@@ -97,6 +100,9 @@ export type SgDatatableProps<T extends SgDatatableRow = SgDatatableRow> = {
|
|
|
97
100
|
groupBoxProps?: Omit<Partial<SgGroupBoxProps>, "children" | "title"> & {
|
|
98
101
|
title?: string;
|
|
99
102
|
};
|
|
103
|
+
showColumnManager?: boolean;
|
|
104
|
+
columnManagerLabel?: string;
|
|
105
|
+
columnManagerMaxHeight?: number | string;
|
|
100
106
|
};
|
|
101
107
|
export declare const SgDatatable: <T extends SgDatatableRow>(props: SgDatatableProps<T> & {
|
|
102
108
|
ref?: React.Ref<SgDatatableRef<T>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SgDatatable.d.ts","sourceRoot":"","sources":["../../src/inputs/SgDatatable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SgDatatable.d.ts","sourceRoot":"","sources":["../../src/inputs/SgDatatable.tsx"],"names":[],"mappings":"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"}
|