ianstormtaylor / slate

A completely customizable framework for building rich text editors. (Currently in beta.)
http://slatejs.org
MIT License
29.71k stars 3.24k forks source link

Any example for using Placeholder component? #931

Closed huozhi closed 6 years ago

huozhi commented 7 years ago

I found that there was only illustration for placeholder component props in https://docs.slatejs.org/reference/components/placeholder.html. And I couldn't find out some examples in slate codebase.

Now I use the default paragraph type placeholder in my app, which works well in examples/rtl.

I would like to know if slate could provide some demo or example code for Placeholder component ? Then I can do more customized things.

Thanks a lot. : )

dmitrizzle commented 7 years ago

Here's how I used it in my project:

Placeholder.js

// tools
import React from "react"
import { Placeholder } from "slate"

// return
export default class extends React.Component {
  render(){
    const { node, state, parent } = this.props
    return (
      <Placeholder
        firstOnly={ true }
        parent={ parent }
        node={ node }
        state={ state }
        style={{ opacity: "0.65" }}
      >
        { this.props.children }
      </Placeholder>
    )
  }
}

schema.js

// tools
import React from "react"

// components
import Placeholder from "./containers/Placeholder"

// return
export const schema = {
    nodes: {
        paragraph: props => <p { ...props.attributes } style={{ position: "relative" }}><Placeholder { ...props }>Write your story...</Placeholder>{ props.children }</p>
    }
}

... Then added this schema to the state.

huozhi commented 7 years ago

Thanks for @dmitrizzle 's example. But I think that it is a little bit obscure to know use Placeholder in editor schema. And the props parent and node are should not be cared by developers. Using in schema is more like customizing the content paragraph.

Why not just give the customized component as prop placeholder={<MyComponent />} ? It might be easier for user to learn.

dmytro-shchurov commented 7 years ago

@dmitrizzle example misses two things. The first one is that Placeholder is implemented in slate-react, not slate; the second is that style needs position: 'inherit', because typically, the first few elements are occupied with block/mark controls, and position: absolute; top: 0; left: 0 will place the placeholder at wrong place

UPD. I've implemented my own version of Placeholder wrapper (in TypeScript), which solves a problem with remaining Placeholder in a focused Editor (until one element is added). Hope it will be usefull

import * as React from 'react';
import { Component } from 'react';

// Components
import { Placeholder } from 'slate-react';

interface SlatePlaceholderProps {
  firstOnly?: boolean;
  node: React.ReactNode;
  parent?: React.ReactNode;
  style?: any;
  state: any;
  editor?: any;
  className?: string;
}

interface SlatePlaceholderState {
  visible: boolean;
}

export class SlatePlaceholder extends Component<SlatePlaceholderProps, SlatePlaceholderState> {
  static defaultProps = {
    firstOnly: true
  }

  state = {
    visible: true
  }

  private _isMounted: boolean = false;

  componentDidMount() {
    const editor = this.props.editor;
    if (editor != undefined) {
      editor.onFocus = this.hidePlaceholder;
      editor.onBlur = this.showPlaceholder;
      this._isMounted = true;
    }
  }

  componentWillUnmount() {
    this._isMounted = false;
    const editor = this.props.editor;
    if (editor != undefined) {
      editor.forceUpdate();  // this is important to allow the placeholder to be hidden after editor is focused the first time
    }
  }

  private hidePlaceholder = () => {
    if (this._isMounted) {
      this.setState({ visible: false });
    }
  }
  private showPlaceholder = () => {
    if (this._isMounted) {
      this.setState({ visible: true });
    }
  }

  render() {
    const { children, ...props } = this.props;
    return (
      this.state.visible ?
        <Placeholder { ...props }>{children}</Placeholder> :
        null
    );
  }
}

and is used in Schema as

export const Schema = (options: { placeholder?} = {}) => ({
  nodes: {
...
    'paragraph': props => <p {...props.attributes}>
      {options && options.placeholder ?
        <SlatePlaceholder
          firstOnly={true}
          editor={props.editor}
          node={props.node}
          parent={props.parent}
          state={props.state}
          style={{ opacity: '0.65', position: 'inherit' }}>{options.placeholder}</Placeholder> :
        null}
      {props.children}</p>,
    'underlined': props => <u {...props.attributes}>{props.children}</u>
  },
  marks: {
...
  }
});
<Editor
            state={this.state.state}
            schema={Schema.Schema({this.props.placeholder})}
...
conorcussell commented 6 years ago

Closing this as the Placeholder component was removed in slate-react@0.7.0