@proyecto-viviana/solid-stately 0.5.1 → 0.6.0-rc.1
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/LICENSE-APACHE-2.0 +201 -0
- package/NOTICE +91 -0
- package/README.md +59 -29
- package/dist/autocomplete/createAutocompleteState.d.ts +2 -1
- package/dist/autocomplete/createAutocompleteState.d.ts.map +1 -0
- package/dist/autocomplete/index.d.ts +1 -0
- package/dist/autocomplete/index.d.ts.map +1 -0
- package/dist/calendar/IncompleteDate.d.ts +2 -1
- package/dist/calendar/IncompleteDate.d.ts.map +1 -0
- package/dist/calendar/createCalendarState.d.ts +10 -9
- package/dist/calendar/createCalendarState.d.ts.map +1 -0
- package/dist/calendar/createDateFieldState.d.ts +4 -20
- package/dist/calendar/createDateFieldState.d.ts.map +1 -0
- package/dist/calendar/createDatePickerState.d.ts +4 -7
- package/dist/calendar/createDatePickerState.d.ts.map +1 -0
- package/dist/calendar/createRangeCalendarState.d.ts +9 -7
- package/dist/calendar/createRangeCalendarState.d.ts.map +1 -0
- package/dist/calendar/createTimeFieldState.d.ts +5 -24
- package/dist/calendar/createTimeFieldState.d.ts.map +1 -0
- package/dist/calendar/index.d.ts +1 -0
- package/dist/calendar/index.d.ts.map +1 -0
- package/dist/calendar/intl/index.d.ts +1 -0
- package/dist/calendar/intl/index.d.ts.map +1 -0
- package/dist/checkbox/createCheckboxGroupState.d.ts +3 -0
- package/dist/checkbox/createCheckboxGroupState.d.ts.map +1 -0
- package/dist/checkbox/index.d.ts +1 -0
- package/dist/checkbox/index.d.ts.map +1 -0
- package/dist/collections/ListCollection.d.ts +5 -0
- package/dist/collections/ListCollection.d.ts.map +1 -0
- package/dist/collections/createListState.d.ts +2 -6
- package/dist/collections/createListState.d.ts.map +1 -0
- package/dist/collections/createMenuState.d.ts +74 -13
- package/dist/collections/createMenuState.d.ts.map +1 -0
- package/dist/collections/createSelectionState.d.ts +2 -5
- package/dist/collections/createSelectionState.d.ts.map +1 -0
- package/dist/collections/getChildNodes.d.ts +6 -3
- package/dist/collections/getChildNodes.d.ts.map +1 -0
- package/dist/collections/index.d.ts +2 -1
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/collections/types.d.ts +36 -2
- package/dist/collections/types.d.ts.map +1 -0
- package/dist/color/Color.d.ts +7 -1
- package/dist/color/Color.d.ts.map +1 -0
- package/dist/color/createColorAreaState.d.ts +6 -5
- package/dist/color/createColorAreaState.d.ts.map +1 -0
- package/dist/color/createColorFieldState.d.ts +4 -5
- package/dist/color/createColorFieldState.d.ts.map +1 -0
- package/dist/color/createColorSliderState.d.ts +4 -5
- package/dist/color/createColorSliderState.d.ts.map +1 -0
- package/dist/color/createColorWheelState.d.ts +2 -5
- package/dist/color/createColorWheelState.d.ts.map +1 -0
- package/dist/color/getColorChannels.d.ts +3 -0
- package/dist/color/getColorChannels.d.ts.map +1 -0
- package/dist/color/index.d.ts +2 -1
- package/dist/color/index.d.ts.map +1 -0
- package/dist/color/intl/index.d.ts +1 -0
- package/dist/color/intl/index.d.ts.map +1 -0
- package/dist/color/types.d.ts +2 -1
- package/dist/color/types.d.ts.map +1 -0
- package/dist/combobox/createComboBoxState.d.ts +92 -29
- package/dist/combobox/createComboBoxState.d.ts.map +1 -0
- package/dist/combobox/index.d.ts +2 -1
- package/dist/combobox/index.d.ts.map +1 -0
- package/dist/data/createAsyncList.d.ts +1 -7
- package/dist/data/createAsyncList.d.ts.map +1 -0
- package/dist/data/createListData.d.ts +1 -6
- package/dist/data/createListData.d.ts.map +1 -0
- package/dist/data/createTreeData.d.ts +1 -6
- package/dist/data/createTreeData.d.ts.map +1 -0
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/datepicker/index.d.ts +1 -0
- package/dist/datepicker/index.d.ts.map +1 -0
- package/dist/disclosure/createDisclosureState.d.ts +2 -5
- package/dist/disclosure/createDisclosureState.d.ts.map +1 -0
- package/dist/disclosure/index.d.ts +1 -0
- package/dist/disclosure/index.d.ts.map +1 -0
- package/dist/dnd/createDragState.d.ts +2 -6
- package/dist/dnd/createDragState.d.ts.map +1 -0
- package/dist/dnd/createDraggableCollectionState.d.ts +44 -11
- package/dist/dnd/createDraggableCollectionState.d.ts.map +1 -0
- package/dist/dnd/createDropState.d.ts +2 -6
- package/dist/dnd/createDropState.d.ts.map +1 -0
- package/dist/dnd/createDroppableCollectionState.d.ts +34 -11
- package/dist/dnd/createDroppableCollectionState.d.ts.map +1 -0
- package/dist/dnd/index.d.ts +3 -2
- package/dist/dnd/index.d.ts.map +1 -0
- package/dist/dnd/types.d.ts +17 -0
- package/dist/dnd/types.d.ts.map +1 -0
- package/dist/form/createFormValidationState.d.ts +2 -9
- package/{src → dist}/form/createFormValidationState.d.ts.map +1 -1
- package/dist/form/index.d.ts +1 -0
- package/dist/form/index.d.ts.map +1 -0
- package/dist/grid/createGridState.d.ts +4 -3
- package/dist/grid/createGridState.d.ts.map +1 -0
- package/dist/grid/index.d.ts +1 -0
- package/dist/grid/index.d.ts.map +1 -0
- package/dist/grid/types.d.ts +23 -1
- package/dist/grid/types.d.ts.map +1 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2947 -969
- package/dist/index.js.map +1 -1
- package/dist/menu/index.d.ts +3 -1
- package/dist/menu/index.d.ts.map +1 -0
- package/dist/numberfield/createNumberFieldState.d.ts +37 -9
- package/dist/numberfield/createNumberFieldState.d.ts.map +1 -0
- package/dist/numberfield/index.d.ts +1 -0
- package/dist/numberfield/index.d.ts.map +1 -0
- package/dist/overlays/createOverlayTriggerState.d.ts +12 -1
- package/dist/overlays/createOverlayTriggerState.d.ts.map +1 -0
- package/dist/overlays/index.d.ts +1 -0
- package/dist/overlays/index.d.ts.map +1 -0
- package/dist/private/flags/flags.d.ts +5 -0
- package/dist/private/flags/flags.d.ts.map +1 -0
- package/dist/private/flags/flags.js +26 -0
- package/dist/private/flags/flags.js.map +1 -0
- package/dist/radio/createRadioGroupState.d.ts +1 -0
- package/dist/radio/createRadioGroupState.d.ts.map +1 -0
- package/dist/radio/index.d.ts +1 -0
- package/dist/radio/index.d.ts.map +1 -0
- package/dist/searchfield/createSearchFieldState.d.ts +2 -5
- package/dist/searchfield/createSearchFieldState.d.ts.map +1 -0
- package/dist/searchfield/index.d.ts +1 -0
- package/dist/searchfield/index.d.ts.map +1 -0
- package/dist/select/createSelectState.d.ts +100 -33
- package/dist/select/createSelectState.d.ts.map +1 -0
- package/dist/select/index.d.ts +2 -1
- package/dist/select/index.d.ts.map +1 -0
- package/dist/selection/Selection.d.ts +2 -1
- package/dist/selection/Selection.d.ts.map +1 -0
- package/dist/selection/SelectionManager.d.ts +1 -19
- package/dist/selection/SelectionManager.d.ts.map +1 -0
- package/dist/selection/createMultipleSelectionState.d.ts +2 -13
- package/dist/selection/createMultipleSelectionState.d.ts.map +1 -0
- package/dist/selection/index.d.ts +1 -0
- package/dist/selection/index.d.ts.map +1 -0
- package/dist/slider/createSliderState.d.ts +37 -14
- package/dist/slider/createSliderState.d.ts.map +1 -0
- package/dist/slider/index.d.ts +1 -0
- package/dist/slider/index.d.ts.map +1 -0
- package/dist/ssr/index.d.ts +2 -13
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/steplist/createStepListState.d.ts +10 -18
- package/dist/steplist/createStepListState.d.ts.map +1 -0
- package/dist/steplist/index.d.ts +1 -0
- package/dist/steplist/index.d.ts.map +1 -0
- package/dist/table/TableCollection.d.ts +1 -0
- package/dist/table/TableCollection.d.ts.map +1 -0
- package/dist/table/createTableColumnResizeState.d.ts +2 -9
- package/dist/table/createTableColumnResizeState.d.ts.map +1 -0
- package/dist/table/createTableState.d.ts +3 -6
- package/dist/table/createTableState.d.ts.map +1 -0
- package/dist/table/createTreeGridState.d.ts +3 -9
- package/dist/table/createTreeGridState.d.ts.map +1 -0
- package/dist/table/index.d.ts +1 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/table/types.d.ts +37 -3
- package/dist/table/types.d.ts.map +1 -0
- package/dist/tabs/createTabListState.d.ts +2 -5
- package/dist/tabs/createTabListState.d.ts.map +1 -0
- package/dist/tabs/index.d.ts +1 -0
- package/dist/tabs/index.d.ts.map +1 -0
- package/dist/textfield/createTextFieldState.d.ts +1 -0
- package/dist/textfield/createTextFieldState.d.ts.map +1 -0
- package/dist/textfield/index.d.ts +1 -0
- package/dist/textfield/index.d.ts.map +1 -0
- package/dist/toast/createToastState.d.ts +2 -5
- package/dist/toast/createToastState.d.ts.map +1 -0
- package/dist/toast/index.d.ts +1 -0
- package/dist/toast/index.d.ts.map +1 -0
- package/dist/toggle/createToggleGroupState.d.ts +1 -0
- package/dist/toggle/createToggleGroupState.d.ts.map +1 -0
- package/dist/toggle/createToggleState.d.ts +1 -0
- package/dist/toggle/createToggleState.d.ts.map +1 -0
- package/dist/toggle/index.d.ts +1 -0
- package/dist/toggle/index.d.ts.map +1 -0
- package/dist/tokenfield/TokenFieldValue.d.ts +96 -0
- package/dist/tokenfield/TokenFieldValue.d.ts.map +1 -0
- package/dist/tokenfield/createTokenFieldState.d.ts +33 -0
- package/dist/tokenfield/createTokenFieldState.d.ts.map +1 -0
- package/dist/tokenfield/index.d.ts +3 -0
- package/dist/tokenfield/index.d.ts.map +1 -0
- package/dist/tooltip/createTooltipTriggerState.d.ts +9 -8
- package/dist/tooltip/createTooltipTriggerState.d.ts.map +1 -0
- package/dist/tooltip/index.d.ts +1 -0
- package/dist/tooltip/index.d.ts.map +1 -0
- package/dist/tree/TreeCollection.d.ts +1 -0
- package/dist/tree/TreeCollection.d.ts.map +1 -0
- package/dist/tree/createTreeState.d.ts +3 -8
- package/dist/tree/createTreeState.d.ts.map +1 -0
- package/dist/tree/index.d.ts +1 -0
- package/dist/tree/index.d.ts.map +1 -0
- package/dist/tree/types.d.ts +19 -1
- package/dist/tree/types.d.ts.map +1 -0
- package/dist/utils/index.d.ts +3 -1
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/owner.d.ts +9 -0
- package/dist/utils/owner.d.ts.map +1 -0
- package/dist/utils/reactivity.d.ts +24 -0
- package/dist/utils/reactivity.d.ts.map +1 -0
- package/package.json +21 -9
- package/src/autocomplete/createAutocompleteState.ts +18 -4
- package/src/calendar/IncompleteDate.ts +15 -1
- package/src/calendar/createCalendarState.ts +321 -125
- package/src/calendar/createDateFieldState.ts +45 -31
- package/src/calendar/createDatePickerState.ts +47 -26
- package/src/calendar/createRangeCalendarState.ts +173 -86
- package/src/calendar/createTimeFieldState.ts +26 -7
- package/src/calendar/intl/index.ts +37 -2
- package/src/checkbox/createCheckboxGroupState.ts +45 -13
- package/src/collections/ListCollection.ts +31 -0
- package/src/collections/createListState.ts +27 -10
- package/src/collections/createMenuState.ts +190 -15
- package/src/collections/createSelectionState.ts +33 -13
- package/src/collections/getChildNodes.ts +20 -3
- package/src/collections/index.ts +6 -0
- package/src/collections/types.ts +35 -2
- package/src/color/Color.ts +61 -22
- package/src/color/createColorAreaState.ts +48 -4
- package/src/color/createColorFieldState.ts +114 -38
- package/src/color/createColorSliderState.ts +61 -9
- package/src/color/createColorWheelState.ts +29 -9
- package/src/color/getColorChannels.ts +16 -0
- package/src/color/index.ts +1 -0
- package/src/color/intl/index.ts +38 -3
- package/src/color/types.ts +1 -1
- package/src/combobox/createComboBoxState.ts +530 -297
- package/src/combobox/index.ts +4 -0
- package/src/data/createAsyncList.ts +41 -6
- package/src/data/createListData.ts +27 -4
- package/src/data/createTreeData.ts +30 -6
- package/src/disclosure/createDisclosureState.ts +40 -19
- package/src/dnd/createDragState.ts +22 -4
- package/src/dnd/createDraggableCollectionState.ts +145 -11
- package/src/dnd/createDropState.ts +21 -3
- package/src/dnd/createDroppableCollectionState.ts +127 -11
- package/src/dnd/index.ts +4 -0
- package/src/dnd/types.ts +16 -0
- package/src/form/createFormValidationState.ts +69 -37
- package/src/grid/createGridState.ts +196 -111
- package/src/grid/types.ts +22 -1
- package/src/index.ts +33 -2
- package/src/menu/index.ts +7 -0
- package/src/numberfield/createNumberFieldState.ts +247 -98
- package/src/overlays/createOverlayTriggerState.ts +31 -4
- package/src/private/flags/flags.ts +25 -0
- package/src/radio/createRadioGroupState.ts +23 -6
- package/src/searchfield/createSearchFieldState.ts +18 -3
- package/src/select/createSelectState.ts +317 -130
- package/src/select/index.ts +8 -1
- package/src/selection/Selection.ts +15 -1
- package/src/selection/SelectionManager.ts +17 -1
- package/src/selection/createMultipleSelectionState.ts +45 -26
- package/src/slider/createSliderState.ts +243 -104
- package/src/ssr/index.ts +34 -4
- package/src/steplist/createStepListState.ts +184 -130
- package/src/table/TableCollection.ts +14 -0
- package/src/table/createTableColumnResizeState.ts +71 -14
- package/src/table/createTableState.ts +26 -4
- package/src/table/createTreeGridState.ts +22 -6
- package/src/table/types.ts +38 -5
- package/src/tabs/createTabListState.ts +63 -43
- package/src/textfield/createTextFieldState.ts +17 -3
- package/src/toast/createToastState.ts +28 -3
- package/src/toggle/createToggleGroupState.ts +20 -5
- package/src/toggle/createToggleState.ts +19 -4
- package/src/tokenfield/TokenFieldValue.ts +499 -0
- package/src/tokenfield/createTokenFieldState.ts +88 -0
- package/src/tokenfield/index.ts +15 -0
- package/src/tooltip/createTooltipTriggerState.ts +49 -13
- package/src/tree/TreeCollection.ts +14 -0
- package/src/tree/createTreeState.ts +70 -36
- package/src/tree/types.ts +18 -1
- package/src/utils/index.ts +10 -1
- package/src/utils/owner.ts +20 -0
- package/src/utils/reactivity.ts +53 -1
- package/src/autocomplete/createAutocompleteState.d.ts +0 -48
- package/src/autocomplete/createAutocompleteState.d.ts.map +0 -1
- package/src/autocomplete/index.d.ts +0 -6
- package/src/autocomplete/index.d.ts.map +0 -1
- package/src/calendar/createCalendarState.d.ts +0 -152
- package/src/calendar/createCalendarState.d.ts.map +0 -1
- package/src/calendar/createDateFieldState.d.ts +0 -145
- package/src/calendar/createDateFieldState.d.ts.map +0 -1
- package/src/calendar/createRangeCalendarState.d.ts +0 -171
- package/src/calendar/createRangeCalendarState.d.ts.map +0 -1
- package/src/calendar/createTimeFieldState.d.ts +0 -97
- package/src/calendar/createTimeFieldState.d.ts.map +0 -1
- package/src/calendar/index.d.ts +0 -80
- package/src/calendar/index.d.ts.map +0 -1
- package/src/checkbox/createCheckboxGroupState.d.ts +0 -73
- package/src/checkbox/createCheckboxGroupState.d.ts.map +0 -1
- package/src/checkbox/index.d.ts +0 -6
- package/src/checkbox/index.d.ts.map +0 -1
- package/src/collections/ListCollection.d.ts +0 -40
- package/src/collections/ListCollection.d.ts.map +0 -1
- package/src/collections/createListState.d.ts +0 -94
- package/src/collections/createListState.d.ts.map +0 -1
- package/src/collections/createMenuState.d.ts +0 -55
- package/src/collections/createMenuState.d.ts.map +0 -1
- package/src/collections/createSelectionState.d.ts +0 -89
- package/src/collections/createSelectionState.d.ts.map +0 -1
- package/src/collections/index.d.ts +0 -38
- package/src/collections/index.d.ts.map +0 -1
- package/src/collections/types.d.ts +0 -147
- package/src/collections/types.d.ts.map +0 -1
- package/src/color/Color.d.ts +0 -43
- package/src/color/Color.d.ts.map +0 -1
- package/src/color/createColorAreaState.d.ts +0 -78
- package/src/color/createColorAreaState.d.ts.map +0 -1
- package/src/color/createColorFieldState.d.ts +0 -57
- package/src/color/createColorFieldState.d.ts.map +0 -1
- package/src/color/createColorSliderState.d.ts +0 -69
- package/src/color/createColorSliderState.d.ts.map +0 -1
- package/src/color/createColorWheelState.d.ts +0 -53
- package/src/color/createColorWheelState.d.ts.map +0 -1
- package/src/color/index.d.ts +0 -39
- package/src/color/index.d.ts.map +0 -1
- package/src/color/types.d.ts +0 -111
- package/src/color/types.d.ts.map +0 -1
- package/src/combobox/createComboBoxState.d.ts +0 -127
- package/src/combobox/createComboBoxState.d.ts.map +0 -1
- package/src/combobox/index.d.ts +0 -13
- package/src/combobox/index.d.ts.map +0 -1
- package/src/disclosure/createDisclosureState.d.ts +0 -68
- package/src/disclosure/createDisclosureState.d.ts.map +0 -1
- package/src/disclosure/index.d.ts +0 -10
- package/src/disclosure/index.d.ts.map +0 -1
- package/src/dnd/createDragState.d.ts +0 -68
- package/src/dnd/createDragState.d.ts.map +0 -1
- package/src/dnd/createDraggableCollectionState.d.ts +0 -68
- package/src/dnd/createDraggableCollectionState.d.ts.map +0 -1
- package/src/dnd/createDropState.d.ts +0 -80
- package/src/dnd/createDropState.d.ts.map +0 -1
- package/src/dnd/createDroppableCollectionState.d.ts +0 -107
- package/src/dnd/createDroppableCollectionState.d.ts.map +0 -1
- package/src/dnd/index.d.ts +0 -60
- package/src/dnd/index.d.ts.map +0 -1
- package/src/dnd/types.d.ts +0 -277
- package/src/dnd/types.d.ts.map +0 -1
- package/src/form/createFormValidationState.d.ts +0 -102
- package/src/form/index.d.ts +0 -16
- package/src/form/index.d.ts.map +0 -1
- package/src/grid/createGridState.d.ts +0 -15
- package/src/grid/createGridState.d.ts.map +0 -1
- package/src/grid/index.d.ts +0 -7
- package/src/grid/index.d.ts.map +0 -1
- package/src/grid/types.d.ts +0 -163
- package/src/grid/types.d.ts.map +0 -1
- package/src/index.d.ts +0 -311
- package/src/index.d.ts.map +0 -1
- package/src/numberfield/createNumberFieldState.d.ts +0 -67
- package/src/numberfield/createNumberFieldState.d.ts.map +0 -1
- package/src/numberfield/index.d.ts +0 -6
- package/src/numberfield/index.d.ts.map +0 -1
- package/src/overlays/createOverlayTriggerState.d.ts +0 -34
- package/src/overlays/createOverlayTriggerState.d.ts.map +0 -1
- package/src/overlays/index.d.ts +0 -6
- package/src/overlays/index.d.ts.map +0 -1
- package/src/radio/createRadioGroupState.d.ts +0 -79
- package/src/radio/createRadioGroupState.d.ts.map +0 -1
- package/src/radio/index.d.ts +0 -7
- package/src/radio/index.d.ts.map +0 -1
- package/src/searchfield/createSearchFieldState.d.ts +0 -27
- package/src/searchfield/createSearchFieldState.d.ts.map +0 -1
- package/src/searchfield/index.d.ts +0 -3
- package/src/searchfield/index.d.ts.map +0 -1
- package/src/select/createSelectState.d.ts +0 -75
- package/src/select/createSelectState.d.ts.map +0 -1
- package/src/select/index.d.ts +0 -2
- package/src/select/index.d.ts.map +0 -1
- package/src/slider/createSliderState.d.ts +0 -72
- package/src/slider/createSliderState.d.ts.map +0 -1
- package/src/slider/index.d.ts +0 -3
- package/src/slider/index.d.ts.map +0 -1
- package/src/ssr/index.d.ts +0 -28
- package/src/ssr/index.d.ts.map +0 -1
- package/src/table/TableCollection.d.ts +0 -54
- package/src/table/TableCollection.d.ts.map +0 -1
- package/src/table/createTableState.d.ts +0 -15
- package/src/table/createTableState.d.ts.map +0 -1
- package/src/table/index.d.ts +0 -18
- package/src/table/index.d.ts.map +0 -1
- package/src/table/types.d.ts +0 -145
- package/src/table/types.d.ts.map +0 -1
- package/src/tabs/createTabListState.d.ts +0 -70
- package/src/tabs/createTabListState.d.ts.map +0 -1
- package/src/tabs/index.d.ts +0 -8
- package/src/tabs/index.d.ts.map +0 -1
- package/src/textfield/createTextFieldState.d.ts +0 -32
- package/src/textfield/createTextFieldState.d.ts.map +0 -1
- package/src/textfield/index.d.ts +0 -6
- package/src/textfield/index.d.ts.map +0 -1
- package/src/toast/createToastState.d.ts +0 -129
- package/src/toast/createToastState.d.ts.map +0 -1
- package/src/toast/index.d.ts +0 -12
- package/src/toast/index.d.ts.map +0 -1
- package/src/toggle/createToggleState.d.ts +0 -34
- package/src/toggle/createToggleState.d.ts.map +0 -1
- package/src/toggle/index.d.ts +0 -2
- package/src/toggle/index.d.ts.map +0 -1
- package/src/tooltip/createTooltipTriggerState.d.ts +0 -39
- package/src/tooltip/createTooltipTriggerState.d.ts.map +0 -1
- package/src/tooltip/index.d.ts +0 -7
- package/src/tooltip/index.d.ts.map +0 -1
- package/src/tree/TreeCollection.d.ts +0 -43
- package/src/tree/TreeCollection.d.ts.map +0 -1
- package/src/tree/createTreeState.d.ts +0 -17
- package/src/tree/createTreeState.d.ts.map +0 -1
- package/src/tree/index.d.ts +0 -13
- package/src/tree/index.d.ts.map +0 -1
- package/src/tree/types.d.ts +0 -162
- package/src/tree/types.d.ts.map +0 -1
- package/src/utils/index.d.ts +0 -2
- package/src/utils/index.d.ts.map +0 -1
- package/src/utils/reactivity.d.ts +0 -28
- package/src/utils/reactivity.d.ts.map +0 -1
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Ported to SolidJS for Proyecto Viviana; based on packages/react-stately/src/combobox/useComboBoxState.ts
|
|
14
|
+
|
|
1
15
|
/**
|
|
2
16
|
* State management for ComboBox components.
|
|
3
17
|
* Based on @react-stately/combobox useComboBoxState.
|
|
@@ -6,12 +20,19 @@
|
|
|
6
20
|
* either type to filter options or select from a list.
|
|
7
21
|
*/
|
|
8
22
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
23
|
+
import { createMemo, createEffect, untrack } from "solid-js";
|
|
24
|
+
import type { Accessor } from "solid-js";
|
|
25
|
+
import { createInternalSignal, propsAccessor, type MaybeAccessor } from "../utils";
|
|
26
|
+
import { createListState, type ListState } from "../collections/createListState";
|
|
12
27
|
import { createOverlayTriggerState } from "../overlays";
|
|
28
|
+
import { ListCollection } from "../collections/ListCollection";
|
|
13
29
|
import type { Key, CollectionNode, Collection, FocusStrategy } from "../collections/types";
|
|
14
30
|
import type { SelectionManager } from "../selection/SelectionManager";
|
|
31
|
+
import {
|
|
32
|
+
createFormValidationState,
|
|
33
|
+
type FormValidationState,
|
|
34
|
+
type ValidationFunction,
|
|
35
|
+
} from "../form";
|
|
15
36
|
|
|
16
37
|
export type MenuTriggerAction = "focus" | "input" | "manual";
|
|
17
38
|
|
|
@@ -19,7 +40,37 @@ export type { FocusStrategy } from "../collections/types";
|
|
|
19
40
|
|
|
20
41
|
export type FilterFn = (textValue: string, inputValue: string) => boolean;
|
|
21
42
|
|
|
22
|
-
export
|
|
43
|
+
export type ComboBoxSelectionMode = "single" | "multiple";
|
|
44
|
+
|
|
45
|
+
/** Single mode is `Key | null`. Multiple mode is `readonly Key[]`. */
|
|
46
|
+
export type ComboBoxValueType<M extends ComboBoxSelectionMode> = M extends "single"
|
|
47
|
+
? Key | null
|
|
48
|
+
: readonly Key[];
|
|
49
|
+
|
|
50
|
+
/** `onChange` receives a mutable `Key[]` in multiple mode. */
|
|
51
|
+
export type ComboBoxChangeValueType<M extends ComboBoxSelectionMode> = M extends "single"
|
|
52
|
+
? Key | null
|
|
53
|
+
: Key[];
|
|
54
|
+
|
|
55
|
+
type ComboBoxValidationType<M extends ComboBoxSelectionMode> = M extends "single"
|
|
56
|
+
? Key | null
|
|
57
|
+
: Key[];
|
|
58
|
+
|
|
59
|
+
const EMPTY_COMBOBOX_KEYS: readonly Key[] = [];
|
|
60
|
+
|
|
61
|
+
export interface ComboBoxValidationValue<M extends ComboBoxSelectionMode = "single"> {
|
|
62
|
+
/**
|
|
63
|
+
* The selected key in the ComboBox.
|
|
64
|
+
* @deprecated Use `value`.
|
|
65
|
+
*/
|
|
66
|
+
selectedKey: Key | null;
|
|
67
|
+
/** The keys of the currently selected items. */
|
|
68
|
+
value: ComboBoxValidationType<M>;
|
|
69
|
+
/** The value of the ComboBox input. */
|
|
70
|
+
inputValue: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ComboBoxStateProps<T = unknown, M extends ComboBoxSelectionMode = "single"> {
|
|
23
74
|
/** The items to display in the combobox dropdown. */
|
|
24
75
|
items?: T[];
|
|
25
76
|
/** Default items when uncontrolled. */
|
|
@@ -32,20 +83,35 @@ export interface ComboBoxStateProps<T = unknown> {
|
|
|
32
83
|
getDisabled?: (item: T) => boolean;
|
|
33
84
|
/** Keys of disabled items. */
|
|
34
85
|
disabledKeys?: Iterable<Key>;
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Whether single or multiple selection is enabled.
|
|
88
|
+
* @default 'single'
|
|
89
|
+
*/
|
|
90
|
+
selectionMode?: M;
|
|
91
|
+
/**
|
|
92
|
+
* The current value (controlled).
|
|
93
|
+
* Single mode is `Key | null`. Multiple mode is `readonly Key[]`.
|
|
94
|
+
*/
|
|
95
|
+
value?: ComboBoxValueType<M>;
|
|
96
|
+
/** The default value (uncontrolled). */
|
|
97
|
+
defaultValue?: ComboBoxValueType<M>;
|
|
98
|
+
/** Handler called when the value changes. */
|
|
99
|
+
onChange?: (value: ComboBoxChangeValueType<M>) => void;
|
|
100
|
+
/**
|
|
101
|
+
* The currently selected key in the collection (controlled).
|
|
102
|
+
* @deprecated Use `value`.
|
|
103
|
+
*/
|
|
38
104
|
selectedKey?: Key | null;
|
|
39
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* The initial selected key in the collection (uncontrolled).
|
|
107
|
+
* @deprecated Use `defaultValue`.
|
|
108
|
+
*/
|
|
40
109
|
defaultSelectedKey?: Key | null;
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/** Handler called when the selection changes (single mode). */
|
|
110
|
+
/**
|
|
111
|
+
* Handler that is called when the selection changes.
|
|
112
|
+
* @deprecated Use `onChange`.
|
|
113
|
+
*/
|
|
46
114
|
onSelectionChange?: (key: Key | null) => void;
|
|
47
|
-
/** Handler called when the selection changes (multiple mode). */
|
|
48
|
-
onSelectionChangeMultiple?: (keys: Set<Key>) => void;
|
|
49
115
|
/** The current input value (controlled). */
|
|
50
116
|
inputValue?: string;
|
|
51
117
|
/** The default input value (uncontrolled). */
|
|
@@ -74,9 +140,25 @@ export interface ComboBoxStateProps<T = unknown> {
|
|
|
74
140
|
menuTrigger?: MenuTriggerAction;
|
|
75
141
|
/** Whether to close the menu on blur. */
|
|
76
142
|
shouldCloseOnBlur?: boolean;
|
|
143
|
+
/** Whether the combobox is invalid (controlled). */
|
|
144
|
+
isInvalid?: boolean;
|
|
145
|
+
/** @deprecated Use isInvalid instead. */
|
|
146
|
+
validationState?: "valid" | "invalid";
|
|
147
|
+
/** Custom validation function. */
|
|
148
|
+
validate?: ValidationFunction<ComboBoxValidationValue<M> | null>;
|
|
149
|
+
/**
|
|
150
|
+
* Whether to use native HTML form validation or ARIA validation semantics.
|
|
151
|
+
* @default "native"
|
|
152
|
+
*/
|
|
153
|
+
validationBehavior?: "aria" | "native";
|
|
154
|
+
/** Field name(s) for server error lookup. */
|
|
155
|
+
name?: string | string[];
|
|
77
156
|
}
|
|
78
157
|
|
|
79
|
-
export interface ComboBoxState<
|
|
158
|
+
export interface ComboBoxState<
|
|
159
|
+
T = unknown,
|
|
160
|
+
M extends ComboBoxSelectionMode = "single",
|
|
161
|
+
> extends FormValidationState {
|
|
80
162
|
/** The collection of items (may be filtered). */
|
|
81
163
|
readonly collection: Accessor<Collection<T>>;
|
|
82
164
|
/**
|
|
@@ -93,17 +175,40 @@ export interface ComboBoxState<T = unknown> {
|
|
|
93
175
|
close(): void;
|
|
94
176
|
/** Toggle the combobox dropdown. */
|
|
95
177
|
toggle(focusStrategy?: FocusStrategy | null, trigger?: MenuTriggerAction): void;
|
|
96
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* The key for the first selected item. Null in multiple mode.
|
|
180
|
+
* @deprecated Use `value`.
|
|
181
|
+
*/
|
|
97
182
|
readonly selectedKey: Accessor<Key | null>;
|
|
98
|
-
/**
|
|
183
|
+
/**
|
|
184
|
+
* The default selected key.
|
|
185
|
+
* @deprecated Use `defaultValue`.
|
|
186
|
+
*/
|
|
99
187
|
readonly defaultSelectedKey: Key | null;
|
|
100
|
-
/**
|
|
188
|
+
/**
|
|
189
|
+
* The value of the first selected item.
|
|
190
|
+
* @deprecated Use `selectedItems`.
|
|
191
|
+
*/
|
|
101
192
|
readonly selectedItem: Accessor<CollectionNode<T> | null>;
|
|
102
|
-
/**
|
|
193
|
+
/**
|
|
194
|
+
* Sets the selected key.
|
|
195
|
+
* @deprecated Use `setValue`.
|
|
196
|
+
*/
|
|
103
197
|
setSelectedKey(key: Key | null): void;
|
|
104
|
-
/** The
|
|
198
|
+
/** The current combobox value. */
|
|
199
|
+
readonly value: Accessor<ComboBoxValueType<M>>;
|
|
200
|
+
/** The default combobox value. */
|
|
201
|
+
readonly defaultValue: ComboBoxValueType<M>;
|
|
202
|
+
/** Sets the combobox value. */
|
|
203
|
+
setValue(value: Key | readonly Key[] | null): void;
|
|
204
|
+
/**
|
|
205
|
+
* Selected keys derived from `value`.
|
|
206
|
+
* Kept for the headless Backspace path and the tag/list adapters.
|
|
207
|
+
*/
|
|
105
208
|
readonly selectedKeys: Accessor<Set<Key>>;
|
|
106
|
-
/**
|
|
209
|
+
/** Replace the selected keys. */
|
|
210
|
+
setSelectedKeys(keys: Iterable<Key>): void;
|
|
211
|
+
/** The currently selected items. */
|
|
107
212
|
readonly selectedItems: Accessor<CollectionNode<T>[]>;
|
|
108
213
|
/** Remove a selected key (multiple mode). */
|
|
109
214
|
removeSelectedKey(key: Key): void;
|
|
@@ -132,7 +237,7 @@ export interface ComboBoxState<T = unknown> {
|
|
|
132
237
|
/** Select a key and close the menu (for ListState compatibility). */
|
|
133
238
|
select(key: Key): void;
|
|
134
239
|
/** The selection mode. */
|
|
135
|
-
readonly selectionMode: Accessor<
|
|
240
|
+
readonly selectionMode: Accessor<M>;
|
|
136
241
|
/** Check if a key is selected. */
|
|
137
242
|
isSelected(key: Key): boolean;
|
|
138
243
|
/** Whether the combobox is disabled. */
|
|
@@ -154,59 +259,65 @@ export const defaultContainsFilter: FilterFn = (textValue, inputValue) => {
|
|
|
154
259
|
* Creates state for a combobox component.
|
|
155
260
|
* Combines list state with input value management and filtering.
|
|
156
261
|
*/
|
|
157
|
-
export function createComboBoxState<T = unknown>(
|
|
158
|
-
props: MaybeAccessor<ComboBoxStateProps<T>>,
|
|
159
|
-
): ComboBoxState<T> {
|
|
160
|
-
const getProps = (
|
|
262
|
+
export function createComboBoxState<T = unknown, M extends ComboBoxSelectionMode = "single">(
|
|
263
|
+
props: MaybeAccessor<ComboBoxStateProps<T, M>>,
|
|
264
|
+
): ComboBoxState<T, M> {
|
|
265
|
+
const getProps = propsAccessor(props);
|
|
161
266
|
|
|
162
267
|
// Extract options with defaults
|
|
163
268
|
const menuTrigger = () => getProps().menuTrigger ?? "input";
|
|
164
269
|
const allowsEmptyCollection = () => getProps().allowsEmptyCollection ?? false;
|
|
165
270
|
const allowsCustomValue = () => getProps().allowsCustomValue ?? false;
|
|
166
271
|
const shouldCloseOnBlur = () => getProps().shouldCloseOnBlur ?? true;
|
|
167
|
-
const isMultiple = () => getProps().selectionMode === "multiple";
|
|
272
|
+
const isMultiple = () => (getProps().selectionMode ?? "single") === "multiple";
|
|
168
273
|
|
|
169
274
|
// Track focus strategy for list navigation
|
|
170
|
-
const [focusStrategy, setFocusStrategy] =
|
|
275
|
+
const [focusStrategy, setFocusStrategy] = createInternalSignal<FocusStrategy | null>(null);
|
|
171
276
|
|
|
172
277
|
// Track whether we're showing all items (vs filtered)
|
|
173
|
-
const [showAllItems, setShowAllItems] =
|
|
278
|
+
const [showAllItems, setShowAllItems] = createInternalSignal(false);
|
|
174
279
|
|
|
175
280
|
// Track the menu open trigger
|
|
176
281
|
let menuOpenTrigger: MenuTriggerAction = "focus";
|
|
177
282
|
|
|
178
|
-
// ----
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
);
|
|
283
|
+
// ---- Value ----
|
|
284
|
+
// One signal matches upstream ValueType<M>. `selectedKey` stays as a
|
|
285
|
+
// deprecated single-mode view. Multiple mode reads and writes `value`.
|
|
286
|
+
type StoredComboBoxValue = Key | readonly Key[] | null;
|
|
183
287
|
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
288
|
+
const controlledPropValue = (): StoredComboBoxValue | undefined => {
|
|
289
|
+
const propsNow = getProps();
|
|
290
|
+
if (propsNow.value !== undefined) {
|
|
291
|
+
return propsNow.value as StoredComboBoxValue;
|
|
292
|
+
}
|
|
293
|
+
if (!isMultiple() && propsNow.selectedKey !== undefined) {
|
|
294
|
+
return propsNow.selectedKey;
|
|
295
|
+
}
|
|
296
|
+
return undefined;
|
|
188
297
|
};
|
|
189
298
|
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
299
|
+
const isValueControlled = () => controlledPropValue() !== undefined;
|
|
300
|
+
|
|
301
|
+
const initialStoredValue = (): StoredComboBoxValue => {
|
|
302
|
+
const propsNow = getProps();
|
|
303
|
+
if (propsNow.defaultValue !== undefined) {
|
|
304
|
+
return propsNow.defaultValue as StoredComboBoxValue;
|
|
193
305
|
}
|
|
194
|
-
|
|
306
|
+
if ((propsNow.selectionMode ?? "single") === "multiple") {
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
return propsNow.defaultSelectedKey ?? null;
|
|
195
310
|
};
|
|
196
311
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const isSelectionControlled = () => getProps().selectedKey !== undefined;
|
|
200
|
-
const [internalSelectedKey, setInternalSelectedKey] = createSignal<Key | null>(
|
|
201
|
-
getProps().defaultSelectedKey ?? null,
|
|
202
|
-
);
|
|
312
|
+
const [internalValue, setInternalValue] =
|
|
313
|
+
createInternalSignal<StoredComboBoxValue>(initialStoredValue());
|
|
203
314
|
|
|
204
315
|
// ---- Input Value State ----
|
|
205
316
|
// Initialized after selection so we can derive from selected item if needed
|
|
206
317
|
const isInputControlled = () => getProps().inputValue !== undefined;
|
|
207
318
|
|
|
208
319
|
// We'll set the proper initial value after collection is created
|
|
209
|
-
const [internalInputValue, setInternalInputValue] =
|
|
320
|
+
const [internalInputValue, setInternalInputValue] = createInternalSignal(
|
|
210
321
|
getProps().defaultInputValue ?? "",
|
|
211
322
|
);
|
|
212
323
|
// Track if we've initialized input from selection
|
|
@@ -217,6 +328,9 @@ export function createComboBoxState<T = unknown>(
|
|
|
217
328
|
};
|
|
218
329
|
|
|
219
330
|
const setInputValue = (value: string) => {
|
|
331
|
+
// Solid 2 batches writes onto a microtask, so onInputChange and the
|
|
332
|
+
// auto-open effect settle in one flush (CB-OC-03: onInputChange then
|
|
333
|
+
// onOpenChange(true, "input")).
|
|
220
334
|
if (!isInputControlled()) {
|
|
221
335
|
setInternalInputValue(value);
|
|
222
336
|
}
|
|
@@ -224,20 +338,130 @@ export function createComboBoxState<T = unknown>(
|
|
|
224
338
|
};
|
|
225
339
|
|
|
226
340
|
// Track last committed input value
|
|
227
|
-
const [lastValue, setLastValue] =
|
|
341
|
+
const [lastValue, setLastValue] = createInternalSignal(inputValue());
|
|
342
|
+
|
|
343
|
+
const storedValue = (): StoredComboBoxValue => {
|
|
344
|
+
const controlled = controlledPropValue();
|
|
345
|
+
return controlled !== undefined ? controlled : internalValue();
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// `Array.isArray` narrows `readonly Key[]` to `any[]` and then fails to
|
|
349
|
+
// exclude that array from the scalar branch. A string/number check keeps
|
|
350
|
+
// the caller's readonly list intact.
|
|
351
|
+
const displayValue = (): Key | readonly Key[] | null => {
|
|
352
|
+
const current = storedValue();
|
|
353
|
+
if (!isMultiple()) {
|
|
354
|
+
return isKeyList(current) ? (current[0] ?? null) : current;
|
|
355
|
+
}
|
|
356
|
+
if (isKeyList(current)) return current;
|
|
357
|
+
if (current == null) return EMPTY_COMBOBOX_KEYS;
|
|
358
|
+
return [current];
|
|
359
|
+
};
|
|
228
360
|
|
|
229
361
|
const selectedKey: Accessor<Key | null> = () => {
|
|
230
|
-
|
|
362
|
+
if (isMultiple()) return null;
|
|
363
|
+
const current = displayValue();
|
|
364
|
+
return isKeyList(current) ? (current[0] ?? null) : current;
|
|
231
365
|
};
|
|
232
366
|
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
367
|
+
const selectedKeys: Accessor<Set<Key>> = () => {
|
|
368
|
+
const current = displayValue();
|
|
369
|
+
if (isKeyList(current)) return new Set(current);
|
|
370
|
+
return current == null ? new Set() : new Set([current]);
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const setValue = (next: Key | readonly Key[] | null) => {
|
|
374
|
+
// Solid 2 batches writes onto a microtask, so a fully controlled
|
|
375
|
+
// onSelectionChange that updates the value then inputValue settles in
|
|
376
|
+
// one flush instead of closing then auto-opening.
|
|
377
|
+
if (!isMultiple()) {
|
|
378
|
+
const key: Key | null =
|
|
379
|
+
typeof next === "string" || typeof next === "number"
|
|
380
|
+
? next
|
|
381
|
+
: next === null
|
|
382
|
+
? null
|
|
383
|
+
: (next[0] ?? null);
|
|
384
|
+
const previous = untrack(selectedKey);
|
|
385
|
+
if (!isValueControlled()) {
|
|
386
|
+
setInternalValue(key);
|
|
387
|
+
}
|
|
388
|
+
getProps().onChange?.(key as ComboBoxChangeValueType<M>);
|
|
389
|
+
if (key !== previous) {
|
|
390
|
+
getProps().onSelectionChange?.(key);
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
236
393
|
}
|
|
237
|
-
|
|
394
|
+
|
|
395
|
+
// Copy into state so the caller's list is never stored or mutated.
|
|
396
|
+
// `onChange` gets a second array, so a callback can mutate its argument
|
|
397
|
+
// without changing state or the input.
|
|
398
|
+
const keys: Key[] =
|
|
399
|
+
typeof next === "string" || typeof next === "number"
|
|
400
|
+
? [next]
|
|
401
|
+
: next === null
|
|
402
|
+
? []
|
|
403
|
+
: [...next];
|
|
404
|
+
if (!isValueControlled()) {
|
|
405
|
+
setInternalValue(keys);
|
|
406
|
+
}
|
|
407
|
+
getProps().onChange?.([...keys] as ComboBoxChangeValueType<M>);
|
|
238
408
|
};
|
|
239
409
|
|
|
410
|
+
const setSelectedKey = (key: Key | null) => {
|
|
411
|
+
setValue(key);
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const defaultValue: ComboBoxValueType<M> = (() => {
|
|
415
|
+
const propsNow = getProps();
|
|
416
|
+
const resolved =
|
|
417
|
+
propsNow.defaultValue !== undefined
|
|
418
|
+
? propsNow.defaultValue
|
|
419
|
+
: isMultiple()
|
|
420
|
+
? (EMPTY_COMBOBOX_KEYS as unknown as ComboBoxValueType<M>)
|
|
421
|
+
: ((propsNow.defaultSelectedKey ?? null) as ComboBoxValueType<M>);
|
|
422
|
+
return (resolved ?? untrack(displayValue)) as ComboBoxValueType<M>;
|
|
423
|
+
})();
|
|
424
|
+
|
|
425
|
+
const defaultSelectedKey =
|
|
426
|
+
getProps().defaultSelectedKey ?? (isMultiple() ? null : (untrack(displayValue) as Key | null));
|
|
427
|
+
|
|
428
|
+
const validationValue = createMemo<ComboBoxValidationValue<M> | null>(() => {
|
|
429
|
+
const dVal = displayValue();
|
|
430
|
+
if (isKeyList(dVal) && dVal.length === 0) {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
inputValue: inputValue(),
|
|
435
|
+
value: (isKeyList(dVal) ? [...dVal] : dVal) as ComboBoxValidationType<M>,
|
|
436
|
+
selectedKey: selectedKey(),
|
|
437
|
+
};
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const validation = createFormValidationState({
|
|
441
|
+
get value() {
|
|
442
|
+
return validationValue();
|
|
443
|
+
},
|
|
444
|
+
get isInvalid() {
|
|
445
|
+
return getProps().isInvalid;
|
|
446
|
+
},
|
|
447
|
+
get validationState() {
|
|
448
|
+
return getProps().validationState;
|
|
449
|
+
},
|
|
450
|
+
get validate() {
|
|
451
|
+
return getProps().validate;
|
|
452
|
+
},
|
|
453
|
+
get name() {
|
|
454
|
+
return getProps().name;
|
|
455
|
+
},
|
|
456
|
+
get validationBehavior() {
|
|
457
|
+
return getProps().validationBehavior ?? "native";
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
|
|
240
461
|
// ---- Overlay State ----
|
|
462
|
+
// Assigned after createListState; onOpenChange only fires after both exist
|
|
463
|
+
// except a defaultOpen init, which we skip via the optional call.
|
|
464
|
+
let listState!: ListState<T>;
|
|
241
465
|
const overlayState = createOverlayTriggerState({
|
|
242
466
|
get isOpen() {
|
|
243
467
|
return getProps().isOpen;
|
|
@@ -247,11 +471,16 @@ export function createComboBoxState<T = unknown>(
|
|
|
247
471
|
},
|
|
248
472
|
onOpenChange(isOpen: boolean) {
|
|
249
473
|
getProps().onOpenChange?.(isOpen, isOpen ? menuOpenTrigger : undefined);
|
|
474
|
+
// RAC useComboBoxState.ts:314-317
|
|
475
|
+
listState?.setFocused(isOpen);
|
|
476
|
+
if (!isOpen) {
|
|
477
|
+
listState?.setFocusedKey(null);
|
|
478
|
+
}
|
|
250
479
|
},
|
|
251
480
|
});
|
|
252
481
|
|
|
253
482
|
// ---- List State (unfiltered collection) ----
|
|
254
|
-
|
|
483
|
+
listState = createListState<T>({
|
|
255
484
|
get items() {
|
|
256
485
|
// Use items or defaultItems
|
|
257
486
|
return getProps().items ?? getProps().defaultItems ?? [];
|
|
@@ -271,34 +500,37 @@ export function createComboBoxState<T = unknown>(
|
|
|
271
500
|
get selectionMode() {
|
|
272
501
|
return isMultiple() ? ("multiple" as const) : ("single" as const);
|
|
273
502
|
},
|
|
274
|
-
|
|
503
|
+
// RAC useComboBoxState.ts:254-255. Single selection cannot be toggled
|
|
504
|
+
// empty. Duplicate events re-emit the same key so a click on the selected
|
|
505
|
+
// option reaches the reset-and-close branch below. Multiple selection
|
|
506
|
+
// still allows clearing the last key.
|
|
507
|
+
get disallowEmptySelection() {
|
|
508
|
+
return !isMultiple();
|
|
509
|
+
},
|
|
510
|
+
allowDuplicateSelectionEvents: true,
|
|
275
511
|
get selectedKeys() {
|
|
276
|
-
|
|
277
|
-
return Array.from(selectedKeys());
|
|
278
|
-
}
|
|
279
|
-
const key = selectedKey();
|
|
280
|
-
return key != null ? [key] : [];
|
|
512
|
+
return convertValue(displayValue());
|
|
281
513
|
},
|
|
282
514
|
onSelectionChange(keys) {
|
|
283
515
|
if (keys === "all") return;
|
|
284
516
|
|
|
285
517
|
if (isMultiple()) {
|
|
286
|
-
|
|
287
|
-
const newKeys = new Set(keys);
|
|
288
|
-
setSelectedKeys(newKeys);
|
|
518
|
+
setValue([...keys]);
|
|
289
519
|
return;
|
|
290
520
|
}
|
|
291
521
|
|
|
292
522
|
const key = keys.size > 0 ? Array.from(keys)[0] : null;
|
|
293
523
|
|
|
294
|
-
//
|
|
524
|
+
// RAC useComboBoxState.ts:265-270 — same key still notifies, then
|
|
525
|
+
// resets the input and closes (click on the already selected option).
|
|
295
526
|
if (key === selectedKey()) {
|
|
527
|
+
getProps().onSelectionChange?.(key);
|
|
296
528
|
resetInputValue();
|
|
297
529
|
closeMenu();
|
|
298
530
|
return;
|
|
299
531
|
}
|
|
300
532
|
|
|
301
|
-
|
|
533
|
+
setValue(key);
|
|
302
534
|
},
|
|
303
535
|
});
|
|
304
536
|
|
|
@@ -307,16 +539,12 @@ export function createComboBoxState<T = unknown>(
|
|
|
307
539
|
|
|
308
540
|
const filteredCollection = createMemo<Collection<T>>(() => {
|
|
309
541
|
const collection = originalCollection();
|
|
310
|
-
const input = inputValue();
|
|
311
542
|
const filter = getProps().defaultFilter;
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (!filter) {
|
|
543
|
+
// RAC useComboBoxState.ts:297-302 — no default filter if items are controlled.
|
|
544
|
+
if (getProps().items != null || !filter) {
|
|
315
545
|
return collection;
|
|
316
546
|
}
|
|
317
|
-
|
|
318
|
-
// Filter the collection based on input value
|
|
319
|
-
return filterCollection(collection, input, filter);
|
|
547
|
+
return filterCollection(collection, inputValue(), filter);
|
|
320
548
|
});
|
|
321
549
|
|
|
322
550
|
// The displayed collection depends on showAllItems flag
|
|
@@ -346,13 +574,16 @@ export function createComboBoxState<T = unknown>(
|
|
|
346
574
|
// Initialize input value from selected item if not already set
|
|
347
575
|
// This runs once on creation
|
|
348
576
|
if (!inputInitialized && !isInputControlled()) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const
|
|
353
|
-
if (
|
|
354
|
-
|
|
355
|
-
|
|
577
|
+
// RAC getDefaultInputValue: an explicit empty defaultInputValue wins.
|
|
578
|
+
// Multiple mode does not seed the input from the first selected item.
|
|
579
|
+
if (getProps().defaultInputValue == null && !isMultiple()) {
|
|
580
|
+
const key = selectedKey();
|
|
581
|
+
if (key != null) {
|
|
582
|
+
const item = originalCollection().getItem(key);
|
|
583
|
+
if (item) {
|
|
584
|
+
setInternalInputValue(item.textValue);
|
|
585
|
+
setLastValue(item.textValue);
|
|
586
|
+
}
|
|
356
587
|
}
|
|
357
588
|
}
|
|
358
589
|
inputInitialized = true;
|
|
@@ -360,8 +591,8 @@ export function createComboBoxState<T = unknown>(
|
|
|
360
591
|
|
|
361
592
|
// ---- Helper Functions ----
|
|
362
593
|
const resetInputValue = () => {
|
|
363
|
-
const
|
|
364
|
-
const textValue =
|
|
594
|
+
const key = selectedKey();
|
|
595
|
+
const textValue = key != null ? (originalCollection().getItem(key)?.textValue ?? "") : "";
|
|
365
596
|
setLastValue(textValue);
|
|
366
597
|
setInputValue(textValue);
|
|
367
598
|
};
|
|
@@ -374,13 +605,12 @@ export function createComboBoxState<T = unknown>(
|
|
|
374
605
|
|
|
375
606
|
// ---- Open/Toggle Logic ----
|
|
376
607
|
// Auto-focus the menu on open the way upstream's listbox does via its
|
|
377
|
-
// `autoFocus: state.focusStrategy || true` prop (useComboBox.ts:
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
// (no aria-activedescendant / no highlighted row) unlike upstream.
|
|
608
|
+
// `autoFocus: state.focusStrategy || true` prop (useComboBox.ts:535 →
|
|
609
|
+
// useSelectableCollection). createListBox does not honor a listbox
|
|
610
|
+
// `autoFocus` prop, so BOTH open() and toggle() apply it here. RAC applies
|
|
611
|
+
// this in a later effect; doing it synchronously keeps the first option
|
|
612
|
+
// paint (slot `aria-labelledby`) stable. createComboBox still announces
|
|
613
|
+
// option count on open.
|
|
384
614
|
const applyOpenFocus = (strategy: FocusStrategy | null) => {
|
|
385
615
|
const key = selectedKey();
|
|
386
616
|
const nextFocusedKey =
|
|
@@ -398,15 +628,20 @@ export function createComboBoxState<T = unknown>(
|
|
|
398
628
|
const open = (strategy: FocusStrategy | null = null, trigger?: MenuTriggerAction) => {
|
|
399
629
|
const displayAll = trigger === "manual" || (trigger === "focus" && menuTrigger() === "focus");
|
|
400
630
|
|
|
401
|
-
//
|
|
631
|
+
// RAC useComboBoxState.ts:331-340 — controlled `items` may open empty;
|
|
632
|
+
// showAllItems only when items are undefined (collection filtering).
|
|
402
633
|
const filtered = filteredCollection();
|
|
403
634
|
const original = originalCollection();
|
|
635
|
+
const items = getProps().items;
|
|
404
636
|
const canOpen =
|
|
405
|
-
allowsEmptyCollection() ||
|
|
637
|
+
allowsEmptyCollection() ||
|
|
638
|
+
filtered.size > 0 ||
|
|
639
|
+
(displayAll && original.size > 0) ||
|
|
640
|
+
items != null;
|
|
406
641
|
|
|
407
642
|
if (!canOpen) return;
|
|
408
643
|
|
|
409
|
-
if (displayAll && !overlayState.isOpen()) {
|
|
644
|
+
if (displayAll && !overlayState.isOpen() && items === undefined) {
|
|
410
645
|
setShowAllItems(true);
|
|
411
646
|
}
|
|
412
647
|
|
|
@@ -421,17 +656,20 @@ export function createComboBoxState<T = unknown>(
|
|
|
421
656
|
const toggle = (strategy: FocusStrategy | null = null, trigger?: MenuTriggerAction) => {
|
|
422
657
|
const displayAll = trigger === "manual" || (trigger === "focus" && menuTrigger() === "focus");
|
|
423
658
|
|
|
424
|
-
// Check if we can open (if closed)
|
|
425
659
|
const filtered = filteredCollection();
|
|
426
660
|
const original = originalCollection();
|
|
661
|
+
const items = getProps().items;
|
|
427
662
|
const canOpen =
|
|
428
|
-
allowsEmptyCollection() ||
|
|
663
|
+
allowsEmptyCollection() ||
|
|
664
|
+
filtered.size > 0 ||
|
|
665
|
+
(displayAll && original.size > 0) ||
|
|
666
|
+
items != null;
|
|
429
667
|
|
|
430
668
|
if (!canOpen && !overlayState.isOpen()) return;
|
|
431
669
|
|
|
432
670
|
const willOpen = !overlayState.isOpen();
|
|
433
671
|
|
|
434
|
-
if (displayAll && willOpen) {
|
|
672
|
+
if (displayAll && willOpen && items === undefined) {
|
|
435
673
|
setShowAllItems(true);
|
|
436
674
|
}
|
|
437
675
|
|
|
@@ -448,19 +686,35 @@ export function createComboBoxState<T = unknown>(
|
|
|
448
686
|
|
|
449
687
|
// ---- Commit/Revert Logic ----
|
|
450
688
|
const commitCustomValue = () => {
|
|
451
|
-
|
|
689
|
+
if (isMultiple()) {
|
|
690
|
+
// Custom text is independent of the selected items.
|
|
691
|
+
setLastValue(inputValue());
|
|
692
|
+
closeMenu();
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
setValue(null);
|
|
452
696
|
closeMenu();
|
|
453
697
|
};
|
|
454
698
|
|
|
455
|
-
const commitSelection = () => {
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const
|
|
460
|
-
|
|
699
|
+
const commitSelection = (shouldForceSelectionChange = false) => {
|
|
700
|
+
// Both the value and the input are controlled: notify, then close.
|
|
701
|
+
// Otherwise reset the input for the caller.
|
|
702
|
+
if (isValueControlled() && isInputControlled()) {
|
|
703
|
+
const key = selectedKey();
|
|
704
|
+
const itemText = key != null ? (originalCollection().getItem(key)?.textValue ?? "") : "";
|
|
705
|
+
if (shouldForceSelectionChange || isMultiple() || inputValue() !== itemText) {
|
|
706
|
+
getProps().onSelectionChange?.(key);
|
|
707
|
+
const current = displayValue();
|
|
708
|
+
const changeValue = (
|
|
709
|
+
isKeyList(current) ? [...current] : current
|
|
710
|
+
) as ComboBoxChangeValueType<M>;
|
|
711
|
+
getProps().onChange?.(changeValue);
|
|
712
|
+
}
|
|
713
|
+
// RAC useComboBoxState.ts:543-545 — stop the auto-open-on-input effect
|
|
714
|
+
// from reopening after this close.
|
|
715
|
+
setLastValue(itemText);
|
|
461
716
|
closeMenu();
|
|
462
717
|
} else {
|
|
463
|
-
// Reset input to selected item's text
|
|
464
718
|
resetInputValue();
|
|
465
719
|
closeMenu();
|
|
466
720
|
}
|
|
@@ -483,22 +737,18 @@ export function createComboBoxState<T = unknown>(
|
|
|
483
737
|
const commit = () => {
|
|
484
738
|
const focusedKey = listState.focusedKey();
|
|
485
739
|
|
|
486
|
-
if (isMultiple()) {
|
|
487
|
-
// In multiple mode, toggle the focused item without closing
|
|
488
|
-
if (overlayState.isOpen() && focusedKey != null) {
|
|
489
|
-
select(focusedKey);
|
|
490
|
-
}
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
740
|
if (overlayState.isOpen() && focusedKey != null) {
|
|
495
|
-
//
|
|
496
|
-
|
|
497
|
-
|
|
741
|
+
// RAC useComboBoxState.ts:564-572 — an already-selected single key goes
|
|
742
|
+
// through commitSelection. A new single key is `selectionManager.select()`
|
|
743
|
+
// without close; the open/close effect then closes while the menu is
|
|
744
|
+
// still open, so auto-open (input !== last && !isOpen) is skipped.
|
|
745
|
+
// Multiple toggles through setValue and leaves the menu open.
|
|
746
|
+
if (!isMultiple() && selectedKey() === focusedKey) {
|
|
747
|
+
commitSelection(true);
|
|
748
|
+
} else if (isMultiple()) {
|
|
749
|
+
select(focusedKey);
|
|
498
750
|
} else {
|
|
499
|
-
|
|
500
|
-
setSelectedKey(focusedKey);
|
|
501
|
-
closeMenu();
|
|
751
|
+
listState.selectionManager.select(focusedKey);
|
|
502
752
|
}
|
|
503
753
|
} else {
|
|
504
754
|
commitValue();
|
|
@@ -514,12 +764,31 @@ export function createComboBoxState<T = unknown>(
|
|
|
514
764
|
};
|
|
515
765
|
|
|
516
766
|
// ---- Focus Handling ----
|
|
517
|
-
const [isFocused, setIsFocused] =
|
|
518
|
-
|
|
767
|
+
const [isFocused, setIsFocused] = createInternalSignal(false);
|
|
768
|
+
|
|
769
|
+
let valueOnFocus: [string, Key | readonly Key[] | null] = [
|
|
770
|
+
untrack(inputValue),
|
|
771
|
+
untrack(displayValue) as Key | readonly Key[] | null,
|
|
772
|
+
];
|
|
773
|
+
|
|
774
|
+
const hasMoved = (a: Key | readonly Key[] | null, b: Key | readonly Key[] | null): boolean => {
|
|
775
|
+
if (a === b) return false;
|
|
776
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
777
|
+
if (a.length !== b.length) return true;
|
|
778
|
+
for (let i = 0; i < a.length; i++) {
|
|
779
|
+
if (a[i] !== b[i]) return true;
|
|
780
|
+
}
|
|
781
|
+
return false;
|
|
782
|
+
}
|
|
783
|
+
return true;
|
|
784
|
+
};
|
|
519
785
|
|
|
786
|
+
// RAC useComboBoxState.ts:578-593 keeps `valueOnFocus = useRef([inputValue, displayValue])`
|
|
787
|
+
// and, on blur, calls `validation.commitValidation()` when the input value or the display
|
|
788
|
+
// value moved while focused.
|
|
520
789
|
const setFocused = (focused: boolean) => {
|
|
521
790
|
if (focused) {
|
|
522
|
-
valueOnFocus = inputValue();
|
|
791
|
+
valueOnFocus = [inputValue(), displayValue() as Key | readonly Key[] | null];
|
|
523
792
|
if (menuTrigger() === "focus" && !getProps().isReadOnly) {
|
|
524
793
|
open(null, "focus");
|
|
525
794
|
}
|
|
@@ -527,47 +796,93 @@ export function createComboBoxState<T = unknown>(
|
|
|
527
796
|
if (shouldCloseOnBlur()) {
|
|
528
797
|
commitValue();
|
|
529
798
|
}
|
|
799
|
+
if (
|
|
800
|
+
inputValue() !== valueOnFocus[0] ||
|
|
801
|
+
hasMoved(displayValue() as Key | readonly Key[] | null, valueOnFocus[1])
|
|
802
|
+
) {
|
|
803
|
+
validation.commitValidation();
|
|
804
|
+
}
|
|
530
805
|
}
|
|
531
806
|
setIsFocused(focused);
|
|
532
807
|
};
|
|
533
808
|
|
|
534
809
|
// ---- Effects for Auto Open/Close ----
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
810
|
+
// RAC useComboBoxState.ts:408-496 is one effect: auto-open, auto-close,
|
|
811
|
+
// close-on-selection, then lastValue. Close-on-selection must not run as a
|
|
812
|
+
// later effect — after a fully-controlled commit the parent updates
|
|
813
|
+
// inputValue, a trailing close leaves !isOpen && input !== last, and
|
|
814
|
+
// auto-open reopens (D13 Enter re-announces "N options available.").
|
|
815
|
+
// lastDisplayValue starts as the current key so an initial selectedKey is
|
|
816
|
+
// not treated as a selection change (RAC lastValueRef = useRef(displayValue)).
|
|
817
|
+
let lastDisplayValue: Key | null | undefined = untrack(selectedKey);
|
|
818
|
+
|
|
819
|
+
createEffect(
|
|
820
|
+
() => ({
|
|
821
|
+
input: inputValue(),
|
|
822
|
+
filtered: filteredCollection(),
|
|
823
|
+
isOpen: overlayState.isOpen(),
|
|
824
|
+
last: lastValue(),
|
|
825
|
+
focused: isFocused(),
|
|
826
|
+
key: isMultiple() ? null : selectedKey(),
|
|
827
|
+
allowsEmpty: allowsEmptyCollection(),
|
|
828
|
+
trigger: menuTrigger(),
|
|
829
|
+
showingAll: showAllItems(),
|
|
830
|
+
inputControlled: isInputControlled(),
|
|
831
|
+
selectionControlled: isValueControlled(),
|
|
832
|
+
multiple: isMultiple(),
|
|
833
|
+
}),
|
|
834
|
+
({
|
|
835
|
+
input,
|
|
836
|
+
filtered,
|
|
837
|
+
isOpen,
|
|
838
|
+
last,
|
|
839
|
+
focused,
|
|
840
|
+
key,
|
|
841
|
+
allowsEmpty,
|
|
842
|
+
trigger,
|
|
843
|
+
showingAll,
|
|
844
|
+
inputControlled,
|
|
845
|
+
selectionControlled,
|
|
846
|
+
multiple,
|
|
847
|
+
}) => {
|
|
848
|
+
// Auto-open when typing
|
|
849
|
+
if (
|
|
850
|
+
focused &&
|
|
851
|
+
(filtered.size > 0 || allowsEmpty) &&
|
|
852
|
+
!isOpen &&
|
|
853
|
+
input !== last &&
|
|
854
|
+
trigger !== "manual"
|
|
855
|
+
) {
|
|
856
|
+
open(null, "input");
|
|
857
|
+
}
|
|
552
858
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
859
|
+
// Auto-close when empty (unless showing all)
|
|
860
|
+
if (!showingAll && !allowsEmpty && isOpen && filtered.size === 0) {
|
|
861
|
+
closeMenu();
|
|
862
|
+
}
|
|
557
863
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
864
|
+
// Close when an item is selected (RAC displayValue !== lastValueRef).
|
|
865
|
+
if (key != null && key !== lastDisplayValue) {
|
|
866
|
+
closeMenu();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Clear focused key when input changes
|
|
870
|
+
if (input !== last) {
|
|
871
|
+
listState.setFocusedKey(null);
|
|
872
|
+
setShowAllItems(false);
|
|
873
|
+
|
|
874
|
+
// Clear selection when the input is cleared. Fully controlled single
|
|
875
|
+
// values, and every multiple value, stay put.
|
|
876
|
+
if (!multiple && input === "" && (!inputControlled || !selectionControlled)) {
|
|
877
|
+
setValue(null);
|
|
878
|
+
}
|
|
562
879
|
|
|
563
|
-
|
|
564
|
-
if (input === "" && (!isInputControlled() || !isSelectionControlled())) {
|
|
565
|
-
setSelectedKey(null);
|
|
880
|
+
setLastValue(input);
|
|
566
881
|
}
|
|
567
882
|
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
|
|
883
|
+
lastDisplayValue = key;
|
|
884
|
+
},
|
|
885
|
+
);
|
|
571
886
|
|
|
572
887
|
// Keep the input text in sync with the selected item (single mode only).
|
|
573
888
|
//
|
|
@@ -585,79 +900,82 @@ export function createComboBoxState<T = unknown>(
|
|
|
585
900
|
// dependency. `originalCollection()` stays tracked (the unfiltered collection
|
|
586
901
|
// only changes when `items` change, never on filtering) so a preset selection
|
|
587
902
|
// still gets its text once async items arrive.
|
|
588
|
-
createEffect(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
903
|
+
createEffect(
|
|
904
|
+
() => {
|
|
905
|
+
if (isMultiple()) return null;
|
|
906
|
+
const key = selectedKey();
|
|
907
|
+
const item = key != null ? originalCollection().getItem(key) : null;
|
|
908
|
+
return {
|
|
909
|
+
key,
|
|
910
|
+
textValue: item?.textValue ?? "",
|
|
911
|
+
inputControlled: isInputControlled(),
|
|
912
|
+
selectionControlled: isValueControlled(),
|
|
913
|
+
};
|
|
914
|
+
},
|
|
915
|
+
(data) => {
|
|
916
|
+
if (!data) return;
|
|
917
|
+
const { key, textValue, inputControlled, selectionControlled } = data;
|
|
918
|
+
// Only update if selection changed and not fully controlled
|
|
919
|
+
if (!inputControlled || !selectionControlled) {
|
|
920
|
+
if (key != null && textValue !== untrack(inputValue)) {
|
|
921
|
+
setInputValue(textValue);
|
|
922
|
+
setLastValue(textValue);
|
|
923
|
+
}
|
|
599
924
|
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
// Close when selection changes (only in single mode)
|
|
604
|
-
createEffect((prevKey: Key | null | undefined) => {
|
|
605
|
-
if (isMultiple()) return undefined;
|
|
606
|
-
const key = selectedKey();
|
|
607
|
-
if (key != null && key !== prevKey) {
|
|
608
|
-
closeMenu();
|
|
609
|
-
}
|
|
610
|
-
return key;
|
|
611
|
-
}, undefined);
|
|
925
|
+
},
|
|
926
|
+
);
|
|
612
927
|
|
|
613
928
|
// ---- Selection Methods for ListState compatibility ----
|
|
614
929
|
// These methods allow createOption to work with ComboBoxState
|
|
615
930
|
const select = (key: Key) => {
|
|
616
931
|
if (isMultiple()) {
|
|
617
|
-
// Toggle selection in multiple mode
|
|
618
932
|
const current = new Set(selectedKeys());
|
|
619
933
|
if (current.has(key)) {
|
|
620
934
|
current.delete(key);
|
|
621
935
|
} else {
|
|
622
936
|
current.add(key);
|
|
623
937
|
}
|
|
624
|
-
|
|
625
|
-
// Don't close menu and don't reset input in multi-select mode
|
|
938
|
+
setValue([...current]);
|
|
626
939
|
} else {
|
|
627
|
-
|
|
940
|
+
setValue(key);
|
|
628
941
|
closeMenu();
|
|
629
942
|
}
|
|
630
943
|
};
|
|
631
944
|
|
|
945
|
+
const setSelectedKeys = (keys: Iterable<Key>) => {
|
|
946
|
+
setValue([...keys]);
|
|
947
|
+
};
|
|
948
|
+
|
|
632
949
|
const removeSelectedKey = (key: Key) => {
|
|
633
|
-
if (isMultiple())
|
|
634
|
-
|
|
635
|
-
current.delete(key);
|
|
636
|
-
setSelectedKeys(current);
|
|
637
|
-
}
|
|
950
|
+
if (!isMultiple()) return;
|
|
951
|
+
setValue([...selectedKeys()].filter((selected) => selected !== key));
|
|
638
952
|
};
|
|
639
953
|
|
|
640
|
-
const selectionMode: Accessor<
|
|
954
|
+
const selectionMode: Accessor<M> = () => (getProps().selectionMode ?? "single") as M;
|
|
641
955
|
|
|
642
|
-
const isSelected = (key: Key) =>
|
|
643
|
-
if (isMultiple()) {
|
|
644
|
-
return selectedKeys().has(key);
|
|
645
|
-
}
|
|
646
|
-
return selectedKey() === key;
|
|
647
|
-
};
|
|
956
|
+
const isSelected = (key: Key) => selectedKeys().has(key);
|
|
648
957
|
|
|
649
958
|
// ---- Return State ----
|
|
650
959
|
return {
|
|
960
|
+
realtimeValidation: validation.realtimeValidation,
|
|
961
|
+
displayValidation: validation.displayValidation,
|
|
962
|
+
updateValidation: validation.updateValidation,
|
|
963
|
+
resetValidation: validation.resetValidation,
|
|
964
|
+
commitValidation: validation.commitValidation,
|
|
651
965
|
collection: displayedCollection,
|
|
652
966
|
isOpen: overlayState.isOpen,
|
|
653
967
|
open,
|
|
654
968
|
close: commitValue,
|
|
655
969
|
toggle,
|
|
656
970
|
selectedKey,
|
|
657
|
-
defaultSelectedKey
|
|
971
|
+
defaultSelectedKey,
|
|
658
972
|
selectedItem,
|
|
659
973
|
setSelectedKey,
|
|
974
|
+
value: () => displayValue() as ComboBoxValueType<M>,
|
|
975
|
+
defaultValue,
|
|
976
|
+
setValue,
|
|
660
977
|
selectedKeys,
|
|
978
|
+
setSelectedKeys,
|
|
661
979
|
selectedItems,
|
|
662
980
|
removeSelectedKey,
|
|
663
981
|
inputValue,
|
|
@@ -688,8 +1006,21 @@ export function createComboBoxState<T = unknown>(
|
|
|
688
1006
|
};
|
|
689
1007
|
}
|
|
690
1008
|
|
|
1009
|
+
function isKeyList(value: Key | readonly Key[] | null): value is readonly Key[] {
|
|
1010
|
+
return value !== null && typeof value !== "string" && typeof value !== "number";
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
function convertValue(value: Key | readonly Key[] | null | undefined): readonly Key[] {
|
|
1014
|
+
if (value == null) return [];
|
|
1015
|
+
if (typeof value === "string" || typeof value === "number") return [value];
|
|
1016
|
+
return value;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
691
1019
|
/**
|
|
692
1020
|
* Filter a collection based on input value.
|
|
1021
|
+
* RAC `useComboBoxState.ts:644-672` copies nodes into a new `ListCollection`,
|
|
1022
|
+
* which reassigns `index` (ListCollection.ts:51-53). Keeping the original
|
|
1023
|
+
* `index` makes virtualized `aria-posinset` report the unfiltered position.
|
|
693
1024
|
*/
|
|
694
1025
|
function filterCollection<T>(
|
|
695
1026
|
collection: Collection<T>,
|
|
@@ -701,19 +1032,18 @@ function filterCollection<T>(
|
|
|
701
1032
|
}
|
|
702
1033
|
|
|
703
1034
|
const filteredItems: CollectionNode<T>[] = [];
|
|
1035
|
+
let index = 0;
|
|
704
1036
|
|
|
705
1037
|
for (const item of collection) {
|
|
706
1038
|
if (item.type === "section") {
|
|
707
|
-
// Filter section children
|
|
708
1039
|
const filteredChildren: CollectionNode<T>[] = [];
|
|
709
1040
|
if (item.childNodes) {
|
|
710
1041
|
for (const child of item.childNodes) {
|
|
711
1042
|
if (child.type === "item" && filter(child.textValue, inputValue)) {
|
|
712
|
-
filteredChildren.push(child);
|
|
1043
|
+
filteredChildren.push({ ...child, index: index++ });
|
|
713
1044
|
}
|
|
714
1045
|
}
|
|
715
1046
|
}
|
|
716
|
-
// Only include section if it has matching children
|
|
717
1047
|
if (filteredChildren.length > 0) {
|
|
718
1048
|
filteredItems.push({
|
|
719
1049
|
...item,
|
|
@@ -722,109 +1052,12 @@ function filterCollection<T>(
|
|
|
722
1052
|
}
|
|
723
1053
|
} else if (item.type === "item") {
|
|
724
1054
|
if (filter(item.textValue, inputValue)) {
|
|
725
|
-
filteredItems.push(item);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Create a new collection from filtered items
|
|
731
|
-
return createFilteredCollection(filteredItems, collection);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
/**
|
|
735
|
-
* Create a filtered collection wrapper.
|
|
736
|
-
*/
|
|
737
|
-
function createFilteredCollection<T>(
|
|
738
|
-
items: CollectionNode<T>[],
|
|
739
|
-
original: Collection<T>,
|
|
740
|
-
): Collection<T> {
|
|
741
|
-
const itemMap = new Map<Key, CollectionNode<T>>();
|
|
742
|
-
|
|
743
|
-
for (const item of items) {
|
|
744
|
-
itemMap.set(item.key, item);
|
|
745
|
-
if (item.childNodes) {
|
|
746
|
-
for (const child of item.childNodes) {
|
|
747
|
-
itemMap.set(child.key, child);
|
|
1055
|
+
filteredItems.push({ ...item, index: index++ });
|
|
748
1056
|
}
|
|
1057
|
+
} else {
|
|
1058
|
+
filteredItems.push({ ...item });
|
|
749
1059
|
}
|
|
750
1060
|
}
|
|
751
1061
|
|
|
752
|
-
return
|
|
753
|
-
get size() {
|
|
754
|
-
let count = 0;
|
|
755
|
-
for (const item of items) {
|
|
756
|
-
if (item.type === "item") {
|
|
757
|
-
count++;
|
|
758
|
-
} else if (item.childNodes) {
|
|
759
|
-
count += Array.from(item.childNodes).filter((c) => c.type === "item").length;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
return count;
|
|
763
|
-
},
|
|
764
|
-
getItem(key: Key) {
|
|
765
|
-
return itemMap.get(key) ?? null;
|
|
766
|
-
},
|
|
767
|
-
getKeys() {
|
|
768
|
-
return itemMap.keys();
|
|
769
|
-
},
|
|
770
|
-
getFirstKey() {
|
|
771
|
-
for (const item of items) {
|
|
772
|
-
if (item.type === "item") return item.key;
|
|
773
|
-
if (item.childNodes) {
|
|
774
|
-
for (const child of item.childNodes) {
|
|
775
|
-
if (child.type === "item") return child.key;
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
return null;
|
|
780
|
-
},
|
|
781
|
-
getLastKey() {
|
|
782
|
-
for (let i = items.length - 1; i >= 0; i--) {
|
|
783
|
-
const item = items[i];
|
|
784
|
-
if (item.type === "item") return item.key;
|
|
785
|
-
if (item.childNodes) {
|
|
786
|
-
const children = Array.from(item.childNodes);
|
|
787
|
-
for (let j = children.length - 1; j >= 0; j--) {
|
|
788
|
-
if (children[j].type === "item") return children[j].key;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
return null;
|
|
793
|
-
},
|
|
794
|
-
getKeyBefore(key: Key) {
|
|
795
|
-
return original.getKeyBefore(key);
|
|
796
|
-
},
|
|
797
|
-
getKeyAfter(key: Key) {
|
|
798
|
-
return original.getKeyAfter(key);
|
|
799
|
-
},
|
|
800
|
-
at(index: number) {
|
|
801
|
-
// Flatten items for indexing
|
|
802
|
-
let currentIndex = 0;
|
|
803
|
-
for (const item of items) {
|
|
804
|
-
if (item.type === "item") {
|
|
805
|
-
if (currentIndex === index) return item;
|
|
806
|
-
currentIndex++;
|
|
807
|
-
} else if (item.childNodes) {
|
|
808
|
-
for (const child of item.childNodes) {
|
|
809
|
-
if (child.type === "item") {
|
|
810
|
-
if (currentIndex === index) return child;
|
|
811
|
-
currentIndex++;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
return null;
|
|
817
|
-
},
|
|
818
|
-
getChildren(key: Key) {
|
|
819
|
-
const item = itemMap.get(key);
|
|
820
|
-
return item?.childNodes ?? [];
|
|
821
|
-
},
|
|
822
|
-
getTextValue(key: Key) {
|
|
823
|
-
const item = itemMap.get(key);
|
|
824
|
-
return item?.textValue ?? "";
|
|
825
|
-
},
|
|
826
|
-
[Symbol.iterator]() {
|
|
827
|
-
return items[Symbol.iterator]();
|
|
828
|
-
},
|
|
829
|
-
};
|
|
1062
|
+
return new ListCollection(filteredItems);
|
|
830
1063
|
}
|