Merge branch 'main' into kof-patch-1

This commit is contained in:
Oleg Isonen authored and GitHub committed 2022-12-19 15:25:32 +00:00
commit ac42cbcb43
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>
@@ -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)
: ""
}
@@ -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,