Skip to content
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

Text Align Feature #22

Open
Ramnath-Karthikesan opened this issue Apr 4, 2023 · 9 comments
Open

Text Align Feature #22

Ramnath-Karthikesan opened this issue Apr 4, 2023 · 9 comments

Comments

@Ramnath-Karthikesan
Copy link

Hi,
Recently I tried building the text-align feature in the Wax editor but there were some problems with my implementation.

The main problem with my approach is that the contents get aligned by wrapping it with a div element with a style attribute. But it would be a lot better if I could just update or add a style attribute to the current node.

Is there a way to resolve this issue?

This is how I implemented the service:

//rightAlignService.js

import { Service } from 'wax-prosemirror-core';
import rightAlignNode from '../schema/rightAlignNode';
import RightAlign from './RightAlign';

class RightAlignService extends Service {
register() {
this.container.bind('RightAlign').to(RightAlign);
const createNode = this.container.get('CreateNode');
createNode(
{
rightAlign: rightAlignNode,
},
);
}
}

export default RightAlignService;

//rightAlignNode.js

const rightAlignNode = {
content: 'block*',
group: 'block',
code: true,
defining: true,
// marks: 'comment insertion deletion',
attrs: { params: { default: '' } },
parseDOM: [
{
tag: 'div',
preserveWhitespace: 'full',
getAttrs(dom) {
return {
params: dom.dataset.params,
};
},
},
],
toDOM(node) {
return ['div', { 'style': 'text-align: right' }, 0];
},
};

export default rightAlignNode;

//RightAlign.js

import { injectable } from 'inversify';
import { wrapIn } from 'prosemirror-commands';
import { Tools } from 'wax-prosemirror-core';

@Injectable()
export default class RightAlign extends Tools {
title = 'Right Align';
icon = 'rightAlign';
name = 'rightAlign';

get run() {
return (state, dispatch) => {
wrapIn(state.config.schema.nodes.rightAlign)(state, dispatch);
};
}

select = (state, activeViewId, activeView) => {
const { disallowedTools } = activeView.props;
if (disallowedTools.includes('rightAlign')) return false;
return true;
};

get enable() {
return state => {
return wrapIn(state.config.schema.nodes.rightAlign)(state);
};
}
}

@christos8333
Copy link
Owner

Hello,
In the config where you import DefaultSchema from core , give your own with the extra attr on the paragraph. If you need that attr in the rest of the blockNodes check ToWaxSchema(https://github.com/christos8333/wax-prosemirror/blob/master/wax-prosemirror-services/src/TrackChangeService/TrackChangeService.js#L18). That will add to the existing node schema any extra attrs you want to pass.

@Ramnath-Karthikesan
Copy link
Author

Hi,
How exactly am I supposed to change that? Can you please suggest the code changes?
I think you are referring to these functions:

get run() {
return (state, dispatch) => {
wrapIn(state.config.schema.nodes.rightAlign)(state, dispatch);
};
}

get enable() {
return state => {
return wrapIn(state.config.schema.nodes.rightAlign)(state);
};
}

Instead of wrapIn what exactly should I do? A code suggestion would be of great help

@christos8333
Copy link
Owner

What you proposed actually. You don't need a wrapper. Add the text-align property to the paragraph. Haven't done it but this makes more sense. So in your config for example
https://github.com/christos8333/wax-prosemirror/blob/master/editors/demo/src/Editoria/config/config.js#L127, don't import that or DefaultSchema and pass your own starting schema . It will be the same with the exception that it will have the extra attrs you need. Then in the command you have created you will need setNodeMarkUp i believe to update the node.

@Ramnath-Karthikesan
Copy link
Author

okay I'll try out these suggestions.
Will you implement the text-align feature in the editor in the future? I think it would be a really useful feature to have.

@Ramnath-Karthikesan
Copy link
Author

Hi,
I tried using the setNodeMarkup command but it throws an error stating:

Uncaught Error: NodeType.create can't construct text nodes

@christos8333
Copy link
Owner

right is not applicable there. Use Commands from wax-prosemirror-core

Commands.setBlockType(state.config.schema.nodes.paragraph, {
class: 'paragraph', style: 'text-align: center'
})(state, dispatch);

@Ramnath-Karthikesan
Copy link
Author

Ramnath-Karthikesan commented Apr 11, 2023

right is not applicable there. Use Commands from wax-prosemirror-core

Commands.setBlockType(state.config.schema.nodes.paragraph, { class: 'paragraph', style: 'text-align: center' })(state, dispatch);

This did not work. The second problem is that if I want to align another tag like h2, or a list, then this approach would be very difficult.
Is there any other approach to solve this problem?

There should be some way to just edit or update the style attribute of the node.

@Ramnath-Karthikesan
Copy link
Author

Ramnath-Karthikesan commented Apr 11, 2023

Currently, the following implementation works but it works only for paragraphs. So need to come up with a different logic to handle alignment for all tags.

//RightAlignService.js

import { Service } from 'wax-prosemirror-core';
import rightAlignNode from '../schema/rightAlignNode';
import RightAlign from './RightAlign';

class RightAlignService extends Service {
  register() {
    this.container.bind('RightAlign').to(RightAlign);
    const createNode = this.container.get('CreateNode');
    createNode(
      {
        rightAlign: rightAlignNode,
      },
      {toWaxSchema: true},
    );
  }
}

export default RightAlignService;

// rightAlignNode.js

const rightAlignNode = {
  content: 'inline*',
  group: 'block',
  priority: 0,
  defining: true,
  attrs: {
    style: { default: 'text-align: right' },
  },
  parseDOM: [
    {
      tag: 'p',
      getAttrs(hook, next) {
        
        Object.assign(hook, {
          style: hook.dom.getAttribute('style'),
        });
        next();
      },
    },
  ],
  toDOM(hook, next) {
    console.log(hook);
    const attrs = { style: hook.node.attrs.style };
    hook.value = ['p', attrs, 0];
    next();
  },
};

export default rightAlignNode;

//RightAlign.js

import { injectable } from 'inversify';
import { Tools, Commands } from 'wax-prosemirror-core';

@injectable()
export default class RightAlign extends Tools {
  title = 'Right Align';
  icon = 'rightAlign';
  name = 'rightAlign';

  get run() {
    return (state, dispatch) => {
      console.log(state.config.schema.nodes)
      Commands.setBlockType(state.config.schema.nodes.centerAlign, {
        class: 'paragraph', style: 'text-align: right'
      })(state, dispatch);
    };
  }

  select = (state, activeViewId, activeView) => {
    const { disallowedTools } = activeView.props;
    if (disallowedTools.includes('rightAlign')) return false;
    return true;
  };

  get enable() {
    return state => {
      Commands.setBlockType(state.config.schema.nodes.centerAlign, {
        class: 'paragraph', style: 'text-align: right'
      })(state, dispatch);
    };
  }
}

@Ramnath-Karthikesan
Copy link
Author

Ramnath-Karthikesan commented Apr 13, 2023

@christos8333 finally got the text-align feature to work.

This is my implementation

To perform center alignment:
Creating a center align service - centerAlignService.js

import { Service } from 'wax-prosemirror-core';
import CenterAlign from './CenterAlign';

class CenterAlignService extends Service {
  register() {
    this.container.bind('CenterAlign').to(CenterAlign);
  }
}

export default CenterAlignService;

CenterAlign.js

import { injectable } from 'inversify';
import { Tools, Commands } from 'wax-prosemirror-core';

@injectable()
export default class CenterAlign extends Tools {
  title = 'Center Align';
  icon = 'centerAlign';
  name = 'centerAlign';

  get run() {
    return (state, dispatch) => {
      Commands.setAttr({style: "text-align: center"})(state, dispatch)
    };
  }

  select = (state, activeViewId, activeView) => {
    const { disallowedTools } = activeView.props;
    if (disallowedTools.includes('centerAlign')) return false;
    return true;
  };

  get enable() {
    return state => {
      Commands.setAttr({style: "text-align: center"})(state, dispatch)
    };
  }
}

and finally setAttr function inside Commands.js

const setAttr = (attrs= {}) => {
  return (state, dispatch) => {
    const { from, to } = state.selection;
    let { tr } = state;
    state.doc.nodesBetween(from, to, (node, pos) => {
      
      if (node.isTextblock){
        const index = state.doc.resolve(pos);
        const currentNode = state.tr.doc.nodeAt(pos);
        
        // console.log(currentNode);
        let node = state.tr.doc.nodeAt(index.before(1));
        let nodePos = index.before(1)
        if((currentNode.type.name == 'paragraph' && node.type.name == 'orderedlist') || (currentNode.type.name == 'paragraph' && node.type.name == 'bulletlist') || (currentNode.type.name == 'paragraph' && node.type.name == 'table')){
          node = currentNode;
          nodePos = pos;
        }
        tr.setNodeMarkup(nodePos, node.type, {...node.attrs, ...attrs}, node.marks);
      }
    });
    dispatch(tr);
    return true;
  };
}

Add the style attribute to all the tags in the schema like below:

paragraph: {
      group: 'block',
      content: 'inline*',
      attrs: {
        id: { default: '' },
        class: { default: '' },
        style: {default: null},
        track: { default: [] },
        group: { default: '' },
        viewid: { default: '' },
      },
      parseDOM: [
        {
          tag: 'p.paragraph',
          getAttrs(dom) {
            return {
              id: dom.dataset.id,
              class: dom.getAttribute('class'),
              style: dom.getAttribute('style'),
              track: SchemaHelpers.parseTracks(dom.dataset.track),
              group: dom.dataset.group,
              viewid: dom.dataset.viewid,
            };
          },
        },
      ],
      toDOM(node) {
        const attrs = SchemaHelpers.blockLevelToDOM(node);
        return ['p', {...attrs, style: node.attrs.style}, 0];
      },
    },

The final part is to integrate it with track changes. Any suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants