fix(combobox): dispatch change event on multiple combo changes
This commit is contained in:
parent
0b6fefa582
commit
d640ab8428
4 changed files with 56 additions and 18 deletions
|
@ -147,6 +147,7 @@ export function MultiComboBox(maybeProps: MultiComboBoxProps) {
|
|||
formValue,
|
||||
allowsCustomValue,
|
||||
valueSeparator,
|
||||
className,
|
||||
...props
|
||||
} = useMemo(() => s.create(maybeProps, MultiComboBoxProps), [maybeProps]);
|
||||
|
||||
|
@ -174,7 +175,7 @@ export function MultiComboBox(maybeProps: MultiComboBoxProps) {
|
|||
const formResetRef = useOnFormReset(onReset);
|
||||
|
||||
return (
|
||||
<div className="fr-ds-combobox__multiple">
|
||||
<div className={`fr-ds-combobox__multiple ${className}`}>
|
||||
{selectedItems.length > 0 ? (
|
||||
<TagGroup onRemove={onRemove} aria-label={props['aria-label']}>
|
||||
<TagList items={selectedItems} className="fr-tag-list">
|
||||
|
@ -203,16 +204,26 @@ export function MultiComboBox(maybeProps: MultiComboBoxProps) {
|
|||
</ComboBox>
|
||||
{name ? (
|
||||
<span ref={ref}>
|
||||
{hiddenInputValues.map((value, i) => (
|
||||
{hiddenInputValues.length == 0 ? (
|
||||
<input
|
||||
type="hidden"
|
||||
value={value}
|
||||
value=""
|
||||
name={name}
|
||||
form={form}
|
||||
ref={i == 0 ? formResetRef : undefined}
|
||||
key={value}
|
||||
ref={formResetRef}
|
||||
/>
|
||||
))}
|
||||
) : (
|
||||
hiddenInputValues.map((value, i) => (
|
||||
<input
|
||||
type="hidden"
|
||||
value={value}
|
||||
name={name}
|
||||
form={form}
|
||||
ref={i == 0 ? formResetRef : undefined}
|
||||
key={value}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
@ -23,6 +23,7 @@ export interface ComboBoxProps
|
|||
}
|
||||
|
||||
const inputMap = new WeakMap<HTMLInputElement, string>();
|
||||
const inputCountMap = new WeakMap<HTMLSpanElement, number>();
|
||||
export function useDispatchChangeEvent() {
|
||||
const ref = useRef<HTMLSpanElement>(null);
|
||||
|
||||
|
@ -30,12 +31,15 @@ export function useDispatchChangeEvent() {
|
|||
ref,
|
||||
dispatch: () => {
|
||||
requestAnimationFrame(() => {
|
||||
const input = ref.current?.querySelector('input');
|
||||
if (input) {
|
||||
const value = input.value;
|
||||
const prevValue = inputMap.get(input) || '';
|
||||
if (value != prevValue) {
|
||||
inputMap.set(input, value);
|
||||
if (ref.current) {
|
||||
const container = ref.current;
|
||||
const inputs = Array.from(container.querySelectorAll('input'));
|
||||
const input = inputs.at(0);
|
||||
if (input && inputChanged(container, inputs)) {
|
||||
inputCountMap.set(container, inputs.length);
|
||||
for (const input of inputs) {
|
||||
inputMap.set(input, input.value.trim());
|
||||
}
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +48,23 @@ export function useDispatchChangeEvent() {
|
|||
};
|
||||
}
|
||||
|
||||
// I am not proude of this code. We have to tack values and number of values to deal with multi select combobox.
|
||||
// I have a plan to remove this code. Soon.
|
||||
function inputChanged(container: HTMLSpanElement, inputs: HTMLInputElement[]) {
|
||||
const prevCount = inputCountMap.get(container) ?? 0;
|
||||
if (prevCount != inputs.length) {
|
||||
return true;
|
||||
}
|
||||
for (const input of inputs) {
|
||||
const value = input.value.trim();
|
||||
const prevValue = inputMap.get(input);
|
||||
if (prevValue == null || prevValue != value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function useSingleList({
|
||||
defaultItems,
|
||||
defaultSelectedKey,
|
||||
|
@ -174,9 +195,13 @@ export function useMultiList({
|
|||
const filteredItems = useMemo(
|
||||
() =>
|
||||
inputValue.length == 0
|
||||
? items
|
||||
: matchSorter(items, inputValue, { keys: ['label'] }),
|
||||
[items, inputValue]
|
||||
? items.filter((item) => !selectedKeys.has(item.value))
|
||||
: matchSorter(
|
||||
items.filter((item) => !selectedKeys.has(item.value)),
|
||||
inputValue,
|
||||
{ keys: ['label'] }
|
||||
),
|
||||
[items, inputValue, selectedKeys]
|
||||
);
|
||||
const selectedItems = useMemo(() => {
|
||||
const selectedItems: Item[] = [];
|
||||
|
|
|
@ -73,7 +73,7 @@ class Champs::MultipleDropDownListChamp < Champ
|
|||
end
|
||||
|
||||
def value=(value)
|
||||
return super(nil) if value.nil?
|
||||
return super(nil) if value.blank?
|
||||
|
||||
values = if value.is_a?(Array)
|
||||
value
|
||||
|
|
|
@ -41,8 +41,6 @@ describe Champs::MultipleDropDownListChamp do
|
|||
expect(champ.value).to eq("[\"val1\"]")
|
||||
champ.value = 'val2'
|
||||
expect(champ.value).to eq("[\"val1\",\"val2\"]")
|
||||
champ.value = ''
|
||||
expect(champ.value).to eq("[\"val1\",\"val2\"]")
|
||||
champ.value = "[brackets] val4"
|
||||
expect(champ.value).to eq("[\"val1\",\"val2\",\"[brackets] val4\"]")
|
||||
champ.value = nil
|
||||
|
@ -51,6 +49,10 @@ describe Champs::MultipleDropDownListChamp do
|
|||
expect(champ.value).to eq("[\"val1\"]")
|
||||
champ.value = []
|
||||
expect(champ.value).to be_nil
|
||||
champ.value = ["val1"]
|
||||
expect(champ.value).to eq("[\"val1\"]")
|
||||
champ.value = ''
|
||||
expect(champ.value).to be_nil
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue