Skip to content

Commit

Permalink
add allowsCustomValue
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexErrant committed Oct 4, 2024
1 parent 7789aa5 commit f08daa2
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 11 deletions.
26 changes: 16 additions & 10 deletions packages/core/src/combobox/combobox-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
type Accessor,
type Component,
type JSX,
type Setter,
type ValidComponent,
createEffect,
createMemo,
Expand Down Expand Up @@ -158,6 +159,9 @@ export interface ComboboxBaseOptions<Option, OptGroup = never>
/** Property name that refers to the children options of an option group. */
optionGroupChildren?: keyof Exclude<OptGroup, null>;

/** Whether the combobox allows a non-item matching input value to be set. */
allowsCustomValue?: boolean;

/** An optional keyboard delegate to override the default. */
keyboardDelegate?: KeyboardDelegate;

Expand Down Expand Up @@ -301,6 +305,7 @@ export function ComboboxBase<
const [local, popperProps, formControlProps, others] = splitProps(
mergedProps,
[
"allowsCustomValue",
"translations",
"itemComponent",
"sectionComponent",
Expand Down Expand Up @@ -368,6 +373,7 @@ export function ComboboxBase<

const [showAllOptions, setShowAllOptions] = createSignal(false);

const [options, setOptions] = createSignal(local.options!);
const [lastDisplayedOptions, setLastDisplayedOptions] = createSignal(
local.options!,
);
Expand Down Expand Up @@ -454,10 +460,10 @@ export function ComboboxBase<

// The combobox doesn't contains option groups.
if (optionGroupChildren == null) {
return local.options as Option[];
return options() as Option[];
}

return local.options!.flatMap(
return options().flatMap(
(item) =>
((item as any)[optionGroupChildren] as Option[]) ?? (item as Option),
);
Expand Down Expand Up @@ -488,11 +494,11 @@ export function ComboboxBase<

// The combobox doesn't contains option groups.
if (optionGroupChildren == null) {
return (local.options as Option[]).filter(filterFn);
return (options() as Option[]).filter(filterFn);
}

const filteredGroups: OptGroup[] = [];
for (const optGroup of local.options as OptGroup[]) {
for (const optGroup of options() as OptGroup[]) {
// Filter options of the group
const filteredChildrenOptions = (
(optGroup as any)[optionGroupChildren] as Option[]
Expand All @@ -513,7 +519,7 @@ export function ComboboxBase<
const displayedOptions = createMemo(() => {
if (disclosureState.isOpen()) {
if (showAllOptions()) {
return local.options!;
return options();
}
return filteredOptions();
}
Expand Down Expand Up @@ -605,7 +611,7 @@ export function ComboboxBase<
const showAllOptions = setShowAllOptions(triggerMode === "manual");

const hasOptions = showAllOptions
? local.options!.length > 0
? options().length > 0
: filteredOptions().length > 0;

// Don't open if there is no option.
Expand Down Expand Up @@ -738,15 +744,13 @@ export function ComboboxBase<
const prevShowAllOptions = prevInput[1];

setLastDisplayedOptions(
prevShowAllOptions ? local.options! : prevFilteredOptions,
prevShowAllOptions ? options() : prevFilteredOptions,
);
} else {
const filteredOptions = input[0];
const showAllOptions = input[1];

setLastDisplayedOptions(
showAllOptions ? local.options! : filteredOptions,
);
setLastDisplayedOptions(showAllOptions ? options() : filteredOptions);
}
}),
);
Expand Down Expand Up @@ -857,6 +861,8 @@ export function ComboboxBase<

const context: ComboboxContextValue = {
dataset,
allowsCustomValue: local.allowsCustomValue ?? false,
setOptions: setOptions as Setter<unknown[]>,
isOpen: disclosureState.isOpen,
isDisabled: () => formControlContext.isDisabled() ?? false,
isMultiple: () => access(local.selectionMode) === "multiple",
Expand Down
10 changes: 9 additions & 1 deletion packages/core/src/combobox/combobox-context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { type Accessor, type JSX, createContext, useContext } from "solid-js";
import {
type Accessor,
type JSX,
type Setter,
createContext,
useContext,
} from "solid-js";

import type { ListState } from "../list";
import type { CollectionNode } from "../primitives";
Expand Down Expand Up @@ -28,6 +34,8 @@ export interface ComboboxContextValue {
activeDescendant: Accessor<string | undefined>;
inputValue: Accessor<string | undefined>;
triggerMode: Accessor<ComboboxTriggerMode>;
setOptions: Setter<unknown[]>;
allowsCustomValue: boolean;
controlRef: Accessor<HTMLElement | undefined>;
inputRef: Accessor<HTMLInputElement | undefined>;
triggerRef: Accessor<HTMLElement | undefined>;
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/combobox/combobox-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ export function ComboboxInput<T extends ValidComponent = "input">(
selectionManager().select(focusedKey);
}
}
if (context.allowsCustomValue) {
const val = e.currentTarget.value;
context.setOptions((x) => [val, ...x]);
selectionManager().select(val);
}

break;
case "Tab":
Expand Down

0 comments on commit f08daa2

Please sign in to comment.