generic.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. import * as React from 'react';
  8. import { HierarchyRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
  9. import { PluginCommands } from '../../mol-plugin/commands';
  10. import { State } from '../../mol-state';
  11. import { PurePluginUIComponent } from '../base';
  12. import { IconButton } from '../controls/common';
  13. import { UpdateTransformControl } from '../state/update-transform';
  14. export class GenericEntryListControls extends PurePluginUIComponent {
  15. get current() {
  16. return this.plugin.managers.structure.hierarchy.behaviors.selection;
  17. }
  18. componentDidMount() {
  19. this.subscribe(this.current, () => this.forceUpdate());
  20. }
  21. get unitcell() {
  22. const { selection } = this.plugin.managers.structure.hierarchy;
  23. if (selection.structures.length === 0) return null;
  24. const refs = [];
  25. for (const s of selection.structures) {
  26. const model = s.model;
  27. if (model?.unitcell && model.unitcell?.cell.obj) refs.push(model.unitcell);
  28. }
  29. if (refs.length === 0) return null;
  30. return <GenericEntry refs={refs} labelMultiple='Unitcells' />;
  31. }
  32. get customControls(): JSX.Element[] | null {
  33. const controls: JSX.Element[] = []
  34. this.plugin.genericRepresentationControls.forEach((provider, key) => {
  35. const [refs, labelMultiple] = provider(this.plugin.managers.structure.hierarchy.selection)
  36. if (refs.length > 0) {
  37. controls.push(<div key={key}>
  38. <GenericEntry refs={refs} labelMultiple={labelMultiple} />
  39. </div>)
  40. }
  41. })
  42. return controls.length > 0 ? controls : null
  43. }
  44. render() {
  45. return <>
  46. <div style={{ marginTop: '6px' }}>
  47. {this.unitcell}
  48. {this.customControls}
  49. </div>
  50. </>
  51. }
  52. }
  53. export class GenericEntry<T extends HierarchyRef> extends PurePluginUIComponent<{ refs: T[], labelMultiple?: string }, { showOptions: boolean }> {
  54. state = { showOptions: false }
  55. componentDidMount() {
  56. this.subscribe(this.plugin.events.state.cell.stateUpdated, e => {
  57. if (State.ObjectEvent.isCell(e, this.pivot?.cell)) this.forceUpdate();
  58. });
  59. }
  60. get pivot() { return this.props.refs[0]; }
  61. toggleVisibility = (e: React.MouseEvent<HTMLElement>) => {
  62. e.preventDefault();
  63. this.plugin.managers.structure.hierarchy.toggleVisibility(this.props.refs);
  64. e.currentTarget.blur();
  65. }
  66. highlight = (e: React.MouseEvent<HTMLElement>) => {
  67. e.preventDefault();
  68. PluginCommands.Interactivity.Object.Highlight(this.plugin, {
  69. state: this.pivot.cell.parent,
  70. ref: this.props.refs.map(c => c.cell.transform.ref)
  71. });
  72. }
  73. clearHighlight = (e: React.MouseEvent<HTMLElement>) => {
  74. e.preventDefault();
  75. PluginCommands.Interactivity.ClearHighlights(this.plugin);
  76. }
  77. focus = (e: React.MouseEvent<HTMLElement>) => {
  78. e.preventDefault();
  79. let allHidden = true;
  80. for (const uc of this.props.refs) {
  81. if (!uc.cell.state.isHidden) {
  82. allHidden = false;
  83. break;
  84. }
  85. }
  86. if (allHidden) {
  87. this.plugin.managers.structure.hierarchy.toggleVisibility(this.props.refs, 'show');
  88. }
  89. const loci = [];
  90. for (const uc of this.props.refs) {
  91. if (uc.cell.state.isHidden) {
  92. continue;
  93. }
  94. const l = uc.cell.obj?.data.repr.getLoci()
  95. if (l) loci.push(l);
  96. }
  97. this.plugin.managers.camera.focusLoci(loci);
  98. }
  99. toggleOptions = () => this.setState({ showOptions: !this.state.showOptions })
  100. render() {
  101. const { refs, labelMultiple } = this.props;
  102. if (refs.length === 0) return null;
  103. const pivot = refs[0];
  104. let label, description;
  105. if (refs.length === 1) {
  106. const { obj } = pivot.cell;
  107. if (!obj) return null;
  108. label = obj?.label;
  109. description = obj?.description;
  110. } else {
  111. label = `${refs.length} ${labelMultiple || 'Objects'}`;
  112. }
  113. return <>
  114. <div className='msp-btn-row-group'>
  115. <button className='msp-form-control msp-control-button-label' title={`${label}. Click to focus.`} onClick={this.focus} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} style={{ textAlign: 'left' }}>
  116. {label} <small>{description}</small>
  117. </button>
  118. <IconButton customClass='msp-form-control' onClick={this.toggleVisibility} icon='visual-visibility' toggleState={!pivot.cell.state.isHidden} title={`${pivot.cell.state.isHidden ? 'Show' : 'Hide'}`} small style={{ flex: '0 0 32px', padding: 0 }} />
  119. {refs.length === 1 && <IconButton customClass='msp-form-control' onClick={this.toggleOptions} icon='dot-3' title='Options' toggleState={this.state.showOptions} style={{ flex: '0 0 32px', padding: 0 }} />}
  120. </div>
  121. {(refs.length === 1 && this.state.showOptions) && <>
  122. <div className='msp-control-offset'>
  123. <UpdateTransformControl state={pivot.cell.parent} transform={pivot.cell.transform} customHeader='none' autoHideApply />
  124. </div>
  125. </>}
  126. </>;
  127. }
  128. }