how to programmatically select all the content of a TextField in ipyvuetify?

Viewed 285

I would like to reproduce this behaviour of github when you clone a url : select all the content when clicked.

in JavaScript it's as simple as that :

<input @focus="$event.target.select()">

in ipyvuetify I can build the component :


import ipyvuetify as v
import pyperclip

class CopyLink(v.TextField):
    
    def __init__(self):

        super().__init__(
            class_ = "ma-5",
            v_model = 'je suis un link', 
            outlined = True,
            label = 'link',
            readonly = True,
            append_icon = 'mdi-clipboard-outline'
        )  

        self.on_event('click', self.copy_link)

    def copy_link(self, widget, event, data):

        # copy the link to clipboard using 
        pyperclip.copy(self.v_model)

        # select all the content in case pyperclip fail
        
        return

I can copy the value to the clipboard using pyperclip but in case the function fail I would like to be able to select all the textfield content. Is it possible ?

2 Answers

Indeed, Pyperclip does not work in cloud-hosted JupyterLab. With the Clipboard web API, a reusable, custom TextField with the appended copy icon can be defined as follows:

class TextFieldCopy(v.VuetifyTemplate):
    v_model = traitlets.Unicode(allow_none=True).tag(sync=True)
    def __init__(self, icon='mdi-clipboard-outline', props='', v_model=''):
        self.template = '''
        <v-text-field %s v-model='v_model' append-icon='%s' @click:append="clipW">
        </v-text-field>
        <script> {methods: {
            clipW() {navigator.clipboard.writeText(this.v_model)} }}
        </script>''' % (props, icon)
        self.v_model = v_model
        super().__init__()

tf = TextFieldCopy(
    props = 'ma-5 outlined label="link" readonly',
    v_model='je suis un link'
)

Ipyvuetify text field component with copy icon

The parameter allow_none=True avoids a Traitlets error when the TextField is set with the clearable property and the clearing cross is clicked, as v-model is then set to None.

Alternatively, the textfield and the button can be separate components linked together with link:

from ipywidgets import link

class BtnClipWriteText(v.VuetifyTemplate):
    text = traitlets.Unicode(allow_none=True).tag(sync=True)
    def __init__(self, props='icon', content='<v-icon>mdi-content-copy</v-icon>'):
        self.template = '''<v-btn %s @click="clipW">%s</v-btn>
        <script> {methods: {
            clipW() {navigator.clipboard.writeText(this.text)} }}
        </script>''' % (props, content)        
        super().__init__()

btn_copy = BtnClipWriteText()
tf = v.TextField(v_model = 'je suis un link')
link((tf, 'v_model'), (btn_copy, 'text'))

For information, the opposite function of pasting the clipboard content into a text field can be similarly achieved with the following template:

class BtnClipReadText(v.VuetifyTemplate):
    text = traitlets.Unicode(allow_none=True).tag(sync=True)
    def __init__(self, props='icon', content='<v-icon>mdi-content-paste</v-icon>'):
        self.template = '''<v-btn %s @click="clipR">%s</v-btn>
        <script> {methods: {
            clipR() {navigator.clipboard.readText().then(
                clipText => this.text = clipText);}}}
        </script>''' % (props, content)
        super().__init__()

In ipyvuetify there is no way to select the text of a TextField. It can be achieved using the template mode:

import ipyvuetify as vy
import traitlets

class MyCopyPaste(vy.VuetifyTemplate):
    template = traitlets.Unicode('''
    
    <template>
        <v-col>
            <v-btn icon @click="copyToClipboard()">
                <v-icon>mdi-content-copy</v-icon>
            </v-btn>
            <textarea v-model="my_text" ref="text" style="display: none" />
        </v-col>    
    </template>
    
    <script>
        module.exports = {
            methods: {
                copyToClipboard() {
                    const txt = this.$refs.text
                    txt.style.display = 'block';
                    txt.select();
                    document.execCommand('copy');
                    txt.style.display = 'none';
                }
            }
        }
    </script>
    ''').tag(sync=True)
    
    my_text = traitlets.Unicode('').tag(sync=True)
    
mcp = MyCopyPaste()
mcp.my_text = 'Hello\nWorld'
mcp
Related