mito icon indicating copy to clipboard operation
mito copied to clipboard

Copy and Paste Out of Mito

Open aarondr77 opened this issue 2 years ago • 3 comments

Is your feature request related to a problem? Please describe. One of the primary ways that users get data into and out of other spreadsheet tools is by copying / pasting the data. The process of getting data into Mito is more formal. Its a several step process, and becomes even more burdensome if the file is not in the same directory that you have launched Jupyter from.

Describe the solution you'd like Users should be able to copy data from an Excel or CSV file into Mito, and they should be able to copy data from Mito into an Excel or CSV file.

Describe alternatives you've considered The alternatives are using Mito's import and export features, which work, but take more steps + time.

aarondr77 avatar Feb 14 '22 23:02 aarondr77

Some thoughts:

  1. Copying out seems like the really high-value thing here. We don't really observe users doing single cell edits often, and I've never seen users chaining whole columns, etc. Implementation should def start here.
  2. Copying in is kinda an ill-defined operation... what does this really mean? Does it overwrite a block of the data frame? What are the limitations?

For these reasons, I'm gonna ask that we a) turn this into into "copy and paste out of a mitosheet", and b) we open a new issue for copy and paste into a mitosheet, if we want to. I think the copy and paste in is a much lower priority / very different thing!

naterush avatar Feb 16 '22 20:02 naterush

Google Sheets Behavior

If you open Finder > Edit > Show Clipboard, you can see what's on your clipboard. I used this to figure out the contents of the clipboard after a copy in a few cases.

Screen Shot 2022-03-02 at 11 22 00 AM

Copying a single cell

1

Copying a single column

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

1
--
2
3
4

</google-sheets-html-origin>

Copying a rectangular range including the first row

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

1 | 5
-- | --
2 | 6
3 | 7
4 | 8

</google-sheets-html-origin>

Rectangular range, not including first row

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

2 | 6
-- | --
3 | 7
4 | 8

</google-sheets-html-origin>

Rectangular range including an empty cell

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

1 | 5
-- | --
2 | 6
3 | 7
4 | 8
  | 9

</google-sheets-html-origin>

Two different selections, same length, next to each other

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

1 | 5
-- | --
2 | 6
3 | 7
4 | 8

</google-sheets-html-origin>

Two different sections, same length, not next to eachother

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

2 | 11
-- | --
3 | 12
4 | 13

</google-sheets-html-origin>

Two different sections, different length, not next to eachother

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

12
--
13

</google-sheets-html-origin>
<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

3
--
4

</google-sheets-html-origin>

NOTE: it appears to take the range with the lowest row index. Idk why, but that's what it does!

Copying with number formatting

First, made 1, 2 into percentages, and then copied them.

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

100.00%
--
200.00%

</google-sheets-html-origin>

NOTE: it copies the value exactly as displayed, rather than copying the underlying data. I think this probably makes sense, as users expect to copy what they do.

Copying with color formatting

Turn 1 and 2 to red and setting the background yellow. Get the following text:

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

1
--
2

</google-sheets-html-origin>

So it doesn't appear to do anything to colors - it doesn't copy them over at all. Not that it really matters -- since we don't have colors.

Copying a cell with tabs and newlines

"this    is
a   test"

Copying a range with tabs and newlines

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

this isa test | 3
-- | --
2 | 4

</google-sheets-html-origin>

NOTE: When copying and pasting this into sheets, it remembers the new line. But when you copy and paste into notion, it does not (or the space).

Copying with the delimiter in the data

<google-sheets-html-origin style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">

123 \| 123 | 123
-- | --


</google-sheets-html-origin>

NOTE: when there is a delimiter, we escape it! There is probably some function that does this.

naterush avatar Mar 02 '22 19:03 naterush

Thoughts on our implementation

What to copy to the clipboard

If there is a single cell highlighted: we just copy the contents of that cell. If there is a single selection highlighted: we copy the full selection. If there are multiple selections, and they are selecting the same rows: we "append them" horizontally, and copy all of them. If there are multiple selections, and they are selecting different rows: we take the selection that has the lowest row.

We treat column headers as a normal header in above. If there is ever more than one row that we are copying, we set apart the first row that is copied by a bar, so that it becomes a header (per Google Sheets, this is true even if they do not include the header).

The data we copy to the clipboard ignores any potential future color or border formatting we add!

On targets for evaluation

I want to be able to effectively copy to the following applications:

  1. Excel
  2. Google Sheets
  3. Notion
  4. Powerpoint

These 4 applications working are what we're targeting for acceptance criteria. We should be able to copy out of Mito and into them as successful as we do with sheets - this is the acceptance criteria. It should work on both Mac and Windows!

On tabs, newlines, and other strange data in what we're copying

Let's do what google sheets does:

  • If it's a single cell being copied, we can put it quotes.
  • If it is a range, we just linearize the entire thing as a single line (e.g. do our best).
  • If there a delimiter, we escape it. We'll need to figure out how to do this.

ON HTML tags

I do not think we need to start with HTML tags; I believe that copying with just the side by side bar structure is enough that we will be able to copy and paste into many different apps.

So, for now, let's not worry about this. If we can copy into the targets for evaluation, then I think we're all good. If not, we'll have to look into formatting this with HTML better!

Detecting command + c or control + c

We can easily add in the following function:

app.commands.addKeyBinding({
        command: 'mito-copy',
        args: {},
        keys: ['Accel C'],
        selector: '.mito-container'
});
app.commands.addCommand('mito-copy', {
    label: 'Tries to copy and paste',
    execute: async (): Promise<void> => {

        console.log("Copy");
    }
});

Copying the right data

The big open question here: as the commands are not defined with access to the mitosheet or the data inside of it, how do we copy data out?

We need access to the selections, and the formatting data, and much more. So really what we're looking for is for the Mito instance to define some functions that get exported (e.g. ones that copy data).

Notably, this function definition needs to change when the the sheet data updates (or the closure it's in needs to update). Implementing this is tough - but we can probably do something similar to the setState updaters map. We then just need an effect that reruns when the data or selection changes, and redefines this variable (I wonder if this has a performance impact).

naterush avatar Mar 02 '22 19:03 naterush