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