forked from ianstormtaylor/slate
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathuse-decorations.ts
More file actions
88 lines (72 loc) · 2.36 KB
/
use-decorations.ts
File metadata and controls
88 lines (72 loc) · 2.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { createContext, useCallback, useContext, useMemo, useRef } from 'react'
import {
type DecoratedRange,
type Descendant,
Node,
type NodeEntry,
} from 'slate'
import { isElementDecorationsEqual, isTextDecorationsEqual } from 'slate-dom'
import { ReactEditor } from '../plugin/react-editor'
import { useGenericSelector } from './use-generic-selector'
import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect'
import { useSlateStatic } from './use-slate-static'
type Callback = () => void
/**
* A React context for sharing the `decorate` prop of the editable and
* subscribing to changes on this prop.
*/
export const DecorateContext = createContext<{
decorate: (entry: NodeEntry) => DecoratedRange[]
addEventListener: (callback: Callback) => () => void
}>({} as any)
export const useDecorations = (
node: Descendant,
parentDecorations: DecoratedRange[]
): DecoratedRange[] => {
const editor = useSlateStatic()
const { decorate, addEventListener } = useContext(DecorateContext)
// Not memoized since we want nodes to be decorated on each render
const selector = () => {
const path = ReactEditor.findPath(editor, node)
return decorate([node, path])
}
const equalityFn = Node.isText(node)
? isTextDecorationsEqual
: isElementDecorationsEqual
const [decorations, update] = useGenericSelector(selector, equalityFn)
useIsomorphicLayoutEffect(() => {
const unsubscribe = addEventListener(update)
update()
return unsubscribe
}, [addEventListener, update])
return useMemo(
() => [...decorations, ...parentDecorations],
[decorations, parentDecorations]
)
}
export const useDecorateContext = (
decorateProp: (entry: NodeEntry) => DecoratedRange[]
) => {
const eventListeners = useRef(new Set<Callback>())
const latestDecorate = useRef(decorateProp)
useIsomorphicLayoutEffect(() => {
latestDecorate.current = decorateProp
eventListeners.current.forEach((listener) => {
listener()
})
}, [decorateProp])
const decorate = useCallback(
(entry: NodeEntry) => latestDecorate.current(entry),
[]
)
const addEventListener = useCallback((callback: Callback) => {
eventListeners.current.add(callback)
return () => {
eventListeners.current.delete(callback)
}
}, [])
return useMemo(
() => ({ decorate, addEventListener }),
[decorate, addEventListener]
)
}