mirror of
https://github.com/wassname/talk.git
synced 2026-07-10 12:07:32 +08:00
New Slot property passthrough
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { Children } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getShallowChanges } from 'coral-framework/utils';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
class IfSlotIsEmpty extends React.Component {
|
||||
static contextTypes = {
|
||||
@@ -9,9 +10,23 @@ class IfSlotIsEmpty extends React.Component {
|
||||
};
|
||||
|
||||
shouldComponentUpdate(next) {
|
||||
const changes = getShallowChanges(this.props, next);
|
||||
|
||||
// Handle special `passthrough` props.
|
||||
const passthroughIndex = changes.indexOf('passthrough');
|
||||
if (passthroughIndex !== -1) {
|
||||
if (!this.props.passthrough || next.passthrough) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
getShallowChanges(this.props.passthrough, next.passthrough).lenght === 0
|
||||
) {
|
||||
changes.splice(passthroughIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent Slot from rerendering when only reduxState has changed and
|
||||
// it does not result in a change.
|
||||
const changes = getShallowChanges(this.props, next);
|
||||
if (changes.length === 1 && changes[0] === 'reduxState') {
|
||||
return this.isSlotEmpty(this.props) !== this.isSlotEmpty(next);
|
||||
}
|
||||
@@ -20,19 +35,58 @@ class IfSlotIsEmpty extends React.Component {
|
||||
return changes.length !== 0;
|
||||
}
|
||||
|
||||
getPassthrough(props = this.props) {
|
||||
const slotProps = omit(props, [
|
||||
'slot',
|
||||
'children',
|
||||
'reduxState',
|
||||
'passthrough',
|
||||
'dispatch',
|
||||
]);
|
||||
|
||||
// @Deprecated
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (Object.keys(slotProps).length) {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
`IfSlotIsEmpty '${
|
||||
props.fill
|
||||
}' passing through unknown props is deprecated, please use 'passthrough' instead`,
|
||||
slotProps
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
}
|
||||
|
||||
if (props.passthrough) {
|
||||
return props.passthrough;
|
||||
}
|
||||
|
||||
if (props.queryData) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
`Slot '${
|
||||
props.fill
|
||||
}' property 'queryData' is deprecated, please use 'passthrough' instead`
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
return {
|
||||
...props.queryData,
|
||||
...slotProps,
|
||||
};
|
||||
}
|
||||
|
||||
return slotProps;
|
||||
}
|
||||
|
||||
isSlotEmpty(props = this.props) {
|
||||
const {
|
||||
slot,
|
||||
className: _a,
|
||||
reduxState,
|
||||
component: _b = 'div',
|
||||
children: _c,
|
||||
queryData,
|
||||
...rest
|
||||
} = props;
|
||||
const { slot, reduxState } = props;
|
||||
const passthrough = this.getPassthrough(props);
|
||||
const slots = Array.isArray(slot) ? slot : [slot];
|
||||
return slots.every(slot =>
|
||||
this.context.plugins.isSlotEmpty(slot, reduxState, rest, queryData)
|
||||
this.context.plugins.isSlotEmpty(slot, reduxState, passthrough)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,6 +98,8 @@ class IfSlotIsEmpty extends React.Component {
|
||||
|
||||
IfSlotIsEmpty.propTypes = {
|
||||
slot: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
children: PropTypes.node.isRequired,
|
||||
passthrough: PropTypes.object,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { Children } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getShallowChanges } from 'coral-framework/utils';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
class IfSlotIsNotEmpty extends React.Component {
|
||||
static contextTypes = {
|
||||
@@ -9,9 +10,23 @@ class IfSlotIsNotEmpty extends React.Component {
|
||||
};
|
||||
|
||||
shouldComponentUpdate(next) {
|
||||
const changes = getShallowChanges(this.props, next);
|
||||
|
||||
// Handle special `passthrough` props.
|
||||
const passthroughIndex = changes.indexOf('passthrough');
|
||||
if (passthroughIndex !== -1) {
|
||||
if (!this.props.passthrough || next.passthrough) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
getShallowChanges(this.props.passthrough, next.passthrough).lenght === 0
|
||||
) {
|
||||
changes.splice(passthroughIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent Slot from rerendering when only reduxState has changed and
|
||||
// it does not result in a change.
|
||||
const changes = getShallowChanges(this.props, next);
|
||||
if (changes.length === 1 && changes[0] === 'reduxState') {
|
||||
return this.isSlotEmpty(this.props) !== this.isSlotEmpty(next);
|
||||
}
|
||||
@@ -20,19 +35,58 @@ class IfSlotIsNotEmpty extends React.Component {
|
||||
return changes.length !== 0;
|
||||
}
|
||||
|
||||
getPassthrough(props = this.props) {
|
||||
const slotProps = omit(props, [
|
||||
'slot',
|
||||
'children',
|
||||
'reduxState',
|
||||
'passthrough',
|
||||
'dispatch',
|
||||
]);
|
||||
|
||||
// @Deprecated
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (Object.keys(slotProps).length) {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
`IfSlotIsEmpty '${
|
||||
props.fill
|
||||
}' passing through unknown props is deprecated, please use 'passthrough' instead`,
|
||||
slotProps
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
}
|
||||
|
||||
if (props.passthrough) {
|
||||
return props.passthrough;
|
||||
}
|
||||
|
||||
if (props.queryData) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
`Slot '${
|
||||
props.fill
|
||||
}' property 'queryData' is deprecated, please use 'passthrough' instead`
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
return {
|
||||
...props.queryData,
|
||||
...slotProps,
|
||||
};
|
||||
}
|
||||
|
||||
return slotProps;
|
||||
}
|
||||
|
||||
isSlotEmpty(props = this.props) {
|
||||
const {
|
||||
slot,
|
||||
className: _a,
|
||||
reduxState,
|
||||
component: _b = 'div',
|
||||
children: _c,
|
||||
queryData,
|
||||
...rest
|
||||
} = props;
|
||||
const { slot, reduxState } = props;
|
||||
const passthrough = this.getPassthrough(props);
|
||||
const slots = Array.isArray(slot) ? slot : [slot];
|
||||
return slots.every(slot =>
|
||||
this.context.plugins.isSlotEmpty(slot, reduxState, rest, queryData)
|
||||
this.context.plugins.isSlotEmpty(slot, reduxState, passthrough)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,6 +98,8 @@ class IfSlotIsNotEmpty extends React.Component {
|
||||
|
||||
IfSlotIsNotEmpty.propTypes = {
|
||||
slot: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
children: PropTypes.node.isRequired,
|
||||
passthrough: PropTypes.object,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
||||
@@ -20,6 +20,20 @@ class Slot extends React.Component {
|
||||
// Prevent Slot from rerendering when only reduxState has changed and
|
||||
// it does not result in a change of slot children.
|
||||
const changes = getShallowChanges(this.props, next);
|
||||
|
||||
// Handle special `passthrough` props.
|
||||
const passthroughIndex = changes.indexOf('passthrough');
|
||||
if (passthroughIndex !== -1) {
|
||||
if (!this.props.passthrough || next.passthrough) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
getShallowChanges(this.props.passthrough, next.passthrough).lenght === 0
|
||||
) {
|
||||
changes.splice(passthroughIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.length === 1 && changes[0] === 'reduxState') {
|
||||
const prevChildrenKeys = this.getChildren(this.props).map(
|
||||
child => child.key
|
||||
@@ -32,8 +46,8 @@ class Slot extends React.Component {
|
||||
return changes.length !== 0;
|
||||
}
|
||||
|
||||
getSlotProps(props = this.props) {
|
||||
return omit(props, [
|
||||
getPassthrough(props = this.props) {
|
||||
const slotProps = omit(props, [
|
||||
'fill',
|
||||
'inline',
|
||||
'className',
|
||||
@@ -43,7 +57,45 @@ class Slot extends React.Component {
|
||||
'queryData',
|
||||
'childFactory',
|
||||
'component',
|
||||
'passthrough',
|
||||
'dispatch',
|
||||
]);
|
||||
|
||||
// @Deprecated
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (Object.keys(slotProps).length) {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
`Slot '${
|
||||
props.fill
|
||||
}' passing through unknown props is deprecated, please use 'passthrough' instead`,
|
||||
slotProps
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
}
|
||||
|
||||
if (props.passthrough) {
|
||||
return props.passthrough;
|
||||
}
|
||||
|
||||
if (props.queryData) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
`Slot '${
|
||||
props.fill
|
||||
}' property 'queryData' is deprecated, please use 'passthrough' instead`
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
return {
|
||||
...props.queryData,
|
||||
...slotProps,
|
||||
};
|
||||
}
|
||||
|
||||
return slotProps;
|
||||
}
|
||||
|
||||
getChildren(props = this.props) {
|
||||
@@ -53,8 +105,7 @@ class Slot extends React.Component {
|
||||
return plugins.getSlotElements(
|
||||
props.fill,
|
||||
props.reduxState,
|
||||
this.getSlotProps(props),
|
||||
props.queryData,
|
||||
this.getPassthrough(props),
|
||||
{ size }
|
||||
);
|
||||
}
|
||||
@@ -67,7 +118,6 @@ class Slot extends React.Component {
|
||||
component: Component,
|
||||
childFactory,
|
||||
defaultComponent: DefaultComponent,
|
||||
queryData,
|
||||
fill,
|
||||
} = this.props;
|
||||
const { plugins } = this.context;
|
||||
@@ -78,8 +128,7 @@ class Slot extends React.Component {
|
||||
const props = plugins.getSlotComponentProps(
|
||||
DefaultComponent,
|
||||
reduxState,
|
||||
this.getSlotProps(this.props),
|
||||
queryData
|
||||
this.getPassthrough()
|
||||
);
|
||||
children = <DefaultComponent {...props} />;
|
||||
}
|
||||
@@ -125,8 +174,12 @@ Slot.propTypes = {
|
||||
component: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
||||
|
||||
// props coming from graphql must be passed through this property.
|
||||
// @Deprecated
|
||||
queryData: PropTypes.object,
|
||||
|
||||
// props that are passed to all Slot Components
|
||||
passthrough: PropTypes.object,
|
||||
|
||||
/**
|
||||
* You may need to apply reactive updates to a child as it is exiting.
|
||||
* This is generally done by using `cloneElement` however in the case of an exiting
|
||||
|
||||
@@ -67,39 +67,64 @@ function addMetaDataToSlotComponents(plugins) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* getSlotComponentProps calculate the props we would pass to the slot component.
|
||||
* query datas are only passed to the component if it is defined in `component.fragments`.
|
||||
*/
|
||||
function getSlotComponentProps(component, reduxState, props, queryData) {
|
||||
const pluginConfig = get(reduxState, 'config.plugin_config') || emptyConfig;
|
||||
return {
|
||||
...props,
|
||||
config: pluginConfig,
|
||||
...(component.fragments
|
||||
? pick(queryData, Object.keys(component.fragments))
|
||||
: withWarnings(component, queryData)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* splitProps detects props coming from the query and
|
||||
* returns `queryData` and `rest`.
|
||||
*/
|
||||
function splitProps(props) {
|
||||
const rest = { ...props };
|
||||
const queryData = {};
|
||||
if (props.passthrough) {
|
||||
Object.keys(props).forEach(k => {
|
||||
if (props[k].__unsafeFromQuery) {
|
||||
queryData[k] = props[k];
|
||||
delete rest[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
return { queryData, rest };
|
||||
}
|
||||
|
||||
class PluginsService {
|
||||
constructor(plugins) {
|
||||
this.plugins = plugins;
|
||||
addMetaDataToSlotComponents(plugins);
|
||||
}
|
||||
|
||||
isSlotEmpty(slot, reduxState, props = {}, queryData = {}) {
|
||||
return (
|
||||
this.getSlotElements(slot, reduxState, props, queryData).length === 0
|
||||
);
|
||||
isSlotEmpty(slot, reduxState, props = {}) {
|
||||
return this.getSlotElements(slot, reduxState, props).length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* getSlotComponentProps calculate the props we would pass to the slot component.
|
||||
* query datas are only passed to the component if it is defined in `component.fragments`.
|
||||
* Returns props that would pass to the given slot component.
|
||||
*/
|
||||
getSlotComponentProps(component, reduxState, props, queryData) {
|
||||
const pluginConfig = get(reduxState, 'config.plugin_config') || emptyConfig;
|
||||
return {
|
||||
...props,
|
||||
config: pluginConfig,
|
||||
...(component.fragments
|
||||
? pick(queryData, Object.keys(component.fragments))
|
||||
: withWarnings(component, queryData)),
|
||||
};
|
||||
getSlotComponentProps(component, reduxState, props) {
|
||||
const { queryData, rest } = splitProps(props);
|
||||
return getSlotComponentProps(component, reduxState, rest, queryData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns React Elements for given slot.
|
||||
*/
|
||||
getSlotElements(slot, reduxState, props = {}, queryData = {}, options = {}) {
|
||||
getSlotElements(slot, reduxState, props = {}, options = {}) {
|
||||
const pluginConfig = get(reduxState, 'config.plugin_config') || emptyConfig;
|
||||
const { size = 0 } = options;
|
||||
const { queryData, rest } = splitProps(props);
|
||||
|
||||
const isDisabled = component => {
|
||||
if (
|
||||
@@ -112,10 +137,10 @@ class PluginsService {
|
||||
|
||||
// Check if component is excluded.
|
||||
if (component.isExcluded) {
|
||||
let resolvedProps = this.getSlotComponentProps(
|
||||
let resolvedProps = getSlotComponentProps(
|
||||
component,
|
||||
reduxState,
|
||||
props,
|
||||
rest,
|
||||
queryData
|
||||
);
|
||||
if (component.mapStateToProps) {
|
||||
@@ -154,12 +179,7 @@ class PluginsService {
|
||||
.map(({ component, key }) =>
|
||||
React.createElement(component, {
|
||||
key,
|
||||
...this.getSlotComponentProps(
|
||||
component,
|
||||
reduxState,
|
||||
props,
|
||||
queryData
|
||||
),
|
||||
...getSlotComponentProps(component, reduxState, rest, queryData),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user