Skip to content

Commit

Permalink
Merge branch 'main' into feat/onError
Browse files Browse the repository at this point in the history
# Conflicts:
#	packages/slate-react/src/components/editable.tsx
  • Loading branch information
zbeyens committed Aug 2, 2023
2 parents 33dcb68 + e0c008f commit 3be4e90
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 80 deletions.
2 changes: 1 addition & 1 deletion docs/api/locations/range.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Check if a `range` is exactly equal to `another`.

Check if a `range` includes a path, a point, or part of another range.

For clarity the definition of `includes` can mean partially includes. Another way to describe this is if one Range intersectns the other Range.
For clarity the definition of `includes` can mean partially includes. Another way to describe this is if one Range intersects the other Range.

#### `Range.isBackward(range: Range) => boolean`

Expand Down
2 changes: 1 addition & 1 deletion docs/general/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ A series of common questions people have about Slate:

One of Slate's core principles is that, unlike most other editors, it does **not** prescribe a specific "schema" to the content you are editing. This means that Slate's core has no concept of "block quotes" or "bold formatting".

For the most part, this leads to increased flexbility without many downsides, but there are certain cases where you have to do a bit more work. Pasting is one of those cases.
For the most part, this leads to increased flexibility without many downsides, but there are certain cases where you have to do a bit more work. Pasting is one of those cases.

Since Slate knows nothing about your domain, it can't know how to parse pasted HTML content \(or other content\). So, by default whenever a user pastes content into a Slate editor, it will parse it as plain text. If you want it to be smarter about pasted content, you need to override the `insert_data` command and deserialize the `DataTransfer` object's `text/html` data as you wish.

Expand Down
173 changes: 172 additions & 1 deletion docs/libraries/slate-react/editable.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,179 @@ type EditableProps = {
} & React.TextareaHTMLAttributes<HTMLDivElement>
```
_NOTE: Detailed breakdown of Props not completed. Refer to the source code at the moment. Under construction._
_NOTE: Detailed breakdown of Props not completed. Refer to the [source code](https://github.com/ianstormtaylor/slate/blob/main/packages/slate-react/src/components/editable.tsx) at the moment. Under construction._
#### `placeholder?: string = ""`
The text to display as a placeholder when the Editor is empty. A typical value for `placeholder` would be "Enter text here..." or "Start typing...". The placeholder text will not be treated as an actual value and will disappear when the user starts typing in the Editor.
#### `readOnly?: boolean = false`
When set to true, renders the editor in a "read-only" state. In this state, user input and interactions will not modify the editor's content.
If this prop is omitted or set to false, the editor remains in the default "editable" state, allowing users to interact with and modify the content.
This prop is particularly useful when you want to display text or rich media content without allowing users to edit it, such as when displaying published content or a preview of the user's input.
#### `renderElement?: (props: RenderElementProps) => JSX.Element`
The `renderElement` prop is a function used to render a custom component for a specific type of Element node in the Slate.js document model.
Here is the type of the `RenderElementProps` passed into the function.
```typescript
export interface RenderElementProps {
children: any
element: Element
attributes: {
'data-slate-node': 'element'
'data-slate-inline'?: true
'data-slate-void'?: true
dir?: 'rtl'
ref: any
}
}
```

The `attributes` must be added to the props of the top level HTML element returned from the function and the `children` must be rendered somewhere inside the returned JSX.

Here is a typical usage of `renderElement` with two types of elements.

```javascript
const initialValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]

const App = () => {
const [editor] = useState(() => withReact(createEditor()))

// Define a rendering function based on the element passed to `props`. We use
// `useCallback` here to memoize the function for subsequent renders.
const renderElement = useCallback(props => {
switch (props.element.type) {
case 'code':
return <CodeElement {...props} />
default:
return <DefaultElement {...props} />
}
}, [])

return (
<Slate editor={editor} initialValue={initialValue}>
<Editable
// Pass in the `renderElement` function.
renderElement={renderElement}
/>
</Slate>
)
}

const CodeElement = props => {
return (
<pre {...props.attributes}>
<code>{props.children}</code>
</pre>
)
}

const DefaultElement = props => {
return <p {...props.attributes}>{props.children}</p>
}
```

#### `renderLeaf?: (props: RenderLeafProps) => JSX.Element`

The `renderLeaf` prop allows you to customize the rendering of leaf nodes in the document tree of your Slate editor. A "leaf" in Slate is the smallest chunk of text and its associated formatting attributes.

The `renderLeaf` function receives an object of type `RenderLeafProps` as its argument:

```typescript
export interface RenderLeafProps {
children: any
leaf: Text
text: Text
attributes: {
'data-slate-leaf': true
}
}
```

Example usage:

```typescript
<Editor
renderLeaf={({ attributes, children, leaf }) => {
return (
<span
{...attributes}
style={{ fontWeight: leaf.bold ? 'bold' : 'normal' }}
>
{children}
</span>
)
}}
/>
```

#### `renderPlaceholder?: (props: RenderPlaceholderProps) => JSX.Element`

The `renderPlaceholder` prop allows you to customize how the placeholder of the Slate.js `Editable` component is rendered when the editor is empty. The placeholder will only be shown when the editor's content is empty.

The `RenderPlaceholderProps` interface looks like this:

```typescript
export type RenderPlaceholderProps = {
children: any
attributes: {
'data-slate-placeholder': boolean
dir?: 'rtl'
contentEditable: boolean
ref: React.RefCallback<any>
style: React.CSSProperties
}
}
```
An example usage might look like:
```jsx
<Editable
renderPlaceholder={({ attributes, children }) => (
<div {...attributes} style={{ fontStyle: 'italic', color: 'gray' }}>
{children}
</div>
)}
/>
```
#### `scrollSelectionIntoView?: (editor: ReactEditor, domRange: DOMRange) => void`
Slate has its own default method to scroll a DOM selection into view that works for most cases; however, if the default behavior isn't working for you, possible due to some complex styling, you may need to override the default behavior by providing a different function here.
#### `as?: React.ElementType = "div"`
The as prop specifies the type of element that will be used to render the Editable component in your React application. By default, this is a `div`.
#### `disableDefaultStyles?: boolean = false`
The `disableDefaultStyles` prop determines whether the default styles of the Slate.js `Editable` component are applied or not.
Please note that with this prop set to `true`, you will need to ensure that your styles cater to all the functionalities of the editor that rely on specific styles to work properly.
Here are the default styles:
```typescript
const defaultStyles = {
// Allow positioning relative to the editable element.
position: 'relative',
// Preserve adjacent whitespace and new lines.
whiteSpace: 'pre-wrap',
// Allow words to break if they are too long.
wordWrap: 'break-word',
// Make the minimum height that of the placeholder.
...(placeholderHeight ? { minHeight: placeholderHeight } : {}),
}
```
2 changes: 1 addition & 1 deletion docs/libraries/slate-react/react-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const [editor] = useState(() => withReact(withHistory(createEditor())))
- [Check methods](react-editor.md#check-methods)
- [Focus and selection methods](react-editor.md#focus-and-selection-methods)
- [DOM translation methods](react-editor.md#dom-translation-methods)
- [DataTranfer methods](react-editor.md#datatransfer-methods)
- [DataTransfer methods](react-editor.md#datatransfer-methods)

## Static methods

Expand Down
2 changes: 1 addition & 1 deletion docs/libraries/slate-react/with-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ const [editor] = useState(() => withReact(withHistory(createEditor())))

The `clipboardFormatKey` option allows you to customize the `DataTransfer` type when Slate data is copied to the clipboard. By default, it is `application/x-slate-fragment` but it can be customized using this option.

This can be useful when a user copies from one Slate editor to a differently configured Slate editor. This could cause nodes to be inserted which are not correctly typed for the receiving editor, corrupting the document. By customizing the `clipboardFormatKey` one can ensure that the raw JSON data isn't cpied between editors with different schemas.
This can be useful when a user copies from one Slate editor to a differently configured Slate editor. This could cause nodes to be inserted which are not correctly typed for the receiving editor, corrupting the document. By customizing the `clipboardFormatKey` one can ensure that the raw JSON data isn't copied between editors with different schemas.
20 changes: 20 additions & 0 deletions packages/slate-react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# slate-react

## 0.98.1

### Patch Changes

- [#5491](https://github.com/ianstormtaylor/slate/pull/5491) [`a5576e56`](https://github.com/ianstormtaylor/slate/commit/a5576e56a73f061972775953f270b34081a5cad8) Thanks [@WcaleNieWolny](https://github.com/WcaleNieWolny)! - Fix firefox table selection if table is contentedtiable

## 0.98.0

### Minor Changes

- [#5486](https://github.com/ianstormtaylor/slate/pull/5486) [`8b548fb5`](https://github.com/ianstormtaylor/slate/commit/8b548fb53af861e1f391f2d5c052e3279f0a0b6c) Thanks [@WcaleNieWolny](https://github.com/WcaleNieWolny)! - Fix invalid usage of the selection API in firefox

## 0.97.2

### Patch Changes

- [#5462](https://github.com/ianstormtaylor/slate/pull/5462) [`a6b606d8`](https://github.com/ianstormtaylor/slate/commit/a6b606d804795d9b134784a35e3b00ac77f3ebbc) Thanks [@Ben-Wormald](https://github.com/Ben-Wormald)! - Update hotkeys util to use isHotkey for better support for non-latin keyboards

* [#5470](https://github.com/ianstormtaylor/slate/pull/5470) [`4bd15ed3`](https://github.com/ianstormtaylor/slate/commit/4bd15ed3950e3a0871f5d0ecb391bb637c05e59d) Thanks [@josephmr](https://github.com/josephmr)! - Fix Android caret placement regression when inputting into empty editor

## 0.97.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/slate-react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "slate-react",
"description": "Tools for building completely customizable richtext editors with React.",
"version": "0.97.1",
"version": "0.98.1",
"license": "MIT",
"repository": "git://github.com/ianstormtaylor/slate.git",
"main": "dist/index.js",
Expand Down
49 changes: 29 additions & 20 deletions packages/slate-react/src/components/editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,12 +321,33 @@ export const Editable = (props: EditableProps) => {
return
}

// Get anchorNode and focusNode
const focusNode = domSelection.focusNode
let anchorNode

// COMPAT: In firefox the normal seletion way does not work
// (https://github.com/ianstormtaylor/slate/pull/5486#issue-1820720223)
if (IS_FIREFOX && domSelection.rangeCount > 1) {
const firstRange = domSelection.getRangeAt(0)
const lastRange = domSelection.getRangeAt(domSelection.rangeCount - 1)

// Right to left
if (firstRange.startContainer === focusNode) {
anchorNode = lastRange.endContainer
} else {
// Left to right
anchorNode = firstRange.startContainer
}
} else {
anchorNode = domSelection.anchorNode
}

// verify that the dom selection is in the editor
const editorElement = EDITOR_TO_ELEMENT.get(editor)!
let hasDomSelectionInEditor = false
if (
editorElement.contains(domSelection.anchorNode) &&
editorElement.contains(domSelection.focusNode)
editorElement.contains(anchorNode) &&
editorElement.contains(focusNode)
) {
hasDomSelectionInEditor = true
}
Expand All @@ -352,7 +373,6 @@ export const Editable = (props: EditableProps) => {
}

// Ensure selection is inside the mark placeholder
const { anchorNode } = domSelection
if (
anchorNode?.parentElement?.hasAttribute(
'data-slate-mark-placeholder'
Expand Down Expand Up @@ -383,7 +403,7 @@ export const Editable = (props: EditableProps) => {
selection && ReactEditor.toDOMRange(editor, selection)

if (newDomRange) {
if (ReactEditor.isComposing(editor)) {
if (ReactEditor.isComposing(editor) && !IS_ANDROID) {
domSelection.collapseToEnd()
} else if (Range.isBackward(selection!)) {
domSelection.setBaseAndExtent(
Expand All @@ -408,27 +428,16 @@ export const Editable = (props: EditableProps) => {
return newDomRange
}

const newDomRange = setDomSelection()
// In firefox if there is more then 1 range and we call setDomSelection we remove the ability to select more cells in a table
if (domSelection.rangeCount <= 1) {
setDomSelection()
}

const ensureSelection =
androidInputManagerRef.current?.isFlushing() === 'action'

if (!IS_ANDROID || !ensureSelection) {
setTimeout(() => {
// COMPAT: In Firefox, it's not enough to create a range, you also need
// to focus the contenteditable element too. (2016/11/16)
if (newDomRange && IS_FIREFOX) {
const el = ReactEditor.toDOMNode(editor, editor)
if (!el) {
editor.onError({
key: 'Editable.toDOMNode',
message: 'Unable to find a DOM node',
})
return
}

el.focus()
}

state.isUpdatingSelection = false
})
return
Expand Down
Loading

0 comments on commit 3be4e90

Please sign in to comment.