pesona-ui 1.0.29 → 1.0.30

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
@@ -8191,95 +8191,44 @@ const useDropdownPositionAndScroll = (isOpen, dropdownOptionsRef) => {
8191
8191
 
8192
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
+ const [internalValue, setInternalValue] = React.useState(value);
8195
8195
  const dropdownRef = React.useRef(null);
8196
8196
  const dropdownOptionsRef = React.useRef(null);
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) => {
8205
- const selectedOption = options.find((opt) => opt.value === optionValue);
8206
- if (!selectedOption)
8207
- return;
8208
- hiddenSelectRef.current.value = String(selectedOption.value);
8209
- onChange?.(createSyntheticEvent(selectedOption.value));
8197
+ const selectRef = React.useRef(null);
8198
+ // Update internal value when value prop changes
8199
+ React.useEffect(() => {
8200
+ setInternalValue(value);
8201
+ }, [value]);
8202
+ // Expose selectRef to parent via forwardRef
8203
+ React.useImperativeHandle(ref, () => selectRef.current);
8204
+ // Handle option click
8205
+ const handleOptionClick = (optionValue) => {
8206
+ setInternalValue(optionValue);
8207
+ // Trigger onChange
8208
+ onChange?.({
8209
+ target: { name, value: optionValue, type: 'select-one' },
8210
+ });
8211
+ if (selectRef.current) {
8212
+ selectRef.current.value = optionValue.toString();
8213
+ selectRef.current.dispatchEvent(new Event('change', { bubbles: true }));
8214
+ }
8210
8215
  setIsOpen(false);
8211
- setTimeout(() => {
8212
- onBlur?.(createSyntheticEvent(selectedOption.value));
8213
- }, 0);
8214
- }, [options, onChange, onBlur, createSyntheticEvent]);
8215
- const handleOutsideClick = React.useCallback(() => {
8216
+ };
8217
+ useOutsideClick([dropdownRef], () => {
8216
8218
  setIsOpen(false);
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);
8219
+ selectRef.current?.blur();
8220
+ });
8224
8221
  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';
8228
- React.useEffect(() => {
8229
- if (hiddenSelectRef.current)
8230
- hiddenSelectRef.current.value = normalizedValue;
8231
- }, [normalizedValue]);
8232
8222
  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) {
8223
+ return () => {
8251
8224
  setIsOpen(false);
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]);
8225
+ };
8226
+ }, []);
8227
+ const displayText = internalValue
8228
+ ? options.find((option) => option.value === internalValue)?.label
8229
+ : selectLabel || 'Select an option';
8275
8230
  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: {
8231
+ React.createElement("select", { ref: selectRef, name: name, value: internalValue || '', onChange: onChange, onBlur: onBlur, required: required, disabled: disabled, style: {
8283
8232
  position: 'absolute',
8284
8233
  left: '-9999px',
8285
8234
  opacity: 0,
@@ -8294,15 +8243,15 @@ const Select = React.forwardRef(({ name, label, message, selectLabel, size = 'md
8294
8243
  " ",
8295
8244
  required && React.createElement("span", { className: "text-danger" }, "*"))),
8296
8245
  React.createElement("div", { className: `dropdown-select-container ${size}`, ref: dropdownRef },
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" },
8246
+ React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`, onClick: () => !disabled && setIsOpen(!isOpen), "aria-haspopup": "listbox", "aria-expanded": isOpen },
8298
8247
  React.createElement("span", { className: "label" }, displayText),
8299
8248
  React.createElement(FiChevronDown, { className: "arrow" })),
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)))))),
8249
+ isOpen && (React.createElement("div", { className: `dropdown-options scrollbar ${disabled ? 'disabled' : ''}`, ref: dropdownOptionsRef, role: "listbox" }, options.map((option, index) => (React.createElement("div", { key: `${option.value}-${index}`, className: `option ${internalValue === option.value ? 'selected' : ''}`, onClick: () => !disabled && handleOptionClick(option.value), role: "option", "aria-selected": internalValue === option.value }, option.label)))))),
8301
8250
  label && floatingLabel && (React.createElement("label", { htmlFor: name },
8302
8251
  label,
8303
8252
  " ",
8304
8253
  required && React.createElement("span", { className: "text-danger" }, "*"))),
8305
- error ? (React.createElement("small", { className: "form-message text-danger", role: "alert" }, error)) : (message && React.createElement("small", { className: "form-message text-muted" }, message))));
8254
+ error ? (React.createElement("small", { className: "form-message text-danger" }, error)) : (message && React.createElement("small", { className: "form-message text-muted" }, message))));
8306
8255
  });
8307
8256
  Select.displayName = 'Select';
8308
8257
 
@@ -8650,12 +8599,15 @@ const RadioButtonGroup = React.forwardRef(({ name, label, message, size = 'md',
8650
8599
  });
8651
8600
  RadioButtonGroup.displayName = 'RadioButtonGroup';
8652
8601
 
8653
- const SelectMultiple = React.forwardRef(({ name, label, message, selectLabel, floatingLabel = false, error, options, value = [], onChange, required, disabled, className, }, ref) => {
8602
+ const SelectMultiple = React.forwardRef(({ name, label, message, selectLabel, floatingLabel = false, error, options, value = [], onChange, onBlur, required, disabled, className, }, ref) => {
8654
8603
  const [isOpen, setIsOpen] = React.useState(false);
8655
8604
  const [selectedValues, setSelectedValues] = React.useState(value);
8656
8605
  const dropdownRef = React.useRef(null);
8657
8606
  const dropdownOptionsRef = React.useRef(null);
8658
- // Update selectedValues hanya jika value dari props berubah
8607
+ const inputRef = React.useRef(null);
8608
+ // expose to RHF
8609
+ React.useImperativeHandle(ref, () => inputRef.current);
8610
+ // update when external value changes
8659
8611
  React.useEffect(() => {
8660
8612
  if (JSON.stringify(value) !== JSON.stringify(selectedValues)) {
8661
8613
  setSelectedValues(value);
@@ -8666,14 +8618,16 @@ const SelectMultiple = React.forwardRef(({ name, label, message, selectLabel, fl
8666
8618
  ? selectedValues.filter((val) => val !== optionValue)
8667
8619
  : [...selectedValues, optionValue];
8668
8620
  setSelectedValues(newSelectedValues);
8669
- if (onChange) {
8670
- onChange({
8671
- target: { name, value: newSelectedValues },
8672
- });
8673
- }
8621
+ onChange?.({
8622
+ target: {
8623
+ name,
8624
+ value: newSelectedValues,
8625
+ },
8626
+ });
8674
8627
  };
8675
8628
  useOutsideClick([dropdownRef], () => {
8676
8629
  setIsOpen(false);
8630
+ inputRef.current?.blur();
8677
8631
  });
8678
8632
  useDropdownPositionAndScroll(isOpen, dropdownOptionsRef);
8679
8633
  const selectedLabels = selectedValues
@@ -8681,12 +8635,13 @@ const SelectMultiple = React.forwardRef(({ name, label, message, selectLabel, fl
8681
8635
  .filter(Boolean)
8682
8636
  .join(', ');
8683
8637
  return (React.createElement(React.Fragment, null,
8638
+ React.createElement("input", { type: "hidden", name: name, value: selectedValues.join(','), ref: inputRef, onBlur: onBlur, required: required }),
8684
8639
  label && !floatingLabel && (React.createElement("label", { htmlFor: name },
8685
8640
  label,
8686
8641
  " ",
8687
8642
  required && React.createElement("span", { className: "text-danger" }, "*"))),
8688
8643
  React.createElement("div", { className: `dropdown-select-container ${className || ''}`, ref: dropdownRef },
8689
- React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`, onClick: () => !disabled && setIsOpen(!isOpen), ref: ref },
8644
+ React.createElement("div", { className: `dropdown-header ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`, onClick: () => !disabled && setIsOpen(!isOpen) },
8690
8645
  React.createElement("span", { className: "label" }, selectedLabels || selectLabel || 'Select an option'),
8691
8646
  React.createElement(FiChevronDown, { className: `arrow ${isOpen ? 'open' : ''}` })),
8692
8647
  isOpen && !disabled && (React.createElement("div", { className: "dropdown-options scrollbar", ref: dropdownOptionsRef, role: "listbox" }, options?.map((option) => (React.createElement("div", { key: option.value, className: `option ${selectedValues.includes(option.value) ? 'selected' : ''}` },