mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-06 05:21:10 +08:00
Support removing the root node
This commit is contained in:
parent
63d32842e6
commit
10dc235f9b
@ -207,38 +207,43 @@ export const ObjectControl: React.FC<ObjectProps> = ({ name, value = {}, onChang
|
|||||||
},
|
},
|
||||||
[onChange]
|
[onChange]
|
||||||
);
|
);
|
||||||
|
const rawJSONForm = (
|
||||||
|
<Form.Textarea
|
||||||
|
id={name}
|
||||||
|
name={name}
|
||||||
|
defaultValue={value}
|
||||||
|
onBlur={(event) => updateRaw(event.target.value)}
|
||||||
|
size="flex"
|
||||||
|
placeholder="Enter JSON string"
|
||||||
|
valid={parseError ? 'error' : null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<JsonTree
|
{data ? (
|
||||||
data={data}
|
<JsonTree
|
||||||
rootName={name}
|
data={data}
|
||||||
onFullyUpdate={onChange}
|
rootName={name}
|
||||||
getStyle={getCustomStyleFunction(useTheme())}
|
onFullyUpdate={onChange}
|
||||||
cancelButtonElement={<Button type="button">Cancel</Button>}
|
getStyle={getCustomStyleFunction(useTheme())}
|
||||||
editButtonElement={<Button type="submit">Save</Button>}
|
cancelButtonElement={<Button type="button">Cancel</Button>}
|
||||||
addButtonElement={
|
editButtonElement={<Button type="submit">Save</Button>}
|
||||||
<Button type="submit" primary>
|
addButtonElement={
|
||||||
Save
|
<Button type="submit" primary>
|
||||||
</Button>
|
Save
|
||||||
}
|
</Button>
|
||||||
plusMenuElement={<ActionIcon icon="add" />}
|
}
|
||||||
minusMenuElement={<ActionIcon icon="subtract" />}
|
plusMenuElement={<ActionIcon icon="add" />}
|
||||||
inputElement={(_: any, __: any, ___: any, key: string) =>
|
minusMenuElement={<ActionIcon icon="subtract" />}
|
||||||
key ? <Input onFocus={selectValue} onBlur={dispatchEnterKey} /> : <Input />
|
inputElement={(_: any, __: any, ___: any, key: string) =>
|
||||||
}
|
key ? <Input onFocus={selectValue} onBlur={dispatchEnterKey} /> : <Input />
|
||||||
fallback={
|
}
|
||||||
<Form.Textarea
|
fallback={rawJSONForm}
|
||||||
id={name}
|
/>
|
||||||
name={name}
|
) : (
|
||||||
defaultValue={value}
|
rawJSONForm
|
||||||
onBlur={(event) => updateRaw(event.target.value)}
|
)}
|
||||||
size="flex"
|
|
||||||
placeholder="Enter JSON string"
|
|
||||||
valid={parseError ? 'error' : null}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -166,7 +166,6 @@ class JsonArray extends Component {
|
|||||||
const { handleRemove, readOnly, getStyle, dataType, minusMenuElement } = this.props;
|
const { handleRemove, readOnly, getStyle, dataType, minusMenuElement } = this.props;
|
||||||
const { minus, collapsed } = getStyle(name, data, keyPath, deep, dataType);
|
const { minus, collapsed } = getStyle(name, data, keyPath, deep, dataType);
|
||||||
|
|
||||||
const isRoot = deep === -1;
|
|
||||||
const isReadOnly = readOnly(name, data, keyPath, deep, dataType);
|
const isReadOnly = readOnly(name, data, keyPath, deep, dataType);
|
||||||
|
|
||||||
const removeItemButton = React.cloneElement(minusMenuElement, {
|
const removeItemButton = React.cloneElement(minusMenuElement, {
|
||||||
@ -181,7 +180,7 @@ class JsonArray extends Component {
|
|||||||
<span className="rejt-collapsed-text" style={collapsed} onClick={this.handleCollapseMode}>
|
<span className="rejt-collapsed-text" style={collapsed} onClick={this.handleCollapseMode}>
|
||||||
[...] {data.length} {data.length === 1 ? 'item' : 'items'}
|
[...] {data.length} {data.length === 1 ? 'item' : 'items'}
|
||||||
</span>
|
</span>
|
||||||
{!isRoot && !isReadOnly && removeItemButton}
|
{!isReadOnly && removeItemButton}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
@ -210,7 +209,7 @@ class JsonArray extends Component {
|
|||||||
onSubmitValueParser,
|
onSubmitValueParser,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { minus, plus, delimiter, ul, addForm } = getStyle(name, data, keyPath, deep, dataType);
|
const { minus, plus, delimiter, ul, addForm } = getStyle(name, data, keyPath, deep, dataType);
|
||||||
const isRoot = deep === -1;
|
|
||||||
const isReadOnly = readOnly(name, data, keyPath, deep, dataType);
|
const isReadOnly = readOnly(name, data, keyPath, deep, dataType);
|
||||||
|
|
||||||
const addItemButton = React.cloneElement(plusMenuElement, {
|
const addItemButton = React.cloneElement(plusMenuElement, {
|
||||||
@ -281,7 +280,7 @@ class JsonArray extends Component {
|
|||||||
<span className="rejt-not-collapsed-delimiter" style={delimiter}>
|
<span className="rejt-not-collapsed-delimiter" style={delimiter}>
|
||||||
{endObject}
|
{endObject}
|
||||||
</span>
|
</span>
|
||||||
{!isReadOnly && !isRoot && removeItemButton}
|
{!isReadOnly && removeItemButton}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,6 @@ class JsonObject extends Component {
|
|||||||
const { minus, collapsed } = getStyle(name, data, keyPath, deep, dataType);
|
const { minus, collapsed } = getStyle(name, data, keyPath, deep, dataType);
|
||||||
const keyList = Object.getOwnPropertyNames(data);
|
const keyList = Object.getOwnPropertyNames(data);
|
||||||
|
|
||||||
const isRoot = deep === -1;
|
|
||||||
const isReadOnly = readOnly(name, data, keyPath, deep, dataType);
|
const isReadOnly = readOnly(name, data, keyPath, deep, dataType);
|
||||||
|
|
||||||
const removeItemButton = React.cloneElement(minusMenuElement, {
|
const removeItemButton = React.cloneElement(minusMenuElement, {
|
||||||
@ -182,7 +181,7 @@ class JsonObject extends Component {
|
|||||||
<span className="rejt-collapsed-text" style={collapsed} onClick={this.handleCollapseMode}>
|
<span className="rejt-collapsed-text" style={collapsed} onClick={this.handleCollapseMode}>
|
||||||
{'{...}'} {keyList.length} {keyList.length === 1 ? 'key' : 'keys'}
|
{'{...}'} {keyList.length} {keyList.length === 1 ? 'key' : 'keys'}
|
||||||
</span>
|
</span>
|
||||||
{!isRoot && !isReadOnly && removeItemButton}
|
{!isReadOnly && removeItemButton}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
@ -214,7 +213,6 @@ class JsonObject extends Component {
|
|||||||
const { minus, plus, addForm, ul, delimiter } = getStyle(name, data, keyPath, deep, dataType);
|
const { minus, plus, addForm, ul, delimiter } = getStyle(name, data, keyPath, deep, dataType);
|
||||||
const keyList = Object.getOwnPropertyNames(data);
|
const keyList = Object.getOwnPropertyNames(data);
|
||||||
|
|
||||||
const isRoot = deep === -1;
|
|
||||||
const isReadOnly = readOnly(name, data, keyPath, deep, dataType);
|
const isReadOnly = readOnly(name, data, keyPath, deep, dataType);
|
||||||
|
|
||||||
const addItemButton = React.cloneElement(plusMenuElement, {
|
const addItemButton = React.cloneElement(plusMenuElement, {
|
||||||
@ -286,7 +284,7 @@ class JsonObject extends Component {
|
|||||||
<span className="rejt-not-collapsed-delimiter" style={delimiter}>
|
<span className="rejt-not-collapsed-delimiter" style={delimiter}>
|
||||||
{endObject}
|
{endObject}
|
||||||
</span>
|
</span>
|
||||||
{!isRoot && !isReadOnly && removeItemButton}
|
{!isReadOnly && removeItemButton}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,9 @@ class JsonValue extends Component {
|
|||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
const { editEnabled, inputRef, name, value, keyPath, deep } = this.state;
|
const { editEnabled, inputRef, name, value, keyPath, deep } = this.state;
|
||||||
const { readOnly, dataType } = this.props;
|
const { readOnly, dataType } = this.props;
|
||||||
const readOnlyResult = readOnly(name, value, keyPath, deep, dataType);
|
const isReadOnly = readOnly(name, value, keyPath, deep, dataType);
|
||||||
|
|
||||||
if (editEnabled && !readOnlyResult && typeof inputRef.focus === 'function') {
|
if (editEnabled && !isReadOnly && typeof inputRef.focus === 'function') {
|
||||||
inputRef.focus();
|
inputRef.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,65 +114,54 @@ class JsonValue extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const style = getStyle(name, originalValue, keyPath, deep, dataType);
|
const style = getStyle(name, originalValue, keyPath, deep, dataType);
|
||||||
let result = null;
|
const isReadOnly = readOnly(name, originalValue, keyPath, deep, dataType);
|
||||||
let minusElement = null;
|
const isEditing = editEnabled && !isReadOnly;
|
||||||
const readOnlyResult = readOnly(name, originalValue, keyPath, deep, dataType);
|
const inputElement = inputElementGenerator(
|
||||||
|
inputUsageTypes.VALUE,
|
||||||
|
comeFromKeyPath,
|
||||||
|
deep,
|
||||||
|
name,
|
||||||
|
originalValue,
|
||||||
|
dataType
|
||||||
|
);
|
||||||
|
|
||||||
if (editEnabled && !readOnlyResult) {
|
const editButtonElementLayout = React.cloneElement(editButtonElement, {
|
||||||
const inputElement = inputElementGenerator(
|
onClick: this.handleEdit,
|
||||||
inputUsageTypes.VALUE,
|
});
|
||||||
comeFromKeyPath,
|
const cancelButtonElementLayout = React.cloneElement(cancelButtonElement, {
|
||||||
deep,
|
onClick: this.handleCancelEdit,
|
||||||
name,
|
});
|
||||||
originalValue,
|
const inputElementLayout = React.cloneElement(inputElement, {
|
||||||
dataType
|
ref: this.refInput,
|
||||||
);
|
defaultValue: JSON.stringify(originalValue),
|
||||||
|
});
|
||||||
const editButtonElementLayout = React.cloneElement(editButtonElement, {
|
const minusMenuLayout = React.cloneElement(minusMenuElement, {
|
||||||
onClick: this.handleEdit,
|
onClick: handleRemove,
|
||||||
});
|
className: 'rejt-minus-menu',
|
||||||
const cancelButtonElementLayout = React.cloneElement(cancelButtonElement, {
|
style: style.minus,
|
||||||
onClick: this.handleCancelEdit,
|
});
|
||||||
});
|
|
||||||
const inputElementLayout = React.cloneElement(inputElement, {
|
|
||||||
ref: this.refInput,
|
|
||||||
defaultValue: JSON.stringify(originalValue),
|
|
||||||
});
|
|
||||||
|
|
||||||
result = (
|
|
||||||
<span className="rejt-edit-form" style={style.editForm}>
|
|
||||||
{inputElementLayout} {cancelButtonElementLayout}
|
|
||||||
{editButtonElementLayout}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
minusElement = null;
|
|
||||||
} else {
|
|
||||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
|
||||||
result = (
|
|
||||||
<span
|
|
||||||
className="rejt-value"
|
|
||||||
style={style.value}
|
|
||||||
onClick={readOnlyResult ? null : this.handleEditMode}
|
|
||||||
>
|
|
||||||
{String(value)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
/* eslint-enable */
|
|
||||||
const minusMenuLayout = React.cloneElement(minusMenuElement, {
|
|
||||||
onClick: handleRemove,
|
|
||||||
className: 'rejt-minus-menu',
|
|
||||||
style: style.minus,
|
|
||||||
});
|
|
||||||
minusElement = readOnlyResult ? null : minusMenuLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className="rejt-value-node" style={style.li}>
|
<li className="rejt-value-node" style={style.li}>
|
||||||
<span className="rejt-name" style={style.name}>
|
<span className="rejt-name" style={style.name}>
|
||||||
{name} :{' '}
|
{name}
|
||||||
|
{' : '}
|
||||||
</span>
|
</span>
|
||||||
{result}
|
{isEditing ? (
|
||||||
{minusElement}
|
<span className="rejt-edit-form" style={style.editForm}>
|
||||||
|
{inputElementLayout} {cancelButtonElementLayout}
|
||||||
|
{editButtonElementLayout}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span
|
||||||
|
className="rejt-value"
|
||||||
|
style={style.value}
|
||||||
|
onClick={isReadOnly ? null : this.handleEditMode}
|
||||||
|
>
|
||||||
|
{String(value)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{!isReadOnly && !isEditing && minusMenuLayout}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ class JsonTree extends Component {
|
|||||||
};
|
};
|
||||||
// Bind
|
// Bind
|
||||||
this.onUpdate = this.onUpdate.bind(this);
|
this.onUpdate = this.onUpdate.bind(this);
|
||||||
|
this.removeRoot = this.removeRoot.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props, state) {
|
static getDerivedStateFromProps(props, state) {
|
||||||
@ -35,6 +36,10 @@ class JsonTree extends Component {
|
|||||||
this.props.onFullyUpdate(data);
|
this.props.onFullyUpdate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeRoot() {
|
||||||
|
this.onUpdate(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { data, rootName } = this.state;
|
const { data, rootName } = this.state;
|
||||||
const {
|
const {
|
||||||
@ -59,7 +64,7 @@ class JsonTree extends Component {
|
|||||||
|
|
||||||
// Node type
|
// Node type
|
||||||
const dataType = getObjectType(data);
|
const dataType = getObjectType(data);
|
||||||
let node = null;
|
|
||||||
let readOnlyFunction = readOnly;
|
let readOnlyFunction = readOnly;
|
||||||
if (getObjectType(readOnly) === 'Boolean') {
|
if (getObjectType(readOnly) === 'Boolean') {
|
||||||
readOnlyFunction = () => readOnly;
|
readOnlyFunction = () => readOnly;
|
||||||
@ -93,6 +98,7 @@ class JsonTree extends Component {
|
|||||||
textareaElementGenerator={textareaElementFunction}
|
textareaElementGenerator={textareaElementFunction}
|
||||||
minusMenuElement={minusMenuElement}
|
minusMenuElement={minusMenuElement}
|
||||||
plusMenuElement={plusMenuElement}
|
plusMenuElement={plusMenuElement}
|
||||||
|
handleRemove={this.removeRoot}
|
||||||
beforeRemoveAction={beforeRemoveAction}
|
beforeRemoveAction={beforeRemoveAction}
|
||||||
beforeAddAction={beforeAddAction}
|
beforeAddAction={beforeAddAction}
|
||||||
beforeUpdateAction={beforeUpdateAction}
|
beforeUpdateAction={beforeUpdateAction}
|
||||||
|
@ -41,7 +41,7 @@ const deepDiff = (value: any, update: any): any => {
|
|||||||
if (deepEqual(value, update)) return undefined;
|
if (deepEqual(value, update)) return undefined;
|
||||||
if (typeof value !== typeof update) return update;
|
if (typeof value !== typeof update) return update;
|
||||||
if (Array.isArray(value)) return update;
|
if (Array.isArray(value)) return update;
|
||||||
if (typeof update === 'object') {
|
if (update && typeof update === 'object') {
|
||||||
return Object.keys(update).reduce((acc, key) => {
|
return Object.keys(update).reduce((acc, key) => {
|
||||||
const diff = deepDiff(value[key], update[key]);
|
const diff = deepDiff(value[key], update[key]);
|
||||||
return diff === undefined ? acc : Object.assign(acc, { [key]: diff });
|
return diff === undefined ? acc : Object.assign(acc, { [key]: diff });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user