mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-08 13:05:08 +09:00
Merge branch 'main' into kof-patch-1
This commit is contained in:
6 files changed
+74
-45
No files matched your search
@@ -65,6 +65,7 @@ const Combobox = ({
|
||||
} = useCombobox({
|
||||
items: itemsProp,
|
||||
value,
|
||||
selectedItem: value,
|
||||
itemToString: (item) => item ?? "",
|
||||
onItemSelect,
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ export const TextControl = ({
|
||||
|
||||
const setValue = setProperty(styleConfig.property);
|
||||
|
||||
const [currentValue, setCurrentValue] = useState<
|
||||
const [intermediateValue, setIntermediateValue] = useState<
|
||||
StyleValue | IntermediateStyleValue
|
||||
>();
|
||||
|
||||
@@ -37,23 +37,24 @@ export const TextControl = ({
|
||||
<CssValueInput
|
||||
icon={icon}
|
||||
property={styleConfig.property}
|
||||
value={currentValue ?? value}
|
||||
value={value}
|
||||
intermediateValue={intermediateValue}
|
||||
keywords={styleConfig.items.map((item) => ({
|
||||
type: "keyword",
|
||||
value: item.name,
|
||||
}))}
|
||||
onChange={(styleValue) => {
|
||||
setCurrentValue(styleValue);
|
||||
setIntermediateValue(styleValue);
|
||||
if (styleValue.type !== "intermediate") {
|
||||
setValue(styleValue, { isEphemeral: true });
|
||||
}
|
||||
}}
|
||||
onPreview={(styleValue) => {
|
||||
onHighlight={(styleValue) => {
|
||||
setValue(styleValue, { isEphemeral: true });
|
||||
}}
|
||||
onChangeComplete={(styleValue) => {
|
||||
setValue(styleValue);
|
||||
setCurrentValue(undefined);
|
||||
setIntermediateValue(undefined);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
+43
-31
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import * as React from "react";
|
||||
import { Flex, TextField } from "@webstudio-is/design-system";
|
||||
import type { StyleValue } from "@webstudio-is/css-data";
|
||||
import { CssValueInput, type IntermediateStyleValue } from "./css-value-input";
|
||||
@@ -10,17 +10,20 @@ export default {
|
||||
};
|
||||
|
||||
export const WithKeywords = () => {
|
||||
const [value, setValue] = React.useState<StyleValue | IntermediateStyleValue>(
|
||||
{
|
||||
type: "keyword",
|
||||
value: "auto",
|
||||
}
|
||||
);
|
||||
const [value, setValue] = React.useState<StyleValue>({
|
||||
type: "keyword",
|
||||
value: "auto",
|
||||
});
|
||||
|
||||
const [intermediateValue, setIntermediateValue] = React.useState<
|
||||
StyleValue | IntermediateStyleValue
|
||||
>();
|
||||
|
||||
return (
|
||||
<CssValueInput
|
||||
property="width"
|
||||
value={value}
|
||||
intermediateValue={intermediateValue}
|
||||
keywords={[
|
||||
{ type: "keyword", value: "auto" },
|
||||
{ type: "keyword", value: "min-content" },
|
||||
@@ -28,14 +31,15 @@ export const WithKeywords = () => {
|
||||
{ type: "keyword", value: "fit-content" },
|
||||
]}
|
||||
onChange={(value) => {
|
||||
setValue(value);
|
||||
setIntermediateValue(value);
|
||||
}}
|
||||
onPreview={(value) => {
|
||||
action("onPreview")(value);
|
||||
onHighlight={(value) => {
|
||||
action("onHighlight")(value);
|
||||
}}
|
||||
onChangeComplete={(newValue) => {
|
||||
// on blur, select, enter etc.
|
||||
setValue(newValue);
|
||||
setIntermediateValue(undefined);
|
||||
action("onChangeComplete")(newValue);
|
||||
}}
|
||||
/>
|
||||
@@ -43,17 +47,20 @@ export const WithKeywords = () => {
|
||||
};
|
||||
|
||||
export const WithIcons = () => {
|
||||
const [value, setValue] = React.useState<StyleValue | IntermediateStyleValue>(
|
||||
{
|
||||
type: "keyword",
|
||||
value: "space-around",
|
||||
}
|
||||
);
|
||||
const [value, setValue] = React.useState<StyleValue>({
|
||||
type: "keyword",
|
||||
value: "space-around",
|
||||
});
|
||||
|
||||
const [intermediateValue, setIntermediateValue] = React.useState<
|
||||
StyleValue | IntermediateStyleValue
|
||||
>();
|
||||
|
||||
return (
|
||||
<CssValueInput
|
||||
property="alignItems"
|
||||
value={value}
|
||||
intermediateValue={intermediateValue}
|
||||
keywords={[
|
||||
{ type: "keyword", value: "normal" },
|
||||
{ type: "keyword", value: "start" },
|
||||
@@ -64,14 +71,15 @@ export const WithIcons = () => {
|
||||
{ type: "keyword", value: "space-between" },
|
||||
]}
|
||||
onChange={(newValue) => {
|
||||
setValue(newValue);
|
||||
setIntermediateValue(newValue);
|
||||
}}
|
||||
onPreview={(value) => {
|
||||
action("onPreview")(value);
|
||||
onHighlight={(value) => {
|
||||
action("onHighlight")(value);
|
||||
}}
|
||||
onChangeComplete={(newValue) => {
|
||||
// on blur, select, enter etc.
|
||||
setValue(newValue);
|
||||
setIntermediateValue(undefined);
|
||||
action("onChangeComplete")(newValue);
|
||||
}}
|
||||
/>
|
||||
@@ -79,19 +87,22 @@ export const WithIcons = () => {
|
||||
};
|
||||
|
||||
export const WithUnits = () => {
|
||||
const [value, setValue] = React.useState<StyleValue | IntermediateStyleValue>(
|
||||
{
|
||||
type: "unit",
|
||||
value: 100,
|
||||
unit: "px",
|
||||
}
|
||||
);
|
||||
const [value, setValue] = React.useState<StyleValue>({
|
||||
type: "unit",
|
||||
value: 100,
|
||||
unit: "px",
|
||||
});
|
||||
|
||||
const [intermediateValue, setIntermediateValue] = React.useState<
|
||||
StyleValue | IntermediateStyleValue
|
||||
>();
|
||||
|
||||
return (
|
||||
<Flex css={{ gap: "$spacing$9" }}>
|
||||
<CssValueInput
|
||||
property="rowGap"
|
||||
value={value}
|
||||
intermediateValue={intermediateValue}
|
||||
keywords={[
|
||||
{ type: "keyword", value: "auto" },
|
||||
{ type: "keyword", value: "min-content" },
|
||||
@@ -99,14 +110,15 @@ export const WithUnits = () => {
|
||||
{ type: "keyword", value: "fit-content" },
|
||||
]}
|
||||
onChange={(newValue) => {
|
||||
setValue(newValue);
|
||||
setIntermediateValue(newValue);
|
||||
}}
|
||||
onPreview={(value) => {
|
||||
action("onPreview")(value);
|
||||
onHighlight={(value) => {
|
||||
action("onHighlight")(value);
|
||||
}}
|
||||
onChangeComplete={(newValue) => {
|
||||
// on blur, select, enter etc.
|
||||
setValue(newValue);
|
||||
setIntermediateValue(undefined);
|
||||
action("onChangeComplete")(newValue);
|
||||
}}
|
||||
/>
|
||||
@@ -114,8 +126,8 @@ export const WithUnits = () => {
|
||||
readOnly
|
||||
value={
|
||||
value
|
||||
? value?.type === "intermediate"
|
||||
? value.value + value.unit
|
||||
? intermediateValue?.type === "intermediate"
|
||||
? intermediateValue.value + intermediateValue.unit
|
||||
: toValue(value)
|
||||
: ""
|
||||
}
|
||||
|
||||
+12
-6
@@ -186,11 +186,15 @@ type CssValueInputValue = StyleValue | IntermediateStyleValue;
|
||||
|
||||
type CssValueInputProps = {
|
||||
property: StyleProperty;
|
||||
value?: CssValueInputValue;
|
||||
value: StyleValue | undefined;
|
||||
intermediateValue: CssValueInputValue | undefined;
|
||||
/**
|
||||
* Selected item in the dropdown
|
||||
*/
|
||||
keywords?: Array<KeywordValue>;
|
||||
onChange: (value: CssValueInputValue) => void;
|
||||
onChangeComplete: (value: StyleValue) => void;
|
||||
onPreview: (value: StyleValue) => void;
|
||||
onHighlight: (value: StyleValue) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -227,11 +231,12 @@ type CssValueInputProps = {
|
||||
export const CssValueInput = ({
|
||||
icon,
|
||||
property,
|
||||
value = unsetValue,
|
||||
keywords = [],
|
||||
onPreview,
|
||||
onHighlight,
|
||||
...props
|
||||
}: CssValueInputProps & { icon?: JSX.Element }) => {
|
||||
const value = props.intermediateValue ?? props.value ?? unsetValue;
|
||||
|
||||
const onChange = (input: string) => {
|
||||
// We don't know what's inside the input,
|
||||
// preserve current unit value if exists
|
||||
@@ -263,6 +268,7 @@ export const CssValueInput = ({
|
||||
} = useCombobox<CssValueInputValue>({
|
||||
items: keywords,
|
||||
value,
|
||||
selectedItem: props.value,
|
||||
itemToString: (item) =>
|
||||
item === null
|
||||
? ""
|
||||
@@ -277,11 +283,11 @@ export const CssValueInput = ({
|
||||
},
|
||||
onItemHighlight: (value) => {
|
||||
if (value == null) {
|
||||
onPreview(unsetValue);
|
||||
onHighlight(unsetValue);
|
||||
return;
|
||||
}
|
||||
if (value.type !== "intermediate") {
|
||||
onPreview(value ?? unsetValue);
|
||||
onHighlight(value ?? unsetValue);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ export const Simple: ComponentStory<typeof Combobox> = () => {
|
||||
placeholder="Select a fruit"
|
||||
items={items}
|
||||
value={value}
|
||||
selectedItem={value}
|
||||
onItemSelect={(value) => {
|
||||
// If the value is cleared, we revert to previous state
|
||||
if (value !== null) {
|
||||
@@ -70,6 +71,7 @@ export const Complex: ComponentStory<typeof Combobox> = () => {
|
||||
useCombobox({
|
||||
items: ["Apple", "Banana", "Orange"],
|
||||
value,
|
||||
selectedItem: value,
|
||||
itemToString: (item) => item ?? "",
|
||||
stateReducer,
|
||||
onItemSelect: (value) => {
|
||||
|
||||
@@ -134,6 +134,7 @@ type UseComboboxProps<Item> = UseDownshiftComboboxProps<Item> & {
|
||||
items: Array<Item>;
|
||||
itemToString: (item: Item | null) => string;
|
||||
value: Item | null; // This is to prevent: "downshift: A component has changed the uncontrolled prop "selectedItem" to be controlled."
|
||||
selectedItem: Item | undefined;
|
||||
onInputChange?: (value: string | undefined) => void;
|
||||
onItemSelect?: (value: Item | null) => void;
|
||||
onItemHighlight?: (value: Item | null) => void;
|
||||
@@ -149,6 +150,7 @@ export const comboboxStateChangeTypes = useDownshiftCombobox.stateChangeTypes;
|
||||
export const useCombobox = <Item,>({
|
||||
items,
|
||||
value,
|
||||
selectedItem,
|
||||
itemToString,
|
||||
onInputChange,
|
||||
onItemSelect,
|
||||
@@ -167,9 +169,10 @@ export const useCombobox = <Item,>({
|
||||
...rest,
|
||||
items: filteredItems,
|
||||
defaultHighlightedIndex: -1,
|
||||
selectedItem: value, // Prevent downshift warning about switching controlled mode
|
||||
selectedItem: selectedItem ?? null, // Prevent downshift warning about switching controlled mode
|
||||
stateReducer,
|
||||
itemToString,
|
||||
inputValue: value ? itemToString(value) : undefined,
|
||||
onInputValueChange({ inputValue, type }) {
|
||||
if (type === comboboxStateChangeTypes.InputChange) {
|
||||
filter(inputValue);
|
||||
@@ -186,7 +189,7 @@ export const useCombobox = <Item,>({
|
||||
},
|
||||
});
|
||||
|
||||
const { isOpen, getItemProps, highlightedIndex, selectedItem, getMenuProps } =
|
||||
const { isOpen, getItemProps, highlightedIndex, getMenuProps } =
|
||||
downshiftProps;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -200,7 +203,9 @@ export const useCombobox = <Item,>({
|
||||
return getItemProps({
|
||||
highlighted: highlightedIndex === options.index,
|
||||
// We need to either deep compare objects here or use itemToString to get primitive types
|
||||
selected: itemToString(selectedItem) === itemToString(options.item),
|
||||
selected:
|
||||
selectedItem !== undefined &&
|
||||
itemToString(selectedItem) === itemToString(options.item),
|
||||
key: options.id,
|
||||
...options,
|
||||
});
|
||||
@@ -238,6 +243,7 @@ export const Combobox = <Item,>({
|
||||
items,
|
||||
value = null,
|
||||
name,
|
||||
selectedItem,
|
||||
placeholder,
|
||||
itemToString,
|
||||
onItemSelect,
|
||||
@@ -254,6 +260,7 @@ export const Combobox = <Item,>({
|
||||
} = useCombobox({
|
||||
items,
|
||||
value,
|
||||
selectedItem,
|
||||
itemToString,
|
||||
onItemSelect,
|
||||
onItemHighlight,
|
||||
|
||||
Reference in new issue
Block a user