Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add architecture for granual pasteAsPlainText support #68

Merged
merged 6 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {
} from './paste-keyboard-shortcut-helper'
import {install as installTable, uninstall as uninstallTable} from './paste-markdown-table'
import {install as installText, uninstall as uninstallText} from './paste-markdown-text'
import {OptionConfig} from './option-config'

interface Subscription {
unsubscribe: () => void
}

function subscribe(el: HTMLElement): Subscription {
installSkipFormatting(el, installTable, installImageLink, installLink, installText, installHTML)

function subscribe(el: HTMLElement, optionConfig?: OptionConfig): Subscription {
installSkipFormatting(el, [installTable, installImageLink, installText, installHTML], [installLink], optionConfig)
return {
unsubscribe: () => {
uninstallSkipFormatting(el)
Expand Down
13 changes: 13 additions & 0 deletions src/option-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface OptionConfig {
defaultPlainTextPaste?: PlainTextParams
}

interface PlainTextParams {
urlLinks?: boolean
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great!


// Not currently implemented behavior
/*imageLinks?: boolean
html?: boolean
table?: boolean
text?: boolean*/
}
13 changes: 12 additions & 1 deletion src/paste-keyboard-shortcut-helper.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {OptionConfig} from './option-config'

const skipFormattingMap = new WeakMap<HTMLElement, boolean>()

function setSkipFormattingFlag(event: KeyboardEvent): void {
Expand All @@ -21,13 +23,22 @@ export function shouldSkipFormatting(el: HTMLElement): boolean {
return shouldSkipFormattingState
}

export function installAround(el: HTMLElement, ...installCallbacks: Array<(el: HTMLElement) => void>): void {
export function installAround(
el: HTMLElement,
installCallbacks: Array<(el: HTMLElement) => void>,
installCallbacksWithOptions: Array<(el: HTMLElement, optionConfig?: OptionConfig) => void>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than have two arrays here, why not just pass options to all the callbacks?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping to avoid confusion given we export the individual installation functions, too, but the documentation update would be sufficient. Let me tackle this now

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Infact I am not even sure this is needed, I believe still keeping them to the same array and simplying defaulting to passing in config params in all cases works (tests are still passing).

image

If my understanding is correct, it'll simply ignore the param

optionConfig?: OptionConfig
): void {
el.addEventListener('keydown', setSkipFormattingFlag)

for (const installCallback of installCallbacks) {
installCallback(el)
}

for (const installCallback of installCallbacksWithOptions) {
installCallback(el, optionConfig)
}

el.addEventListener('paste', unsetSkipFormattedFlag)
}

Expand Down
18 changes: 16 additions & 2 deletions src/paste-markdown-link.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import {OptionConfig} from './option-config'
import {insertText} from './text'
import {shouldSkipFormatting} from './paste-keyboard-shortcut-helper'

export function install(el: HTMLElement): void {
const pasteLinkAsPlainTextOverSelectedTextMap = new WeakMap<HTMLElement, boolean>()

export function install(el: HTMLElement, optionConfig?: OptionConfig): void {
pasteLinkAsPlainTextOverSelectedTextMap.set(el, optionConfig?.defaultPlainTextPaste?.urlLinks === true)
el.addEventListener('paste', onPaste)
}

Expand All @@ -11,7 +15,16 @@ export function uninstall(el: HTMLElement): void {

function onPaste(event: ClipboardEvent) {
const {currentTarget: el} = event
if (shouldSkipFormatting(el as HTMLElement)) return
const element = el as HTMLElement
const shouldPasteAsPlainText = pasteLinkAsPlainTextOverSelectedTextMap.get(element) ?? false
const shouldSkipDefaultBehavior = shouldSkipFormatting(element)

if (
(!shouldPasteAsPlainText && shouldSkipDefaultBehavior) ||
(shouldPasteAsPlainText && !shouldSkipDefaultBehavior)
) {
return
}

const transfer = event.clipboardData
if (!transfer || !hasPlainText(transfer)) return
Expand All @@ -26,6 +39,7 @@ function onPaste(event: ClipboardEvent) {

const selectedText = field.value.substring(field.selectionStart, field.selectionEnd)
if (!selectedText.length) return

// Prevent linkification when replacing an URL
// Trim whitespace in case whitespace is selected by mistake or by intention
if (isURL(selectedText.trim())) return
Expand Down
38 changes: 38 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,38 @@ describe('paste-markdown', function () {
assert.equal(textarea.value, 'The examples can be found [here](https://github.com).')
})

it('turns pasted urls on selected text into markdown links if pasteLinkAsPlainTextOverSelectedText is false', function () {
subscription = subscribeWithOptionConfig(subscription, textarea, false)

// eslint-disable-next-line i18n-text/no-en
textarea.value = 'The examples can be found here.'
textarea.setSelectionRange(26, 30)
paste(textarea, {'text/plain': 'https://github.com'})
assert.equal(textarea.value, 'The examples can be found [here](https://github.com).')
})

it('turns pasted urls on selected text into markdown links if pasteLinkAsPlainTextOverSelectedText is true and skip format flag is true', function () {
subscription = subscribeWithOptionConfig(subscription, textarea, true)

// eslint-disable-next-line i18n-text/no-en
textarea.value = 'The examples can be found here.'
textarea.setSelectionRange(26, 30)
dispatchSkipFormattingKeyEvent(textarea)
paste(textarea, {'text/plain': 'https://github.com'})
assert.equal(textarea.value, 'The examples can be found [here](https://github.com).')
})

it('pastes as plain text on selected text if pasteLinkAsPlainTextOverSelectedText is true', function () {
subscription = subscribeWithOptionConfig(subscription, textarea, true)

// eslint-disable-next-line i18n-text/no-en
textarea.value = 'The examples can be found here.'
textarea.setSelectionRange(26, 30)
paste(textarea, {'text/plain': 'https://github.com'})
// The text area will be unchanged at this stage as the paste won't be handled by our listener
assert.equal(textarea.value, 'The examples can be found here.')
})

it('creates a markdown link when the pasted url includes a trailing slash', function () {
// eslint-disable-next-line i18n-text/no-en
textarea.value = 'The examples can be found here.'
Expand Down Expand Up @@ -353,6 +385,12 @@ function dispatchSkipFormattingKeyEvent(textarea) {
)
}

function subscribeWithOptionConfig(subscription, textarea, urlLinks) {
// Clear the before test subscription with no config and re-subscribe with config
subscription.unsubscribe()
return subscribe(textarea, {defaultPlainTextPaste: {urlLinks}})
}

function paste(textarea, data) {
const dataTransfer = new DataTransfer()
for (const key in data) {
Expand Down