Skip to content

Commit

Permalink
fix: #49
Browse files Browse the repository at this point in the history
  • Loading branch information
anc95 committed May 23, 2023
1 parent 2f1e97e commit 5b45547
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 80 deletions.
64 changes: 34 additions & 30 deletions src/content/container/ask-writely/index.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
import { MutableRefObject, useRef } from 'react';
import ReactDraggable from 'react-draggable';
import { useSelectionManager } from '../store/selection';
import { useView } from '../store/view';
import { Content } from './content';
import { MutableRefObject, useRef } from 'react'
import ReactDraggable from 'react-draggable'
import { useSelectionManager } from '../store/selection'
import { useView } from '../store/view'
import { Content } from './content'

let fixedRef: MutableRefObject<HTMLDivElement>;
let fixedRef: MutableRefObject<HTMLDivElement>

export const AskWritely: React.FC = () => {
const selectionManager = useSelectionManager();
const { position } = selectionManager;
const { viewStatus } = useView();
const _fixedRef = useRef<HTMLDivElement>();
const selectionManager = useSelectionManager()
const { position } = selectionManager
const { viewStatus } = useView()
const _fixedRef = useRef<HTMLDivElement>()

fixedRef = _fixedRef;
fixedRef = _fixedRef

if (viewStatus === 'none') {
return null;
return null
}

return (
<ReactDraggable handle=".handle">
<div
ref={_fixedRef}
style={{
position: 'fixed',
top: `${position.y}px`,
left: `${position.x}px`,
zIndex: 9999999999999,
}}
>
<Content />
</div>
</ReactDraggable>
);
};
const content = (
<div
ref={_fixedRef}
style={{
position: 'fixed',
top: `${position.y}px`,
left: `${position.x}px`,
zIndex: 9999999999999,
}}
>
<Content />
</div>
)

if (viewStatus === 'icon') {
return content
}

return <ReactDraggable handle=".handle">{content}</ReactDraggable>
}

export const getFixedDom = () => {
return fixedRef.current;
};
return fixedRef.current
}
100 changes: 50 additions & 50 deletions src/content/utils/selection/index.ts
Original file line number Diff line number Diff line change
@@ -1,137 +1,137 @@
import { Highlight } from './highlight';
import { debounce } from 'lodash-es';
import { Highlight } from './highlight'
import { debounce } from 'lodash-es'
export class SelectionManager {
protected selectChangeHandlers = [];
protected selectChangeHandlers = []
// lock a selction, means when selection change, we won't emit onSelectionChange
protected locked = false;
protected selection: Selection;
protected highlight: Highlight;
protected savedRange: Range;
private textPasted: boolean;
protected locked = false
protected selection: Selection
protected highlight: Highlight
protected savedRange: Range
private textPasted: boolean

public text: string = '';
public position: { x: number; y: number };
public text: string = ''
public position: { x: number; y: number }

constructor() {
this.setup();
this.highlight = new Highlight();
this.setup()
this.highlight = new Highlight()
}

get activeSelection() {
return this.selection;
return this.selection
}

get isLocked() {
return this.locked;
return this.locked
}

public onSelectionChange = (cb: (selection: Selection) => void) => {
this.selectChangeHandlers.push(cb);
this.selectChangeHandlers.push(cb)

return () => {
this.selectChangeHandlers = this.selectChangeHandlers.filter(
(handler) => handler != cb
);
};
};
)
}
}

public setLock(locked: boolean) {
if (this.locked === locked) {
return;
return
}

this.locked = locked;
this.locked = locked

if (!locked) {
this.textPasted = false;
this.textPasted = false
}
}

public async append(text?: string, replace?: boolean) {
this.restoreRange();
const container = this.selection.getRangeAt(0).commonAncestorContainer;
this.restoreRange()
const container = this.selection.getRangeAt(0).commonAncestorContainer

// for input/text-area. In most case we selected the parent selection. not themself
const inputNode = [...container.childNodes].find(
(child) => child.nodeName === 'TEXTAREA' || child.nodeName === 'INPUT'
);
)

if (inputNode) {
this.selection.getRangeAt(0).selectNode(inputNode);
this.selection.getRangeAt(0).selectNode(inputNode)

if (!replace) {
this.selection.collapseToEnd();
this.selection.collapseToEnd()
}

(inputNode as any).focus();
return document.execCommand('insertText', false, text);
;(inputNode as any).focus()
return document.execCommand('insertText', false, text)
} else {
if (!replace) {
this.selection.collapseToEnd();
container?.parentElement?.focus?.();
this.selection.collapseToEnd()
container?.parentElement?.focus?.()
}

// don't know why. but at first time settimeout excute paste actions, everything works as expected
if (this.textPasted) {
document.execCommand('paste');
document.execCommand('paste')
} else {
setTimeout(() => {
document.execCommand('paste');
this.textPasted = true;
}, 100);
document.execCommand('paste')
this.textPasted = true
}, 100)
}
}
}

public replace(text?: string) {
this.append(text, true);
this.append(text, true)
}

private setup() {
// debounce the event
const eventHandler = debounce((e: MouseEvent) => {
this.selection =
(e.target as any)?.ownerDocument?.getSelection() ||
window.getSelection();
window.getSelection()

const valid = !!this.selection.toString();
const valid = !!this.selection.toString()

if (valid) {
this.position = {
x: Math.max(e.x - 30, 10),
y: e.y + 10,
};
}

if (!this.locked) {
this.savedRange = this.selection.getRangeAt(0).cloneRange();
this.setText();
this.savedRange = this.selection.getRangeAt(0).cloneRange()
this.setText()
}

this.selectChangeHandlers.forEach((handler) => handler(this.selection));
this.selectChangeHandlers.forEach((handler) => handler(this.selection))
}
}, 300);
}, 300)

// listen keyup and check if there is a selection
document.addEventListener('mouseup', eventHandler, true);
document.addEventListener('mouseup', eventHandler, true)

// bind mouseup for every iframes
const iframes = [...document.getElementsByTagName('iframe')];
const iframes = [...document.getElementsByTagName('iframe')]
iframes.forEach((f) => {
if (f.contentDocument) {
f.contentDocument.addEventListener('mouseup', eventHandler, true);
f.contentDocument.addEventListener('mouseup', eventHandler, true)
}
});
})
}

private setText() {
this.text = this.selection.toString();
this.text = this.selection.toString()
}

private restoreRange() {
this.selection.removeAllRanges();
this.selection.addRange(this.savedRange);
this.selection.removeAllRanges()
this.selection.addRange(this.savedRange)

// clone a new copy, to prevent it's being altered
this.savedRange = this.savedRange.cloneRange();
this.savedRange = this.savedRange.cloneRange()
}
}

0 comments on commit 5b45547

Please sign in to comment.