Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import {MultiSelect} from '../primereact/index.js'; import type {IDropdownOption, IWidgetProps} from '@feasibleone/blong'; import {useEffect, useState} from 'react'; import {useBlong} from '../context/BlongContext.js'; import {dropdownRegistry} from '../model/dropdownRegistry.js'; type SelectOption = IDropdownOption; function toOptions(data: unknown): SelectOption[] { if (!Array.isArray(data)) return []; return data.map((item: Record<string, unknown>) => ({ label: String(item.label ?? item.name ?? item.value ?? item), value: item.value ?? item, })); } export function MultiSelectWidget({ id, name, schema, value, onChange, onBlur, error, readOnly, disabled, }: IWidgetProps) { const { fetch: fetchAction, options: staticOptions, dropdown: dropdownKey, type: widgetType, } = schema.widget ?? {}; const {handler} = useBlong(); const [options, setOptions] = useState<SelectOption[]>(() => staticOptions ? toOptions(staticOptions) : [], ); useEffect(() => { let cancelled = false; if (staticOptions) return; // Priority 1: named dropdown via portal orchestrator (batched + cached) if (dropdownKey) { const loader = (key: string) => ( handler.portalDropdownList({names: [key]}, {}) as Promise< Record<string, unknown> > ).then(result => toOptions(result[key])); dropdownRegistry .get(dropdownKey, loader) .then(data => { if (!cancelled) setOptions(data); }) .catch(() => { // Error surfaced by the central dispatch wrapper in BlongProvider. }); return () => { cancelled = true; }; } // Priority 2: explicit fetch action if (!fetchAction) return; (handler[fetchAction]({}, {}) as Promise<unknown>) .then(data => { if (!cancelled) setOptions(toOptions(data)); }) .catch(() => {}); return () => { cancelled = true; }; }, [fetchAction, dropdownKey, handler, staticOptions]); const arrValue: unknown[] = Array.isArray(value) ? value : value != null ? [value] : []; // multiSelectPanel: render as inline checkbox panel // (inline + flex + itemClassName='col-3' → 4-column grid of checkboxes) const isPanel = widgetType === 'multiSelectPanel'; if (readOnly && !isPanel) { // Regular multi-select in read-only: show comma-separated labels const labels = arrValue.map(v => options.find(o => o.value === v)?.label ?? String(v)); return <span className="blong-display">{labels.join(', ')}</span>; } return ( <MultiSelect inputId={id ?? name} data-testid={id ?? name} value={arrValue} options={options} onChange={readOnly ? undefined : e => onChange(e.value)} onHide={isPanel || readOnly ? undefined : onBlur} disabled={disabled || readOnly} className={`blong-multiselect w-full ${error ? 'p-invalid' : ''}`} display="chip" filter={!isPanel && options.length > 8} placeholder={schema.placeholder ?? 'Select…'} {...(isPanel ? { inline: true, flex: true, itemClassName: 'col-3', } : {})} /> ); } |