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

CKEditor5Field #381

Closed
wants to merge 21 commits into from
Closed

Conversation

hasansezertasan
Copy link
Contributor

No description provided.

Copy link
Owner

@jowilf jowilf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution. I've added some comments; could you please review them?

starlette_admin/fields.py Outdated Show resolved Hide resolved
starlette_admin/templates/forms/ckeditor5.html Outdated Show resolved Hide resolved
@hasansezertasan hasansezertasan changed the title CKEditor5Field and CKEditor4Field CKEditor5Field Dec 8, 2023
@hasansezertasan
Copy link
Contributor Author

hasansezertasan commented Dec 9, 2023

Creating a custom JavaScript-initialized field for Starlette Admin

Since we removed CKEditor4Field from this PR, I still need it in my project. So I created this tutorial that shows how to add JavaScript-initialized fields to Starlette Admin.

Creating Field

from dataclasses import dataclass
from typing import Any,  Union, List, Dict
from dataclasses import field as dc_field
import json

from starlette.requests import Request
from starlette_admin._types import RequestAction
from starlette_admin.fields import TextAreaField


@dataclass
class CKEditor4Field(TextAreaField):
    """A field that provides a WYSIWYG editor for long text content using the
     [CKEditor4](https://ckeditor.com/) library.
    This field can be used as an alternative to the [TextAreaField][starlette_admin.fields.TextAreaField]
    to provide a more sophisticated editor for user input.
    Parameters:
        version: CKEditor4 version
        distribution: CKEditor4 distribution
        height: Editor height
        data_options: Other options to pass to SimpleMDE
    """

    class_: str = "field-ckeditor4 form-control"
    display_template: str = "displays/ckeditor4.html"
    form_template: str = "forms/ckeditor4.html"
    version: str = "4.22.1"
    distribution: str = "standard"

    height: Union[int, str] = 300
    data_options: Dict[str, Any] = dc_field(default_factory=dict)
    """For more options, see the [CKEditor 4 API docs](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html)"""

    def additional_js_links(self, request: Request, action: RequestAction) -> List[str]:
        if action.is_form():
            return [
                f"https://cdn.ckeditor.com/{self.version}/{self.distribution}/ckeditor.js",
                request.url_for("static", path="js/form.js"),
            ]
        return []

    def config(self) -> str:
        return json.dumps({
            "height": self.height,
            **self.data_options
        })

Where to write JavaScript code

So, Starlette Admin uses some JavaScript to initialize some of the fields. The JavaScript code is in the statics/js/form.js file. You can add your custom field integration here. But I don't recommend it. Because if you update Starlette Admin or try to distribute your project, you will lose your changes. So I recommend creating a new file in your custom statics folder and adding your custom field integration there.

Copy and paste this code block to your static/js/form.js file.

(function () {
    function processCustomElement(element) {
        // Your custom field integration here
    }
    $(function () {
        processCustomElement(document);
    });
})();

Add your logic to the processCustomElement function. This function will be called when the page is loaded. So you can add your custom field integration here.

(function () {
    function processCustomElement(element) {
        // CKEditor4Field integration
        $(".field-ckeditor4", element).each(function () {
            let config = $(this).attr("config");
            config = JSON.parse(config);
            CKEDITOR.replace(this, config);
        });
        // end CKEditor5Field integration
    }
    $(function () {
        processCustomElement(document);
    });
})();

I believe I don't need to tell you that you need to add a static folder to your project but still...

Add a static folder to your FastAPI project

from starlette.staticfiles import StaticFiles

app.mount(
    path="/static",
    app=StaticFiles(directory=static_dir),
    name="static",
)

Add a static folder to your Starlette Project: Static Files - Starlette

Create your templates

In your templates folder add forms and displays folders.

In the forms folder create a ckeditor4.html file and add this code block:

<div class="{% if error %}{{ field.error_class }}{% endif %}">
    <textarea name="{{ field.id }}" data-bs-toggle="autosize" class="{{ field.class_ }}
            {% if error %}{{ field.error_class }}{% endif %}" {{ field.input_params() | safe }}
            config="{{ field.config() }}"
    >{% if data %}{{ data }}{% endif %}</textarea>
    {% if field.help_text %}
        <small class="form-hint">{{ field.help_text }}</small>
    {% endif %}
</div>
{% include "forms/_error.html" %}

In the displays folder create a ckeditor4.html file and add this code block:

<p>{{ data | safe }}</p>

Conclusion

That's it. Now you can use your custom field in your models like any other field. If your application can't find your templates, check this issue: #376


@jowilf Should we add this one to the documentation?

@hasansezertasan hasansezertasan deleted the CKEditorField branch January 13, 2024 05:45
@hasansezertasan
Copy link
Contributor Author

@jowilf I have renamed the branch so GitHub automatically closed the PR. If it sounds good, I can open another PR.

@hasansezertasan
Copy link
Contributor Author

It's available in the starlette-admin-fields project.

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

Successfully merging this pull request may close these issues.

None yet

2 participants