-
Notifications
You must be signed in to change notification settings - Fork 2
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
feat(adapters): caret adapter basic implementation #58
Conversation
✅ Mutation testing passedReport: https://dashboard.stryker-mutator.io/reports/github.com/editor-js/document-model/PR-58 Mutated filessrc/utils/EventBus/index.ts |
Coverage report for
|
St.❔ |
Category | Percentage | Covered / Total |
---|---|---|---|
🟢 | Statements | 100% | 589/589 |
🟢 | Branches | 99.27% | 136/137 |
🟢 | Functions | 99.33% | 149/150 |
🟢 | Lines | 100% | 564/564 |
Test suite run success
347 tests passing in 20 suites.
Report generated by 🧪jest coverage report action from 4c73832
/** | ||
* Caret index is a tuple of start and end offset of a caret | ||
*/ | ||
export type CaretIndex = [number, number]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might re-use type from model
/** | ||
* Index stores start and end offset of a caret depending on a root of input | ||
*/ | ||
#index: CaretIndex = [0, 0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If caret is not set, what index it would be?
* | ||
* @param selection - changed document selection | ||
*/ | ||
#onDocumentSelectionChange = (selection: Selection | null): void => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could try to optimise memory usage with a method (not a property). And pass the context from outside
* @param model - EditorJSModel instance | ||
* @param blockIndex - index of a block that contains input | ||
*/ | ||
constructor(private readonly model: EditorJSModel, private readonly blockIndex: number) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be better to use # modifier. It would add more lines, but properties would be "really" private
* @param input - input to watch caret change | ||
* @param dataKey - key of data property in block's data that contains input's value | ||
*/ | ||
public attachInput(input: HTMLElement, dataKey: string): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need an index of the block as well. Also dataKey is not used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
block index passed to the constructor. It is not used right now as well as model
and dataKey
. I'll implement a working with model later
* Unsubscribes from input caret change | ||
*/ | ||
public detachInput(): void { | ||
this.#offSelectionChange(this.#onDocumentSelectionChange); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could set null to #input
|
||
const range = selection.getRangeAt(0); | ||
|
||
return this.#input?.contains(range.startContainer) ?? false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok for now, but we should think of cross-block selection case in the future
* | ||
* @param node - node to check | ||
*/ | ||
function isLineBreak(node: Node): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move it below the exported members
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There will be ES Lint error: using of method before it declared
export const useSelectionChange = createSingleton(() => { | ||
const subscribers = new Set<Subscriber>(); | ||
|
||
document.addEventListener('selectionchange', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need an API to remove the listener?
/** | ||
* Returns absolute caret index related to input | ||
*/ | ||
public get index(): TextRange { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be above private methods
/** | ||
* Used to save context of the callback. | ||
*/ | ||
context: CaretAdapter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be not only CaretAdapter in the future I suppose. Could be just unknown
|
||
const range = selection.getRangeAt(0); | ||
|
||
return input.contains(range.startContainer) ?? false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you just add todo to think of cross-block selection later. To know where to look once we'll start working on it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can actually use range.intersectsNode(input)
*/ | ||
export const useSelectionChange = createSingleton(() => { | ||
/** | ||
* User to iterate over all inputs and check if selection is related to them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* User to iterate over all inputs and check if selection is related to them. | |
* Used to iterate over all inputs and check if selection is related to them. |
/** | ||
* WeakMap that stores subscribers for each input. | ||
*/ | ||
const subscribers = new WeakMap<InputWithCaret, Subscriber>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could there be more than one subscriber for an input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no
return { | ||
on, | ||
off, | ||
destroy, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once destroyed, there'll be no way to re-init as createSingletone
will always return the same instance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've exported init
method as well. The destroy flow is not designed yet, so it will be implemented once we can understand it
Basic implementation of the Caret DOM Adapter
attachInput
anddetachInput
methodschange
event — used for playground purposes. Could be removed laterInput
components with Caret Adapter attached. Caret index is displayed at the right side of the input