Comments (3)
Hey @su27, glad to know this is useful to you! This is getting fairly close to a stable 1.0, although there are parts of the API that still need work.
For decorators, I think this would be what you are looking for: https://github.com/springload/draftjs_exporter/blob/master/draftjs_exporter/entities.py. I'm not 100% familiar with Draft.js terminology, I think this should be called decorators.py
.
This part of the API is trying to look like React's. Here is a Button decorator:
class Button():
def render(self, props):
data = props.get('data', {})
href = data.get('href', '#')
icon = data.get('icon', None)
text = data.get('text', '')
return DOM.create_element('a', {'class': 'icon-text' if icon else None, 'href': href},
DOM.create_element(Icon, {'name': icon}) if icon else None,
DOM.create_element('span', {'class': 'icon-text__text'}, text) if icon else text,
)
Note that it uses another Icon
component/decorator, which is defined in that same file. I wouldn't rely on those built-in decorators, they are mostly here to show how this API is supposed to work. Compared to React's API / JSX, this:
- Does not convert camelCase propNames to kebab-case HTML attributes. You're supposed to provide the HTML attributes. Not sure what I think of this.
- Supports conditional rendering (
DOM.create_element(...) if thing else None
). - Does not support children as an array (I think this will come, just haven't had the use for it yet).
To build your own decorators, there are two options:
DOM.create_element
is meant to be the go-to API to create HTML elements for decorators.DOM.parse_html
is meant to facilitate the integration with other tools like existing templates or some other HTML generation. You should be able to give it an HTML string and it should "just work".
Then, when configuring the exporter this would look like:
config = {
'entity_decorators': {
'LINK': WagtailLink(),
'MODEL': WagtailModel(),
'TOKEN': Null(),
'DOCUMENT': Document(),
'WAGTAIL_IMAGE': WagtailImage(),
'WAGTAIL_EMBED': WagtailEmbed(),
'ANCHOR': Anchor(),
},
'block_map': BLOCK_MAP,
}
And here are the definitions for those various decorators (part of a codebase using this library, that I plan to make public, but haven't had the chance to yet):
class WagtailLink():
def render(self, props):
data = props.get('data', {})
if 'id' in data:
try:
page = Page.objects.get(id=data['id'])
href = page.url
except Page.DoesNotExist:
# TODO If the page does not exist anymore, we want a 404.
href = data.get('url', '#')
else:
href = data.get('url', '#')
return DOM.create_element('a', {
'href': href,
'title': data.get('title'),
})
class WagtailModel():
def render(self, props):
data = props.get('data', {})
href = '#'
className = ''
# TODO I'm sure there is a better way to do this.
# See http://stackoverflow.com/questions/4881607/django-get-model-from-string
# See http://stackoverflow.com/questions/1221240/django-content-type-how-do-i-get-an-object
if data['contentType'] == 'location.Location':
try:
location = Location.objects.get(pk=data['id'])
href = reverse('locator_detail', args=[location.type_slug, location.id, location.slug])
className = 'link--location'
except Location.DoesNotExist:
raise()
# TODO If the page does not exist anymore, we want a 404.
return DOM.create_element('a', {
'className': className,
'href': href,
})
class WagtailImage():
"""
Inspired by:
- https://github.com/torchbox/wagtail/blob/master/wagtail/wagtailimages/rich_text.py
- https://github.com/torchbox/wagtail/blob/master/wagtail/wagtailimages/shortcuts.py
- https://github.com/torchbox/wagtail/blob/master/wagtail/wagtailimages/formats.py
"""
def render(self, props):
image_model = get_image_model()
alignment = props['data'].get('alignment', 'left')
alt_text = props['data'].get('altText', '')
try:
image = image_model.objects.get(id=props['data']['id'])
except image_model.DoesNotExist:
return DOM.create_element('img', {'alt': alt_text})
image_format = get_image_format(alignment)
rendition = get_rendition_or_not_found(image, image_format.filter_spec)
return DOM.create_element('img', dict(rendition.attrs_dict, **{
'class': image_format.classnames,
'src': rendition.url,
'alt': alt_text,
}))
class WagtailEmbed():
"""
Inspired by: https://github.com/torchbox/wagtail/blob/master/wagtail/wagtailembeds/rich_text.py
"""
def render(self, props):
return DOM.parse_html(embed_to_frontend_html(props['data']['url']))
class Anchor():
def render(self, props):
return DOM.create_element('a', {
'href': '#%s' % props['data'].get('slug', '')
})
class Icon():
def render(self, props):
href = 'icon-%s' % props['name']
return DOM.create_element('svg', {'class': 'icon'},
DOM.create_element('use', {'xlink:href': href}),
)
class Document():
"""
Based on deluxetext/filters.py DocumentLinkParser.
"""
def render(self, props):
document_model = get_document_model()
doc = document_model.objects.get(id=props['data']['id'])
extended_file = get_extended_file_information(file=doc.file)
extension = extended_file.get('extension')
# TODO Can we reuse more stuff from https://github.com/torchbox/wagtail/tree/master/wagtail/wagtaildocs?
metadata_text = '({size} {extension})'.format(
size=extended_file.get('size'),
extension=extension,
)
return DOM.create_element('a', {'href': doc.url},
DOM.create_element('span', {'class': 'file-info icon-text'},
DOM.create_element(Icon, {'name': extension.lower()}),
DOM.create_element('span', {'class': 'icon-text__text'}, metadata_text),
),
)
One more note: I'm not sure why we went with decorators as classes with a render
method instead of just the render
function being the decorator. Perhaps there is a use case that I forgot about. Perhaps we would support both, like React's stateless function components, and class components. Expect API churn.
What do you think? Is this what you're after?
from draftjs_exporter.
Hi @thibaudcolas,
Thanks very much for your detailed response!
In fact, I've read your code before, and understand the way you mentioned above, actually I've defined a lot of custom entities for my own need, for example, Image
with caption on bottom of the photo, Video
, Separator
as a dividing line, etc. I noticed you called this mechanism decorator
, which is totally fine but different from facebook's terminology. I think they just call it entity.
The "decorator" I mentioned in the title means another mechanism which is defined here, it's just simple regular expression search and replace in the blocks, according to some user defined strategies, for example: replace @someone
to blue spans, or replace urls in the text to actual links. As to implement, I suppose you could simply treat them as another kind of commands.
And yes! I did wonder why not just use render
function instead of the instance method, but I think maybe it has own benefit, we can define the class with not only render strategy, but much more stuff, like what attributes are required or optional in the data
dict, etc. So I think it's just fine.
Another wondering: when I write those custom entities, I find it quite trivial to define complex html structure with the create_element
method. Maybe we can think about adopting template rendering? Even JSX for isomorphically rendering? OK that's a little too far.
from draftjs_exporter.
Doh, I actually never used those things but they do look like a very important part of the Draft.js API to cover. Thank you for the details!
from draftjs_exporter.
Related Issues (20)
- Experiment with PEP-484 type hints HOT 1
- Provide readme in reStructuredText format for pypi HOT 8
- Entities with adjacent offset are rendered incorrectly HOT 6
- Drop support for Python 2.7
- QUESTION - Can I create JSON from HTML with this code? HOT 2
- DOM class variable issue with multiple engines (draftjs_exporter_markdown) HOT 4
- Publish as a wheel βΒ and hash issues with newly-published distributions on existing releases v2.1.7, v2.1.6, v2.1.5 HOT 5
- Nested inline style configuration question HOT 3
- Is it possible to export draftjs state as plain text (i.e., without any html tags) HOT 1
- How to interlink with mathjax to export math equations HTML HOT 1
- How to apply text align inline style inside header tag? HOT 1
- Improve dependencies compatibility definition, and testing HOT 3
- Change default engine to the new dependency-free one introduced in #77 HOT 4
- Exporter loads the html5lib engine even if it isn't used HOT 1
- style_map components should be given data on render
- Entity renderers should be given more data on render HOT 1
- Unicode decode error installing on Ubuntu 16.04 HOT 2
- Unicode decode reading markdown file HOT 4
- Missing git tags for 2.1.1 HOT 2
- The setup.py in windows can cause a encoding error. HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from draftjs_exporter.