@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
121 lines (116 loc) • 4.42 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _toArray from "@babel/runtime/helpers/toArray";
import { useState, useCallback, useMemo } from 'react';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import { usePluginStateEffect } from '../usePluginStateEffect';
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/**
* This is designed to iterate through an object to get the path of its result
* based on separation via "."
*
* Example:
* ```typescript
* type Test = { deepObject: { value: number } };
* // Type should be `"deepObject" | "deepObject.value"`
* type Result = NestedKeys<Test>;
* ```
*/
/**
* This is designed to iterate through a path of an object to get the type of its result
* based on separation via "."
*
* Example:
* ```typescript
* type Test = { deepObject: { value: number } }
* // Type should be `number`
* type Result = Path<Test, 'deepObject.value'>
* ```
*/
/**
*
* ⚠️⚠️⚠️ This is a debounced hook ⚠️⚠️⚠️
* If the plugins you are listening to generate multiple shared states while the user is typing,
* your React Component will get only the last one.
*
* Used to return the current plugin state of input dependencies.
* It will recursively retrieve a slice of the state using a "." to separate
* parts of the state.
*
* Example:
*
* ```typescript
* const pluginA: NextEditorPlugin<
'pluginA',
{
sharedState: { deepObj: { value: number | undefined } };
}
>
* ```
* You can use `const value = useSharedPluginStateSelector(api, 'pluginA.deepObj.value')` to retrieve the value
*
* Example in plugin:
*
* ```typescript
* function ExampleContent({ api }: Props) {
* const title = useSharedPluginStateSelector(api, 'dog.title')
* return <p>{ title } { exampleState.description }</p>
* }
*
* const examplePlugin: NextEditorPlugin<'example', { dependencies: [typeof pluginDog] }> = ({ api }) => {
* return {
* name: 'example',
* contentComponent: () => <ExampleContent api={api} />
* }
* }
* ```
*
* NOTE: If you pass an invalid path, `undefined` will be returned
*
* @param api
* @param plugin
* @returns
*/
export function useSharedPluginStateSelector(api, plugin) {
var transformer = useCallback(function (pluginState) {
var _plugin$split = plugin.split('.'),
_plugin$split2 = _toArray(_plugin$split),
pluginName = _plugin$split2[0],
properties = _plugin$split2.slice(1);
if (!pluginState || (properties === null || properties === void 0 ? void 0 : properties.length) === 0) {
return undefined;
}
return get(pluginState === null || pluginState === void 0 ? void 0 : pluginState["".concat(pluginName, "State")], properties);
}, [plugin]);
var pluginNameArray = useMemo(function () {
var _plugin$split3 = plugin.split('.'),
_plugin$split4 = _slicedToArray(_plugin$split3, 1),
pluginName = _plugin$split4[0];
return [pluginName];
}, [plugin]);
var initialState = useMemo(function () {
var _api$pluginName;
var _plugin$split5 = plugin.split('.'),
_plugin$split6 = _slicedToArray(_plugin$split5, 1),
pluginName = _plugin$split6[0];
return transformer(_defineProperty({}, "".concat(pluginName, "State"), api === null || api === void 0 || (_api$pluginName = api[pluginName]) === null || _api$pluginName === void 0 ? void 0 : _api$pluginName.sharedState.currentState()));
}, [plugin, api, transformer]);
return useSharedPluginStateSelectorInternal(api, pluginNameArray, transformer, initialState);
}
// eslint-disable-next-line @typescript-eslint/max-params
function useSharedPluginStateSelectorInternal(api, plugins, transformer, initialState) {
var _useState = useState(initialState),
_useState2 = _slicedToArray(_useState, 2),
selectedPluginState = _useState2[0],
setSelectedPluginState = _useState2[1];
usePluginStateEffect(api, plugins, function (pluginStates) {
// `pluginStates`: This is the same type through inference - but typescript doesn't recognise them as they are computed slightly differently
var transformedValue = transformer(pluginStates);
if (!isEqual(transformedValue, selectedPluginState)) {
setSelectedPluginState(transformedValue);
}
});
return selectedPluginState;
}