pesona-ui 1.0.29 → 1.0.31
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.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useState, useRef, useMemo, forwardRef, useCallback } from 'react';
|
|
1
|
+
import React, { useEffect, useState, useRef, useMemo, forwardRef, useImperativeHandle, useCallback } from 'react';
|
|
2
2
|
import ReactDOM, { createPortal } from 'react-dom';
|
|
3
3
|
|
|
4
4
|
const Button = ({ type = 'button', className, children, ...rest }) => {
|
|
@@ -8187,120 +8187,55 @@ const useDropdownPositionAndScroll = (isOpen, dropdownOptionsRef) => {
|
|
|
8187
8187
|
}, [isOpen, dropdownOptionsRef]);
|
|
8188
8188
|
};
|
|
8189
8189
|
|
|
8190
|
-
const Select = forwardRef((
|
|
8190
|
+
const Select = forwardRef((props, ref) => {
|
|
8191
|
+
const { name, label, message, selectLabel = 'Select an option', size = 'md', floatingLabel = false, error, options, value = '', onChange, onBlur, required, disabled, } = props;
|
|
8191
8192
|
const [isOpen, setIsOpen] = useState(false);
|
|
8192
|
-
const [highlightIndex, setHighlightIndex] = useState(-1);
|
|
8193
8193
|
const dropdownRef = useRef(null);
|
|
8194
8194
|
const dropdownOptionsRef = useRef(null);
|
|
8195
|
-
const
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
target: { name, value: coerceValue(val), type: 'select-one' },
|
|
8200
|
-
currentTarget: { name, value: coerceValue(val), type: 'select-one' },
|
|
8201
|
-
}), [name, coerceValue]);
|
|
8202
|
-
const handleOptionSelect = useCallback((optionValue) => {
|
|
8203
|
-
const selectedOption = options.find((opt) => opt.value === optionValue);
|
|
8204
|
-
if (!selectedOption)
|
|
8205
|
-
return;
|
|
8206
|
-
hiddenSelectRef.current.value = String(selectedOption.value);
|
|
8207
|
-
onChange?.(createSyntheticEvent(selectedOption.value));
|
|
8208
|
-
setIsOpen(false);
|
|
8209
|
-
setTimeout(() => {
|
|
8210
|
-
onBlur?.(createSyntheticEvent(selectedOption.value));
|
|
8211
|
-
}, 0);
|
|
8212
|
-
}, [options, onChange, onBlur, createSyntheticEvent]);
|
|
8213
|
-
const handleOutsideClick = useCallback(() => {
|
|
8195
|
+
const selectRef = useRef(null);
|
|
8196
|
+
useImperativeHandle(ref, () => selectRef.current);
|
|
8197
|
+
useEffect(() => setIsOpen(false), []);
|
|
8198
|
+
useOutsideClick([dropdownRef], () => {
|
|
8214
8199
|
setIsOpen(false);
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
onBlur(createSyntheticEvent(value));
|
|
8218
|
-
}, 0);
|
|
8219
|
-
}
|
|
8220
|
-
}, [value, onBlur, createSyntheticEvent]);
|
|
8221
|
-
useOutsideClick([dropdownRef], handleOutsideClick);
|
|
8200
|
+
selectRef.current?.blur(); // Trigger onBlur RHF
|
|
8201
|
+
});
|
|
8222
8202
|
useDropdownPositionAndScroll(isOpen, dropdownOptionsRef);
|
|
8223
|
-
const
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
return;
|
|
8239
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
8240
|
-
e.preventDefault();
|
|
8241
|
-
if (isOpen && highlightIndex >= 0) {
|
|
8242
|
-
handleOptionSelect(options[highlightIndex].value);
|
|
8243
|
-
}
|
|
8244
|
-
else {
|
|
8245
|
-
setIsOpen((prev) => !prev);
|
|
8246
|
-
}
|
|
8247
|
-
}
|
|
8248
|
-
else if (e.key === 'Escape' && isOpen) {
|
|
8249
|
-
setIsOpen(false);
|
|
8250
|
-
}
|
|
8251
|
-
else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
8252
|
-
e.preventDefault();
|
|
8253
|
-
if (!isOpen) {
|
|
8254
|
-
setIsOpen(true);
|
|
8255
|
-
}
|
|
8256
|
-
else {
|
|
8257
|
-
setHighlightIndex((prev) => {
|
|
8258
|
-
const next = e.key === 'ArrowDown'
|
|
8259
|
-
? (prev + 1) % options.length
|
|
8260
|
-
: (prev - 1 + options.length) % options.length;
|
|
8261
|
-
return next;
|
|
8262
|
-
});
|
|
8263
|
-
}
|
|
8264
|
-
}
|
|
8265
|
-
}, [disabled, isOpen, highlightIndex, options, handleOptionSelect]);
|
|
8266
|
-
useEffect(() => {
|
|
8267
|
-
if (isOpen && dropdownOptionsRef.current && highlightIndex >= 0) {
|
|
8268
|
-
const optionElements = dropdownOptionsRef.current.querySelectorAll('.option');
|
|
8269
|
-
const highlighted = optionElements[highlightIndex];
|
|
8270
|
-
highlighted?.scrollIntoView({ block: 'nearest' });
|
|
8203
|
+
const displayText = value
|
|
8204
|
+
? options.find((opt) => opt.value === value)?.label
|
|
8205
|
+
: selectLabel;
|
|
8206
|
+
const handleOptionClick = (optionValue) => {
|
|
8207
|
+
const syntheticEvent = {
|
|
8208
|
+
target: {
|
|
8209
|
+
name,
|
|
8210
|
+
value: optionValue,
|
|
8211
|
+
},
|
|
8212
|
+
};
|
|
8213
|
+
onChange?.(syntheticEvent);
|
|
8214
|
+
// Change the select value to trigger onChange in RHF
|
|
8215
|
+
if (selectRef.current) {
|
|
8216
|
+
selectRef.current.value = String(optionValue);
|
|
8217
|
+
selectRef.current.dispatchEvent(new Event('change', { bubbles: true }));
|
|
8271
8218
|
}
|
|
8272
|
-
|
|
8219
|
+
setIsOpen(false);
|
|
8220
|
+
};
|
|
8273
8221
|
return (React.createElement(React.Fragment, null,
|
|
8274
|
-
React.createElement("select", { ref:
|
|
8275
|
-
hiddenSelectRef.current = el;
|
|
8276
|
-
if (typeof ref === 'function')
|
|
8277
|
-
ref(el);
|
|
8278
|
-
else if (ref)
|
|
8279
|
-
ref.current = el;
|
|
8280
|
-
}, name: name, value: normalizedValue, onChange: onChange, onBlur: onBlur, required: required, disabled: disabled, style: {
|
|
8281
|
-
position: 'absolute',
|
|
8282
|
-
left: '-9999px',
|
|
8283
|
-
opacity: 0,
|
|
8284
|
-
pointerEvents: 'none',
|
|
8285
|
-
width: '1px',
|
|
8286
|
-
height: '1px',
|
|
8287
|
-
}, tabIndex: -1, "aria-hidden": "true" },
|
|
8222
|
+
React.createElement("select", { ref: selectRef, name: name, value: value, onChange: onChange, onBlur: onBlur, required: required, disabled: disabled, style: { position: 'absolute', left: '-9999px' }, tabIndex: -1, "aria-hidden": "true" },
|
|
8288
8223
|
React.createElement("option", { value: "" }, "Select an option"),
|
|
8289
|
-
options.map((
|
|
8224
|
+
options.map((opt) => (React.createElement("option", { key: opt.value, value: opt.value }, opt.label)))),
|
|
8290
8225
|
label && !floatingLabel && (React.createElement("label", { htmlFor: name },
|
|
8291
8226
|
label,
|
|
8292
8227
|
" ",
|
|
8293
8228
|
required && React.createElement("span", { className: "text-danger" }, "*"))),
|
|
8294
8229
|
React.createElement("div", { className: `dropdown-select-container ${size}`, ref: dropdownRef },
|
|
8295
|
-
React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}
|
|
8230
|
+
React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`, onClick: () => !disabled && setIsOpen(!isOpen), "aria-haspopup": "listbox", "aria-expanded": isOpen },
|
|
8296
8231
|
React.createElement("span", { className: "label" }, displayText),
|
|
8297
8232
|
React.createElement(FiChevronDown, { className: "arrow" })),
|
|
8298
|
-
isOpen && (React.createElement("div", { className: "dropdown-options scrollbar", ref: dropdownOptionsRef, role: "listbox" }, options.map((
|
|
8233
|
+
isOpen && (React.createElement("div", { className: "dropdown-options scrollbar", ref: dropdownOptionsRef, role: "listbox" }, options.map((opt) => (React.createElement("div", { key: opt.value, className: `option ${opt.value === value ? 'selected' : ''}`, onClick: () => handleOptionClick(opt.value), role: "option", "aria-selected": opt.value === value }, opt.label)))))),
|
|
8299
8234
|
label && floatingLabel && (React.createElement("label", { htmlFor: name },
|
|
8300
8235
|
label,
|
|
8301
8236
|
" ",
|
|
8302
8237
|
required && React.createElement("span", { className: "text-danger" }, "*"))),
|
|
8303
|
-
error ? (React.createElement("small", { className: "form-message text-danger"
|
|
8238
|
+
error ? (React.createElement("small", { className: "form-message text-danger" }, error)) : (message && React.createElement("small", { className: "form-message text-muted" }, message))));
|
|
8304
8239
|
});
|
|
8305
8240
|
Select.displayName = 'Select';
|
|
8306
8241
|
|
|
@@ -8697,39 +8632,49 @@ const SelectMultiple = forwardRef(({ name, label, message, selectLabel, floating
|
|
|
8697
8632
|
});
|
|
8698
8633
|
SelectMultiple.displayName = 'SelectMultiple';
|
|
8699
8634
|
|
|
8700
|
-
const SelectWithSearch = forwardRef((
|
|
8635
|
+
const SelectWithSearch = forwardRef((props, ref) => {
|
|
8636
|
+
const { name, label, message, selectLabel = 'Select an option', size = 'md', floatingLabel = false, error, options = [], value = '', onChange, required, disabled, selectSearch = '', setSelectSearch, isLoading = false, } = props;
|
|
8701
8637
|
const [isOpen, setIsOpen] = useState(false);
|
|
8702
8638
|
const dropdownRef = useRef(null);
|
|
8703
8639
|
const dropdownOptionsRef = useRef(null);
|
|
8640
|
+
const selectRef = useRef(null);
|
|
8641
|
+
useImperativeHandle(ref, () => selectRef.current);
|
|
8642
|
+
useOutsideClick([dropdownRef], () => setIsOpen(false));
|
|
8643
|
+
useDropdownPositionAndScroll(isOpen, dropdownOptionsRef);
|
|
8644
|
+
const displayText = value
|
|
8645
|
+
? options.find((opt) => opt.value === value)?.label
|
|
8646
|
+
: selectLabel;
|
|
8704
8647
|
const handleOptionClick = (optionValue) => {
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8648
|
+
const syntheticEvent = {
|
|
8649
|
+
target: {
|
|
8650
|
+
name,
|
|
8651
|
+
value: optionValue,
|
|
8652
|
+
},
|
|
8653
|
+
};
|
|
8654
|
+
onChange?.(syntheticEvent);
|
|
8655
|
+
// Change the select value to trigger onChange in RHF
|
|
8656
|
+
if (selectRef.current) {
|
|
8657
|
+
selectRef.current.value = String(optionValue);
|
|
8658
|
+
selectRef.current.dispatchEvent(new Event('change', { bubbles: true }));
|
|
8709
8659
|
}
|
|
8710
8660
|
setIsOpen(false);
|
|
8711
8661
|
};
|
|
8712
|
-
useOutsideClick([dropdownRef], () => setIsOpen(false));
|
|
8713
|
-
useDropdownPositionAndScroll(isOpen, dropdownOptionsRef);
|
|
8714
|
-
useEffect(() => {
|
|
8715
|
-
return () => setIsOpen(false);
|
|
8716
|
-
}, []);
|
|
8717
|
-
const displayText = value
|
|
8718
|
-
? options.find((option) => option.value === value)?.label
|
|
8719
|
-
: selectLabel || 'Select an option';
|
|
8720
8662
|
return (React.createElement(React.Fragment, null,
|
|
8663
|
+
React.createElement("select", { ref: selectRef, name: name, value: value, onChange: onChange, style: { position: 'absolute', left: '-9999px' }, tabIndex: -1, "aria-hidden": "true" },
|
|
8664
|
+
React.createElement("option", { value: "" }, "Select an option"),
|
|
8665
|
+
options.map((opt) => (React.createElement("option", { key: opt.value, value: opt.value }, opt.label)))),
|
|
8721
8666
|
label && !floatingLabel && (React.createElement("label", { htmlFor: name },
|
|
8722
8667
|
label,
|
|
8723
8668
|
" ",
|
|
8724
8669
|
required && React.createElement("span", { className: "text-danger" }, "*"))),
|
|
8725
8670
|
React.createElement("div", { className: `dropdown-select-container ${size}`, ref: dropdownRef },
|
|
8726
|
-
React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`, onClick: () => !disabled && setIsOpen(!isOpen),
|
|
8671
|
+
React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`, onClick: () => !disabled && setIsOpen(!isOpen), "aria-haspopup": "listbox", "aria-expanded": isOpen },
|
|
8727
8672
|
React.createElement("span", { className: "label" }, displayText),
|
|
8728
8673
|
React.createElement(FiChevronDown, { className: "arrow" })),
|
|
8729
|
-
isOpen && (React.createElement("div", { className:
|
|
8674
|
+
isOpen && (React.createElement("div", { className: "dropdown-options scrollbar", ref: dropdownOptionsRef, role: "listbox" },
|
|
8730
8675
|
React.createElement("div", { className: "search-input" },
|
|
8731
|
-
React.createElement(Input, { name: "search", placeholder: "Ketik untuk mencari..", type: "search",
|
|
8732
|
-
isLoading ? (React.createElement("div", { className: "p-10 text-center" }, "Loading options...")) : options.length === 0 ? (React.createElement("div", { className: "p-10 text-center" }, "Tidak ada hasil ditemukan")) : (options.map((
|
|
8676
|
+
React.createElement(Input, { name: "search", placeholder: "Ketik untuk mencari..", type: "search", autoFocus: true, value: selectSearch, onChange: (e) => setSelectSearch?.(e.currentTarget.value) })),
|
|
8677
|
+
isLoading ? (React.createElement("div", { className: "p-10 text-center" }, "Loading options...")) : options.length === 0 ? (React.createElement("div", { className: "p-10 text-center" }, "Tidak ada hasil ditemukan")) : (options.map((opt) => (React.createElement("div", { key: opt.value, className: `option ${value === opt.value ? 'selected' : ''}`, onClick: () => handleOptionClick(opt.value), role: "option", "aria-selected": value === opt.value }, opt.label))))))),
|
|
8733
8678
|
label && floatingLabel && (React.createElement("label", { htmlFor: name },
|
|
8734
8679
|
label,
|
|
8735
8680
|
" ",
|