screenshot.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. /**
  2. * Copyright (c) 2019-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 { PluginCommands } from '../../mol-plugin/commands';
  9. import { PluginContext } from '../../mol-plugin/context';
  10. import { PluginUIComponent } from '../base';
  11. import { Button, ExpandGroup, ToggleButton } from '../controls/common';
  12. import { CopySvg, CropFreeSvg, CropOrginalSvg, CropSvg, GetAppSvg } from '../controls/icons';
  13. import { ParameterControls } from '../controls/parameters';
  14. import { ScreenshotPreview } from '../controls/screenshot';
  15. import { useBehavior } from '../hooks/use-behavior';
  16. import { LocalStateSnapshotParams, StateExportImportControls } from '../state/snapshots';
  17. interface ImageControlsState {
  18. showPreview: boolean,
  19. isDisabled: boolean,
  20. imageData?: string
  21. }
  22. export class DownloadScreenshotControls extends PluginUIComponent<{ close: () => void }, ImageControlsState> {
  23. state: ImageControlsState = {
  24. showPreview: true,
  25. isDisabled: false
  26. } as ImageControlsState;
  27. private download = () => {
  28. this.plugin.helpers.viewportScreenshot?.download();
  29. this.props.close();
  30. };
  31. private copy = async () => {
  32. try {
  33. await this.plugin.helpers.viewportScreenshot?.copyToClipboard();
  34. PluginCommands.Toast.Show(this.plugin, {
  35. message: 'Copied to clipboard.',
  36. title: 'Screenshot',
  37. timeoutMs: 1500
  38. });
  39. } catch {
  40. return this.copyImg();
  41. }
  42. };
  43. private copyImg = async () => {
  44. const src = await this.plugin.helpers.viewportScreenshot?.getImageDataUri();
  45. this.setState({ imageData: src });
  46. };
  47. componentDidMount() {
  48. this.subscribe(this.plugin.state.data.behaviors.isUpdating, v => {
  49. this.setState({ isDisabled: v });
  50. });
  51. }
  52. componentWillUnmount() {
  53. this.setState({ imageData: void 0 });
  54. }
  55. open = (e: React.ChangeEvent<HTMLInputElement>) => {
  56. if (!e.target.files || !e.target.files![0]) return;
  57. PluginCommands.State.Snapshots.OpenFile(this.plugin, { file: e.target.files![0] });
  58. };
  59. render() {
  60. const hasClipboardApi = !!(navigator.clipboard as any)?.write;
  61. return <div>
  62. {this.state.showPreview && <div className='msp-image-preview'>
  63. <ScreenshotPreview plugin={this.plugin} />
  64. <CropControls plugin={this.plugin} />
  65. </div>}
  66. <div className='msp-flex-row'>
  67. {!this.state.imageData && <Button icon={CopySvg} onClick={hasClipboardApi ? this.copy : this.copyImg} disabled={this.state.isDisabled}>Copy</Button>}
  68. {this.state.imageData && <Button onClick={() => this.setState({ imageData: void 0 })} disabled={this.state.isDisabled}>Clear</Button>}
  69. <Button icon={GetAppSvg} onClick={this.download} disabled={this.state.isDisabled}>Download</Button>
  70. </div>
  71. {this.state.imageData && <div className='msp-row msp-copy-image-wrapper'>
  72. <div>Right click below + Copy Image</div>
  73. <img src={this.state.imageData} style={{ width: '100%', height: 32, display: 'block' }} />
  74. </div>}
  75. <ScreenshotParams plugin={this.plugin} isDisabled={this.state.isDisabled} />
  76. <ExpandGroup header='State'>
  77. <StateExportImportControls onAction={this.props.close} />
  78. <ExpandGroup header='Save Options' initiallyExpanded={false} noOffset>
  79. <LocalStateSnapshotParams />
  80. </ExpandGroup>
  81. </ExpandGroup>
  82. </div>;
  83. }
  84. }
  85. function ScreenshotParams({ plugin, isDisabled }: { plugin: PluginContext, isDisabled: boolean }) {
  86. const helper = plugin.helpers.viewportScreenshot!;
  87. const values = useBehavior(helper.behaviors.values);
  88. return <ParameterControls params={helper.params} values={values} onChangeValues={v => helper.behaviors.values.next(v)} isDisabled={isDisabled} />;
  89. }
  90. function CropControls({ plugin }: { plugin: PluginContext }) {
  91. const helper = plugin.helpers.viewportScreenshot;
  92. const cropParams = useBehavior(helper?.behaviors.cropParams!);
  93. useBehavior(helper?.behaviors.relativeCrop);
  94. if (!helper) return null;
  95. return <div style={{ width: '100%', height: '24px', marginTop: '8px' }}>
  96. <ToggleButton icon={CropOrginalSvg} title='Auto-crop' inline isSelected={cropParams.auto}
  97. style={{ background: 'transparent', float: 'left', width: 'auto', height: '24px', lineHeight: '24px' }}
  98. toggle={() => helper.toggleAutocrop()} label={'Auto-crop ' + (cropParams.auto ? 'On' : 'Off')} />
  99. {!cropParams.auto && <Button icon={CropSvg} title='Crop'
  100. style={{ background: 'transparent', float: 'right', height: '24px', lineHeight: '24px', width: '24px', padding: '0' }}
  101. onClick={() => helper.autocrop()} />}
  102. {!cropParams.auto && !helper.isFullFrame && <Button icon={CropFreeSvg} title='Reset Crop'
  103. style={{ background: 'transparent', float: 'right', height: '24px', lineHeight: '24px', width: '24px', padding: '0' }}
  104. onClick={() => helper.resetCrop()} />}
  105. </div>;
  106. }