mirror of
				https://github.com/reduxjs/redux-devtools.git
				synced 2025-11-04 01:47:25 +03:00 
			
		
		
		
	added copy to clipboard functionality to Tree, in Actions and State tab
This commit is contained in:
		
							parent
							
								
									27075b1e43
								
							
						
					
					
						commit
						73618765a7
					
				| 
						 | 
					@ -61,7 +61,9 @@
 | 
				
			||||||
    "storage",
 | 
					    "storage",
 | 
				
			||||||
    "file:///*",
 | 
					    "file:///*",
 | 
				
			||||||
    "http://*/*",
 | 
					    "http://*/*",
 | 
				
			||||||
    "https://*/*"
 | 
					    "https://*/*",
 | 
				
			||||||
 | 
					    "clipboardWrite",
 | 
				
			||||||
 | 
					    "clipboardRead"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;",
 | 
					  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;",
 | 
				
			||||||
  "update_url": "https://clients2.google.com/service/update2/crx",
 | 
					  "update_url": "https://clients2.google.com/service/update2/crx",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,9 @@
 | 
				
			||||||
    "storage",
 | 
					    "storage",
 | 
				
			||||||
    "file:///*",
 | 
					    "file:///*",
 | 
				
			||||||
    "http://*/*",
 | 
					    "http://*/*",
 | 
				
			||||||
    "https://*/*"
 | 
					    "https://*/*",
 | 
				
			||||||
 | 
					    "clipboardWrite",
 | 
				
			||||||
 | 
					    "clipboardRead"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
 | 
					  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,7 +57,9 @@
 | 
				
			||||||
    "storage",
 | 
					    "storage",
 | 
				
			||||||
    "file:///*",
 | 
					    "file:///*",
 | 
				
			||||||
    "http://*/*",
 | 
					    "http://*/*",
 | 
				
			||||||
    "https://*/*"
 | 
					    "https://*/*",
 | 
				
			||||||
 | 
					    "clipboardWrite",
 | 
				
			||||||
 | 
					    "clipboardRead"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "content_security_policy": "script-src 'self'; object-src 'self'; img-src 'self' data:;"
 | 
					  "content_security_policy": "script-src 'self'; object-src 'self'; img-src 'self' data:;"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,8 @@ import ActionPreviewHeader from './ActionPreviewHeader';
 | 
				
			||||||
import DiffTab from './tabs/DiffTab';
 | 
					import DiffTab from './tabs/DiffTab';
 | 
				
			||||||
import StateTab from './tabs/StateTab';
 | 
					import StateTab from './tabs/StateTab';
 | 
				
			||||||
import ActionTab from './tabs/ActionTab';
 | 
					import ActionTab from './tabs/ActionTab';
 | 
				
			||||||
import cloneDeep from 'lodash.clonedeep';
 | 
					import { getValueByPath } from './utils/getValueByPath';
 | 
				
			||||||
 | 
					import { copyToClipboard } from './utils/copyToClipboard';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TabComponentProps<S, A extends Action<string>> {
 | 
					export interface TabComponentProps<S, A extends Action<string>> {
 | 
				
			||||||
  labelRenderer: LabelRenderer;
 | 
					  labelRenderer: LabelRenderer;
 | 
				
			||||||
| 
						 | 
					@ -81,16 +82,6 @@ class ActionPreview<S, A extends Action<string>> extends Component<
 | 
				
			||||||
  static defaultProps = {
 | 
					  static defaultProps = {
 | 
				
			||||||
    tabName: DEFAULT_STATE.tabName,
 | 
					    tabName: DEFAULT_STATE.tabName,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  copyToClipboard = (path: (string | number)[]) => {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      const objectByPath = path.reduce((obj: any, key) => (obj && obj[key] !== undefined) ? obj[key] : null, this.props.nextState);
 | 
					 | 
				
			||||||
      const jsonString = JSON.stringify(objectByPath, null, 2);
 | 
					 | 
				
			||||||
      void navigator.clipboard.writeText(jsonString)
 | 
					 | 
				
			||||||
    } catch (err) {
 | 
					 | 
				
			||||||
      console.error('Failed to copy: ', err);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render(): JSX.Element {
 | 
					  render(): JSX.Element {
 | 
				
			||||||
    const {
 | 
					    const {
 | 
				
			||||||
| 
						 | 
					@ -195,7 +186,7 @@ class ActionPreview<S, A extends Action<string>> extends Component<
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  labelRenderer: LabelRenderer = ([key, ...rest], nodeType, expanded) => {
 | 
					  labelRenderer: LabelRenderer = ([key, ...rest], nodeType, expanded) => {
 | 
				
			||||||
    const { onInspectPath, inspectedPath } = this.props;
 | 
					    const { onInspectPath, inspectedPath } = this.props;
 | 
				
			||||||
 | 
					    const reversedPath = [key, ...rest].reverse();
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <span>
 | 
					      <span>
 | 
				
			||||||
        <span>{key}</span>
 | 
					        <span>{key}</span>
 | 
				
			||||||
| 
						 | 
					@ -213,7 +204,7 @@ class ActionPreview<S, A extends Action<string>> extends Component<
 | 
				
			||||||
          onClick={() =>
 | 
					          onClick={() =>
 | 
				
			||||||
            onInspectPath([
 | 
					            onInspectPath([
 | 
				
			||||||
              ...inspectedPath.slice(0, inspectedPath.length - 1),
 | 
					              ...inspectedPath.slice(0, inspectedPath.length - 1),
 | 
				
			||||||
              ...[key, ...rest].reverse(),
 | 
					              ...reversedPath,
 | 
				
			||||||
            ])
 | 
					            ])
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
| 
						 | 
					@ -231,7 +222,17 @@ class ActionPreview<S, A extends Action<string>> extends Component<
 | 
				
			||||||
          })}
 | 
					          })}
 | 
				
			||||||
          onClick={event => {
 | 
					          onClick={event => {
 | 
				
			||||||
            event.stopPropagation();
 | 
					            event.stopPropagation();
 | 
				
			||||||
            this.copyToClipboard([...inspectedPath.slice(0, inspectedPath.length - 1), key]);
 | 
					            let objectForCopying;
 | 
				
			||||||
 | 
					            if (this.props.tabName === 'Action') {
 | 
				
			||||||
 | 
					              objectForCopying = getValueByPath(this.props.action, reversedPath);
 | 
				
			||||||
 | 
					            } else if (this.props.tabName === 'State') {
 | 
				
			||||||
 | 
					              objectForCopying = getValueByPath(this.props.nextState, reversedPath);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (objectForCopying !== undefined) {
 | 
				
			||||||
 | 
					              copyToClipboard(objectForCopying);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              console.error('Unable to find the object to copy');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
        {'(copy)'}
 | 
					        {'(copy)'}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					import cloneDeep from 'lodash.clonedeep';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const copyToClipboard = (object: any) => {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const deepCopiedObject = cloneDeep(object);
 | 
				
			||||||
 | 
					    const jsonString = JSON.stringify(deepCopiedObject, null, 2);
 | 
				
			||||||
 | 
					    void navigator.clipboard.writeText(jsonString);
 | 
				
			||||||
 | 
					  } catch (err) {
 | 
				
			||||||
 | 
					    console.error('Error during copy: ', err);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					export const getValueByPath = (obj: any, path: (string | number)[]) => {
 | 
				
			||||||
 | 
					  let current: any = obj;
 | 
				
			||||||
 | 
					  for (let i = 0; i < path.length; i++) {
 | 
				
			||||||
 | 
					    const key = path[i];
 | 
				
			||||||
 | 
					    const adjustedKey = typeof key === 'string' && !isNaN(Number(key)) ? parseInt(key, 10) : key;
 | 
				
			||||||
 | 
					    if (current[adjustedKey] === undefined) {
 | 
				
			||||||
 | 
					      return undefined;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    current = current[adjustedKey];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return current;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user