pesona-ui 1.0.27 → 1.0.29
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/index.cjs.js
CHANGED
|
@@ -8189,53 +8189,120 @@ const useDropdownPositionAndScroll = (isOpen, dropdownOptionsRef) => {
|
|
|
8189
8189
|
}, [isOpen, dropdownOptionsRef]);
|
|
8190
8190
|
};
|
|
8191
8191
|
|
|
8192
|
-
const Select = React.forwardRef(({ name, label, message, selectLabel, size = 'md', floatingLabel = false, error, options, value = '', onChange, required, disabled, }, ref) => {
|
|
8192
|
+
const Select = React.forwardRef(({ name, label, message, selectLabel, size = 'md', floatingLabel = false, error, options, value = '', onChange, onBlur, required, disabled, }, ref) => {
|
|
8193
8193
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
8194
|
+
const [highlightIndex, setHighlightIndex] = React.useState(-1);
|
|
8194
8195
|
const dropdownRef = React.useRef(null);
|
|
8195
8196
|
const dropdownOptionsRef = React.useRef(null);
|
|
8196
|
-
const
|
|
8197
|
+
const hiddenSelectRef = React.useRef(null);
|
|
8198
|
+
const shouldReturnNumber = React.useMemo(() => options.every((opt) => typeof opt.value === 'number'), [options]);
|
|
8199
|
+
const coerceValue = React.useCallback((val) => (shouldReturnNumber ? Number(val) : val), [shouldReturnNumber]);
|
|
8200
|
+
const createSyntheticEvent = React.useCallback((val) => ({
|
|
8201
|
+
target: { name, value: coerceValue(val), type: 'select-one' },
|
|
8202
|
+
currentTarget: { name, value: coerceValue(val), type: 'select-one' },
|
|
8203
|
+
}), [name, coerceValue]);
|
|
8204
|
+
const handleOptionSelect = React.useCallback((optionValue) => {
|
|
8197
8205
|
const selectedOption = options.find((opt) => opt.value === optionValue);
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
if (onChange) {
|
|
8203
|
-
onChange({
|
|
8204
|
-
target: {
|
|
8205
|
-
name,
|
|
8206
|
-
value: actualValue,
|
|
8207
|
-
},
|
|
8208
|
-
});
|
|
8209
|
-
}
|
|
8206
|
+
if (!selectedOption)
|
|
8207
|
+
return;
|
|
8208
|
+
hiddenSelectRef.current.value = String(selectedOption.value);
|
|
8209
|
+
onChange?.(createSyntheticEvent(selectedOption.value));
|
|
8210
8210
|
setIsOpen(false);
|
|
8211
|
-
|
|
8212
|
-
|
|
8211
|
+
setTimeout(() => {
|
|
8212
|
+
onBlur?.(createSyntheticEvent(selectedOption.value));
|
|
8213
|
+
}, 0);
|
|
8214
|
+
}, [options, onChange, onBlur, createSyntheticEvent]);
|
|
8215
|
+
const handleOutsideClick = React.useCallback(() => {
|
|
8213
8216
|
setIsOpen(false);
|
|
8214
|
-
|
|
8217
|
+
if (onBlur && value !== undefined && value !== null && value !== '') {
|
|
8218
|
+
setTimeout(() => {
|
|
8219
|
+
onBlur(createSyntheticEvent(value));
|
|
8220
|
+
}, 0);
|
|
8221
|
+
}
|
|
8222
|
+
}, [value, onBlur, createSyntheticEvent]);
|
|
8223
|
+
useOutsideClick([dropdownRef], handleOutsideClick);
|
|
8215
8224
|
useDropdownPositionAndScroll(isOpen, dropdownOptionsRef);
|
|
8225
|
+
const normalizedValue = React.useMemo(() => (value === null || value === undefined || value === '' ? '' : String(value)), [value]);
|
|
8226
|
+
const selectedOption = React.useMemo(() => options.find((opt) => String(opt.value) === normalizedValue), [options, normalizedValue]);
|
|
8227
|
+
const displayText = selectedOption?.label || selectLabel || 'Select an option';
|
|
8216
8228
|
React.useEffect(() => {
|
|
8217
|
-
|
|
8229
|
+
if (hiddenSelectRef.current)
|
|
8230
|
+
hiddenSelectRef.current.value = normalizedValue;
|
|
8231
|
+
}, [normalizedValue]);
|
|
8232
|
+
React.useEffect(() => {
|
|
8233
|
+
if (isOpen) {
|
|
8234
|
+
const index = options.findIndex((opt) => String(opt.value) === normalizedValue);
|
|
8235
|
+
setHighlightIndex(index >= 0 ? index : 0);
|
|
8236
|
+
}
|
|
8237
|
+
}, [isOpen, options, normalizedValue]);
|
|
8238
|
+
const handleKeyDown = React.useCallback((e) => {
|
|
8239
|
+
if (disabled)
|
|
8240
|
+
return;
|
|
8241
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
8242
|
+
e.preventDefault();
|
|
8243
|
+
if (isOpen && highlightIndex >= 0) {
|
|
8244
|
+
handleOptionSelect(options[highlightIndex].value);
|
|
8245
|
+
}
|
|
8246
|
+
else {
|
|
8247
|
+
setIsOpen((prev) => !prev);
|
|
8248
|
+
}
|
|
8249
|
+
}
|
|
8250
|
+
else if (e.key === 'Escape' && isOpen) {
|
|
8218
8251
|
setIsOpen(false);
|
|
8219
|
-
}
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8252
|
+
}
|
|
8253
|
+
else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
8254
|
+
e.preventDefault();
|
|
8255
|
+
if (!isOpen) {
|
|
8256
|
+
setIsOpen(true);
|
|
8257
|
+
}
|
|
8258
|
+
else {
|
|
8259
|
+
setHighlightIndex((prev) => {
|
|
8260
|
+
const next = e.key === 'ArrowDown'
|
|
8261
|
+
? (prev + 1) % options.length
|
|
8262
|
+
: (prev - 1 + options.length) % options.length;
|
|
8263
|
+
return next;
|
|
8264
|
+
});
|
|
8265
|
+
}
|
|
8266
|
+
}
|
|
8267
|
+
}, [disabled, isOpen, highlightIndex, options, handleOptionSelect]);
|
|
8268
|
+
React.useEffect(() => {
|
|
8269
|
+
if (isOpen && dropdownOptionsRef.current && highlightIndex >= 0) {
|
|
8270
|
+
const optionElements = dropdownOptionsRef.current.querySelectorAll('.option');
|
|
8271
|
+
const highlighted = optionElements[highlightIndex];
|
|
8272
|
+
highlighted?.scrollIntoView({ block: 'nearest' });
|
|
8273
|
+
}
|
|
8274
|
+
}, [highlightIndex, isOpen]);
|
|
8224
8275
|
return (React.createElement(React.Fragment, null,
|
|
8276
|
+
React.createElement("select", { ref: (el) => {
|
|
8277
|
+
hiddenSelectRef.current = el;
|
|
8278
|
+
if (typeof ref === 'function')
|
|
8279
|
+
ref(el);
|
|
8280
|
+
else if (ref)
|
|
8281
|
+
ref.current = el;
|
|
8282
|
+
}, name: name, value: normalizedValue, onChange: onChange, onBlur: onBlur, required: required, disabled: disabled, style: {
|
|
8283
|
+
position: 'absolute',
|
|
8284
|
+
left: '-9999px',
|
|
8285
|
+
opacity: 0,
|
|
8286
|
+
pointerEvents: 'none',
|
|
8287
|
+
width: '1px',
|
|
8288
|
+
height: '1px',
|
|
8289
|
+
}, tabIndex: -1, "aria-hidden": "true" },
|
|
8290
|
+
React.createElement("option", { value: "" }, "Select an option"),
|
|
8291
|
+
options.map((option) => (React.createElement("option", { key: option.value, value: option.value }, option.label)))),
|
|
8225
8292
|
label && !floatingLabel && (React.createElement("label", { htmlFor: name },
|
|
8226
8293
|
label,
|
|
8227
8294
|
" ",
|
|
8228
8295
|
required && React.createElement("span", { className: "text-danger" }, "*"))),
|
|
8229
8296
|
React.createElement("div", { className: `dropdown-select-container ${size}`, ref: dropdownRef },
|
|
8230
|
-
React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`, onClick: () => !disabled && setIsOpen(!isOpen),
|
|
8297
|
+
React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''} ${error ? 'error' : ''}`, onClick: () => !disabled && setIsOpen(!isOpen), onKeyDown: handleKeyDown, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-invalid": !!error, tabIndex: disabled ? -1 : 0, role: "combobox" },
|
|
8231
8298
|
React.createElement("span", { className: "label" }, displayText),
|
|
8232
8299
|
React.createElement(FiChevronDown, { className: "arrow" })),
|
|
8233
|
-
isOpen && (React.createElement("div", { className:
|
|
8300
|
+
isOpen && (React.createElement("div", { className: "dropdown-options scrollbar", ref: dropdownOptionsRef, role: "listbox" }, options.map((option, index) => (React.createElement("div", { key: `${option.value}-${index}`, className: `option ${String(value) === String(option.value) ? 'selected' : ''} ${highlightIndex === index ? 'highlight' : ''}`, onClick: () => !disabled && handleOptionSelect(option.value), role: "option", "aria-selected": String(value) === String(option.value), tabIndex: 0 }, option.label)))))),
|
|
8234
8301
|
label && floatingLabel && (React.createElement("label", { htmlFor: name },
|
|
8235
8302
|
label,
|
|
8236
8303
|
" ",
|
|
8237
8304
|
required && React.createElement("span", { className: "text-danger" }, "*"))),
|
|
8238
|
-
error ? (React.createElement("small", { className: "form-message text-danger" }, error)) : (message && React.createElement("small", { className: "form-message text-muted" }, message))));
|
|
8305
|
+
error ? (React.createElement("small", { className: "form-message text-danger", role: "alert" }, error)) : (message && React.createElement("small", { className: "form-message text-muted" }, message))));
|
|
8239
8306
|
});
|
|
8240
8307
|
Select.displayName = 'Select';
|
|
8241
8308
|
|
|
@@ -8573,9 +8640,12 @@ const RadioButtonGroup = React.forwardRef(({ name, label, message, size = 'md',
|
|
|
8573
8640
|
label,
|
|
8574
8641
|
" ",
|
|
8575
8642
|
required && React.createElement("span", { className: "text-danger" }, "*"))),
|
|
8576
|
-
React.createElement("div", { className: "btn-group", "data-toggle": "buttons" }, options.map((option) =>
|
|
8577
|
-
|
|
8578
|
-
React.createElement("
|
|
8643
|
+
React.createElement("div", { className: "btn-group", "data-toggle": "buttons" }, options.map((option) => {
|
|
8644
|
+
const isActive = option.value.toString() === selectedValue?.toString();
|
|
8645
|
+
return (React.createElement("label", { key: option.value.toString(), className: `btn auto btn-default btn-${size} ${isActive ? 'active' : ''}` },
|
|
8646
|
+
React.createElement("input", { type: "radio", id: option.value.toString(), name: name, value: option.value.toString(), ref: ref, ...rest }),
|
|
8647
|
+
React.createElement("span", null, option.label)));
|
|
8648
|
+
})),
|
|
8579
8649
|
error ? (React.createElement("small", { className: "form-message text-danger" }, error)) : (message && React.createElement("small", { className: "form-message text-muted" }, message))));
|
|
8580
8650
|
});
|
|
8581
8651
|
RadioButtonGroup.displayName = 'RadioButtonGroup';
|