mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-09 05:26:24 +09:00
wip new interface
This commit is contained in:
1 parent
fe2a186a4b
commit
4f09beb691
2 files changed
+43
-51
No files matched your search
@@ -19,11 +19,10 @@ export const Simple: ComponentStory<typeof Combobox> = () => {
|
||||
name="fruit"
|
||||
options={options}
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
onOptionSelect={setValue}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomInput: ComponentStory<typeof Combobox> = () => {
|
||||
const options = [
|
||||
{ label: "Apple", value: "apple", disabled: true },
|
||||
@@ -37,8 +36,8 @@ export const CustomInput: ComponentStory<typeof Combobox> = () => {
|
||||
name="fruit"
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
onOptionHightlight={(option: Option) => {}}
|
||||
onOptionSelect={setValue}
|
||||
onOptionHighlight={(option) => {}}
|
||||
disclosure={(props) => (
|
||||
<TextField
|
||||
{...props}
|
||||
|
||||
@@ -4,47 +4,49 @@ import { TextField } from "./text-field";
|
||||
import { IconButton } from "./icon-button";
|
||||
import { MenuAnchor, Menu, MenuContent, MenuItem } from "./menu";
|
||||
|
||||
type Option = { label: string };
|
||||
type BaseOption = { label: string };
|
||||
|
||||
type ComboboxProps = ComponentProps<typeof TextField> & {
|
||||
options: Array<Option>;
|
||||
onValueSelect: (value: Option) => void;
|
||||
onValueEnter: (value: Option) => void;
|
||||
onItemEnter: (value: Option) => void;
|
||||
onItemLeave: (value: Option) => void;
|
||||
value: Option;
|
||||
const getTextValue = <Option extends BaseOption>(option?: Option) => {
|
||||
return option ? option.label : "";
|
||||
};
|
||||
|
||||
export const Combobox = ({
|
||||
type DisclosureProps = ComponentProps<typeof TextField>;
|
||||
|
||||
type ComboboxProps<Option> = {
|
||||
name: string;
|
||||
options: ReadonlyArray<Option>;
|
||||
value?: Option;
|
||||
onOptionSelect?: (value: Option) => void;
|
||||
onOptionHighlight?: (value?: Option) => void;
|
||||
disclosure?: (props: DisclosureProps) => JSX.Element;
|
||||
};
|
||||
|
||||
export const Combobox = <Option extends BaseOption>({
|
||||
options,
|
||||
onValueSelect,
|
||||
onValueEnter,
|
||||
onItemEnter,
|
||||
onItemLeave,
|
||||
value,
|
||||
css,
|
||||
...rest
|
||||
}: ComboboxProps) => {
|
||||
name,
|
||||
onOptionSelect,
|
||||
onOptionHighlight,
|
||||
disclosure = (props) => <TextField {...props} />,
|
||||
}: ComboboxProps<Option>) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [currentValue, setCurrentValue] = useState(value);
|
||||
const [textValue, setTextValue] = useState(getTextValue<Option>(value));
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentValue(value);
|
||||
setTextValue(getTextValue<Option>(value));
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<Menu open={isOpen} modal={true} onOpenChange={setIsOpen}>
|
||||
<MenuAnchor asChild>
|
||||
<TextField
|
||||
{...rest}
|
||||
value={currentValue}
|
||||
autoComplete="off"
|
||||
// @todo avoid hardcoding padding
|
||||
css={{ ...css, paddingRight: 30 }}
|
||||
onChange={(event) => {
|
||||
setCurrentValue(event.target.value);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
{disclosure({
|
||||
name,
|
||||
autoComplete: "off",
|
||||
value: textValue,
|
||||
onChange: (event) => {
|
||||
setTextValue(event.target.value);
|
||||
},
|
||||
onKeyDown: (event) => {
|
||||
switch (event.key) {
|
||||
case "ArrowDown":
|
||||
case "ArrowUp": {
|
||||
@@ -52,41 +54,32 @@ export const Combobox = ({
|
||||
break;
|
||||
}
|
||||
case "Enter": {
|
||||
onValueEnter(event.currentTarget.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
},
|
||||
})}
|
||||
<IconButton variant="ghost" size="1">
|
||||
<ChevronDownIcon />
|
||||
</IconButton>
|
||||
</MenuAnchor>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="1"
|
||||
// @todo avoid hardcoding margin
|
||||
css={{ marginLeft: -32 }}
|
||||
onClick={() => {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</IconButton>
|
||||
<MenuContent loop portalled asChild>
|
||||
<div>
|
||||
{options.map(({ label }, index) => {
|
||||
{options.map((option, index) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={index}
|
||||
onMouseEnter={() => {
|
||||
onItemEnter(label);
|
||||
// onOptionHighlight(option);
|
||||
}}
|
||||
onFocus={() => {
|
||||
onItemEnter(label);
|
||||
// onOptionHighlight(option);
|
||||
}}
|
||||
onSelect={() => {
|
||||
onValueSelect(label);
|
||||
//onOptionSelect(option);
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
{getTextValue<Option>(option)}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in new issue
Block a user