panda-cms 0.7.5 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/components/panda/cms/admin/user_activity_component.html.erb +2 -2
- data/app/components/panda/cms/admin/user_activity_component.rb +2 -2
- data/app/components/panda/cms/admin/user_display_component.html.erb +1 -1
- data/app/components/panda/cms/admin/user_display_component.rb +2 -2
- data/app/components/panda/cms/rich_text_component.rb +5 -5
- data/app/controllers/panda/cms/admin/base_controller.rb +18 -0
- data/app/controllers/panda/cms/admin/block_contents_controller.rb +1 -2
- data/app/controllers/panda/cms/admin/dashboard_controller.rb +5 -4
- data/app/controllers/panda/cms/admin/files_controller.rb +1 -3
- data/app/controllers/panda/cms/admin/forms_controller.rb +3 -4
- data/app/controllers/panda/cms/admin/menus_controller.rb +2 -3
- data/app/controllers/panda/cms/admin/pages_controller.rb +7 -7
- data/app/controllers/panda/cms/admin/posts_controller.rb +9 -10
- data/app/controllers/panda/cms/admin/settings/bulk_editor_controller.rb +4 -4
- data/app/controllers/panda/cms/admin/settings_controller.rb +2 -3
- data/app/controllers/panda/cms/application_controller.rb +13 -5
- data/app/controllers/panda/cms/pages_controller.rb +2 -2
- data/app/helpers/panda/cms/application_helper.rb +3 -3
- data/app/helpers/panda/cms/asset_helper.rb +14 -1
- data/app/javascript/panda/cms/application_panda_cms.js +2 -34
- data/app/javascript/panda/cms/controllers/index.js +5 -15
- data/app/models/panda/cms/block_content.rb +1 -1
- data/app/models/panda/cms/current.rb +3 -12
- data/app/models/panda/cms/post.rb +3 -3
- data/app/models/panda/cms/visit.rb +1 -1
- data/app/views/layouts/panda/cms/application.html.erb +1 -1
- data/app/views/panda/cms/admin/dashboard/show.html.erb +2 -2
- data/app/views/panda/cms/admin/files/index.html.erb +1 -1
- data/app/views/panda/cms/admin/forms/index.html.erb +4 -4
- data/app/views/panda/cms/admin/forms/new.html.erb +2 -2
- data/app/views/panda/cms/admin/forms/show.html.erb +1 -1
- data/app/views/panda/cms/admin/menus/index.html.erb +4 -4
- data/app/views/panda/cms/admin/pages/edit.html.erb +6 -6
- data/app/views/panda/cms/admin/pages/index.html.erb +5 -5
- data/app/views/panda/cms/admin/pages/new.html.erb +2 -2
- data/app/views/panda/cms/admin/posts/_form.html.erb +1 -1
- data/app/views/panda/cms/admin/posts/edit.html.erb +2 -2
- data/app/views/panda/cms/admin/posts/index.html.erb +5 -5
- data/app/views/panda/cms/admin/posts/new.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/bulk_editor/new.html.erb +1 -1
- data/app/views/panda/cms/admin/settings/index.html.erb +3 -3
- data/app/views/panda/cms/admin/shared/_breadcrumbs.html.erb +3 -3
- data/app/views/panda/cms/admin/shared/_flash.html.erb +1 -1
- data/app/views/panda/cms/admin/shared/_sidebar.html.erb +8 -8
- data/config/initializers/panda/cms.rb +6 -3
- data/config/routes.rb +8 -16
- data/db/migrate/20250106223303_add_author_id_to_panda_cms_posts.rb +3 -1
- data/db/migrate/20250126234001_create_panda_social_instagram_posts.rb +2 -0
- data/db/migrate/20250809231125_migrate_users_to_panda_core.rb +111 -0
- data/db/migrate/20250811111000_make_post_user_references_nullable.rb +11 -0
- data/lib/panda/cms/asset_loader.rb +4 -4
- data/lib/panda/cms/engine.rb +42 -98
- data/lib/panda-cms/version.rb +1 -1
- data/lib/panda-cms.rb +50 -40
- data/lib/tasks/assets.rake +162 -122
- data/lib/tasks/panda/cms/migrations.rake +13 -0
- metadata +19 -36
- data/app/builders/panda/cms/form_builder.rb +0 -225
- data/app/components/panda/cms/admin/button_component.rb +0 -70
- data/app/components/panda/cms/admin/container_component.rb +0 -13
- data/app/components/panda/cms/admin/flash_message_component.rb +0 -47
- data/app/components/panda/cms/admin/heading_component.rb +0 -46
- data/app/components/panda/cms/admin/panel_component.rb +0 -13
- data/app/components/panda/cms/admin/table_component.rb +0 -46
- data/app/components/panda/cms/admin/tag_component.rb +0 -35
- data/app/constraints/panda/cms/admin_constraint.rb +0 -21
- data/app/controllers/panda/cms/admin/my_profile_controller.rb +0 -44
- data/app/controllers/panda/cms/admin/sessions_controller.rb +0 -92
- data/app/javascript/panda/cms/controllers/theme_form_controller.js +0 -25
- data/app/javascript/panda/cms/editor/css_extractor.js +0 -80
- data/app/javascript/panda/cms/editor/editor_js_config.js +0 -306
- data/app/javascript/panda/cms/editor/editor_js_initializer.js +0 -334
- data/app/javascript/panda/cms/editor/plain_text_editor.js +0 -110
- data/app/javascript/panda/cms/editor/resource_loader.js +0 -204
- data/app/javascript/panda/cms/editor/rich_text_editor.js +0 -162
- data/app/models/panda/cms/breadcrumb.rb +0 -14
- data/app/models/panda/cms/user.rb +0 -33
- data/app/services/panda/cms/html_to_editor_js_converter.rb +0 -195
- data/app/views/panda/cms/admin/my_profile/edit.html.erb +0 -35
- data/app/views/panda/cms/admin/sessions/new.html.erb +0 -17
- data/db/migrate/20250504221812_add_current_theme_to_panda_cms_users.rb +0 -7
- data/lib/panda/cms/editor_js/blocks/alert.rb +0 -36
- data/lib/panda/cms/editor_js/blocks/base.rb +0 -35
- data/lib/panda/cms/editor_js/blocks/header.rb +0 -17
- data/lib/panda/cms/editor_js/blocks/image.rb +0 -39
- data/lib/panda/cms/editor_js/blocks/list.rb +0 -34
- data/lib/panda/cms/editor_js/blocks/paragraph.rb +0 -18
- data/lib/panda/cms/editor_js/blocks/quote.rb +0 -44
- data/lib/panda/cms/editor_js/blocks/table.rb +0 -52
- data/lib/panda/cms/editor_js/renderer.rb +0 -127
- data/lib/panda/cms/editor_js.rb +0 -18
- data/lib/panda/cms/editor_js_content.rb +0 -61
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Panda
|
4
|
-
module CMS
|
5
|
-
module Admin
|
6
|
-
class MyProfileController < ApplicationController
|
7
|
-
before_action :set_initial_breadcrumb, only: %i[edit update]
|
8
|
-
before_action :authenticate_admin_user!
|
9
|
-
|
10
|
-
# Shows the edit form for the current user's profile
|
11
|
-
# @type GET
|
12
|
-
# @return void
|
13
|
-
def edit
|
14
|
-
render :edit, locals: {user: current_user}
|
15
|
-
end
|
16
|
-
|
17
|
-
# Updates the current user's profile
|
18
|
-
# @type PATCH/PUT
|
19
|
-
# @return void
|
20
|
-
def update
|
21
|
-
if current_user.update(user_params)
|
22
|
-
flash[:success] = "Your profile has been updated successfully."
|
23
|
-
redirect_to edit_admin_my_profile_path
|
24
|
-
else
|
25
|
-
render :edit, locals: {user: current_user}, status: :unprocessable_entity
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def set_initial_breadcrumb
|
32
|
-
add_breadcrumb "My Profile", edit_admin_my_profile_path
|
33
|
-
end
|
34
|
-
|
35
|
-
# Only allow a list of trusted parameters through
|
36
|
-
# @type private
|
37
|
-
# @return ActionController::StrongParameters
|
38
|
-
def user_params
|
39
|
-
params.require(:user).permit(:firstname, :lastname, :email, :current_theme)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Panda
|
4
|
-
module CMS
|
5
|
-
module Admin
|
6
|
-
class SessionsController < ApplicationController
|
7
|
-
layout "panda/cms/public"
|
8
|
-
|
9
|
-
def new
|
10
|
-
@providers = Panda::CMS.config.authentication.select { |_, v| v[:enabled] && !v[:hidden] }.keys
|
11
|
-
end
|
12
|
-
|
13
|
-
def create
|
14
|
-
user_info = request.env.dig("omniauth.auth", "info")
|
15
|
-
provider = params[:provider].to_sym
|
16
|
-
|
17
|
-
unless Panda::CMS.config.authentication.dig(provider, :enabled)
|
18
|
-
Rails.logger.error "Authentication provider '#{provider}' is not enabled"
|
19
|
-
redirect_to admin_login_path, flash: {error: t("panda.cms.admin.sessions.create.error")}
|
20
|
-
return
|
21
|
-
end
|
22
|
-
|
23
|
-
user = Panda::CMS::User.find_by(email: user_info["email"])
|
24
|
-
|
25
|
-
if !user && Panda::CMS.config.authentication.dig(provider, :create_account_on_first_login)
|
26
|
-
create_as_admin = Panda::CMS.config.authentication.dig(provider, :create_as_admin)
|
27
|
-
|
28
|
-
# Always create the first user as admin, regardless of what our settings look like
|
29
|
-
# else we can't ever really login. :)
|
30
|
-
create_as_admin = true if !create_as_admin && Panda::CMS::User.count.zero?
|
31
|
-
|
32
|
-
if user_info["first_name"] && user_info["last_name"]
|
33
|
-
firstname = user_info["first_name"]
|
34
|
-
lastname = user_info["last_name"]
|
35
|
-
elsif user_info["name"]
|
36
|
-
firstname, lastname = user_info["name"].split(" ", 2)
|
37
|
-
end
|
38
|
-
|
39
|
-
user = User.find_or_create_by(
|
40
|
-
email: user_info["email"]
|
41
|
-
) do |u|
|
42
|
-
u.firstname = firstname
|
43
|
-
u.lastname = lastname
|
44
|
-
u.admin = create_as_admin
|
45
|
-
u.image_url = user_info["image"]
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
if user.nil?
|
50
|
-
# User can't be found with this email address
|
51
|
-
Rails.logger.error "User does not exist: #{user_info["email"]}"
|
52
|
-
redirect_to admin_login_path, flash: {error: t("panda.cms.admin.sessions.create.error")}
|
53
|
-
return
|
54
|
-
end
|
55
|
-
|
56
|
-
unless user.admin?
|
57
|
-
# User can't be found with this email address or can't login
|
58
|
-
Rails.logger.error "User ID #{user.id} attempted admin login, is not admin."
|
59
|
-
redirect_to admin_login_path, flash: {error: t("panda.cms.admin.sessions.create.error")}
|
60
|
-
return
|
61
|
-
end
|
62
|
-
|
63
|
-
session[:user_id] = user.id
|
64
|
-
Panda::CMS::Current.user = user
|
65
|
-
|
66
|
-
redirect_path = request.env["omniauth.origin"] || admin_dashboard_path
|
67
|
-
redirect_to redirect_path, flash: {success: t("panda.cms.admin.sessions.create.success")}
|
68
|
-
rescue ::OmniAuth::Strategies::OAuth2::CallbackError => e
|
69
|
-
Rails.logger.error "OAuth2 login callback error: #{e.message}"
|
70
|
-
redirect_to admin_login_path, flash: {error: t("panda.cms.admin.sessions.create.error")}
|
71
|
-
rescue ::OAuth2::Error => e
|
72
|
-
Rails.logger.error "OAuth2 login error: #{e.message}"
|
73
|
-
redirect_to admin_login_path, flash: {error: t("panda.cms.admin.sessions.create.error")}
|
74
|
-
rescue => e
|
75
|
-
Rails.logger.error "Unknown login error: #{e.message}"
|
76
|
-
redirect_to admin_login_path, flash: {error: t("panda.cms.admin.sessions.create.error")}
|
77
|
-
end
|
78
|
-
|
79
|
-
def failure
|
80
|
-
Rails.logger.error "Login failure: #{params[:message]} from #{params[:origin]} using #{params[:strategy]}"
|
81
|
-
redirect_to admin_login_path, flash: {error: t("panda.cms.admin.sessions.create.error")}
|
82
|
-
end
|
83
|
-
|
84
|
-
def destroy
|
85
|
-
Panda::CMS::Current.user = nil
|
86
|
-
session[:user_id] = nil
|
87
|
-
redirect_to admin_login_path, flash: {success: t("panda.cms.admin.sessions.destroy.success")}
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
import { Controller } from "@hotwired/stimulus";
|
2
|
-
|
3
|
-
// Connects to data-controller="theme-form"
|
4
|
-
export default class extends Controller {
|
5
|
-
connect() {
|
6
|
-
// Ensure submit button is enabled on connect
|
7
|
-
this.enableSubmitButton();
|
8
|
-
}
|
9
|
-
|
10
|
-
updateTheme(event) {
|
11
|
-
const newTheme = event.target.value;
|
12
|
-
document.documentElement.dataset.theme = newTheme;
|
13
|
-
}
|
14
|
-
|
15
|
-
enableSubmitButton() {
|
16
|
-
// Find the submit button in the form and ensure it's enabled
|
17
|
-
const form = this.element;
|
18
|
-
if (form) {
|
19
|
-
const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');
|
20
|
-
if (submitButton) {
|
21
|
-
submitButton.disabled = false;
|
22
|
-
}
|
23
|
-
}
|
24
|
-
}
|
25
|
-
}
|
@@ -1,80 +0,0 @@
|
|
1
|
-
export class CSSExtractor {
|
2
|
-
/**
|
3
|
-
* Extracts CSS rules from within a specific selector and transforms them for EditorJS
|
4
|
-
* @param {string} css - The CSS content to parse
|
5
|
-
* @returns {string} The extracted and transformed CSS rules
|
6
|
-
*/
|
7
|
-
static extractStyles(css) {
|
8
|
-
const rules = []
|
9
|
-
let inComponents = false
|
10
|
-
let inContentRule = false
|
11
|
-
let braceCount = 0
|
12
|
-
let currentRule = ''
|
13
|
-
|
14
|
-
// Split CSS into lines and process each line
|
15
|
-
const lines = css.split('\n')
|
16
|
-
|
17
|
-
for (const line of lines) {
|
18
|
-
const trimmedLine = line.trim()
|
19
|
-
|
20
|
-
// Check if we're entering components layer
|
21
|
-
if (trimmedLine === '@layer components {') {
|
22
|
-
inComponents = true
|
23
|
-
continue
|
24
|
-
}
|
25
|
-
|
26
|
-
// Only process lines within components layer
|
27
|
-
if (!inComponents) continue
|
28
|
-
|
29
|
-
// If we find the .content selector
|
30
|
-
if (!inContentRule && trimmedLine.startsWith('.content')) {
|
31
|
-
inContentRule = true
|
32
|
-
braceCount++
|
33
|
-
// Transform the selector for EditorJS
|
34
|
-
currentRule = '.codex-editor__redactor .ce-block .ce-block__content'
|
35
|
-
if (trimmedLine.includes('{')) {
|
36
|
-
currentRule += ' {'
|
37
|
-
}
|
38
|
-
continue
|
39
|
-
}
|
40
|
-
|
41
|
-
// If we're inside a content rule
|
42
|
-
if (inContentRule) {
|
43
|
-
// Transform selectors for EditorJS
|
44
|
-
let transformedLine = line
|
45
|
-
.replace(/\.content\s+/g, '.codex-editor__redactor .ce-block .ce-block__content ')
|
46
|
-
.replace(/\bh1\b(?![-_])/g, 'h1.ce-header')
|
47
|
-
.replace(/\bh2\b(?![-_])/g, 'h2.ce-header')
|
48
|
-
.replace(/\bh3\b(?![-_])/g, 'h3.ce-header')
|
49
|
-
.replace(/\bul\b(?![-_])/g, 'ul.cdx-list')
|
50
|
-
.replace(/\bol\b(?![-_])/g, 'ol.cdx-list')
|
51
|
-
.replace(/\bli\b(?![-_])/g, 'li.cdx-list__item')
|
52
|
-
.replace(/\bblockquote\b(?![-_])/g, '.cdx-quote')
|
53
|
-
|
54
|
-
currentRule += '\n' + transformedLine
|
55
|
-
|
56
|
-
// Count braces to handle nested rules
|
57
|
-
braceCount += (trimmedLine.match(/{/g) || []).length
|
58
|
-
braceCount -= (trimmedLine.match(/}/g) || []).length
|
59
|
-
|
60
|
-
// If braces are balanced, we've found the end of the rule
|
61
|
-
if (braceCount === 0) {
|
62
|
-
rules.push(currentRule)
|
63
|
-
inContentRule = false
|
64
|
-
currentRule = ''
|
65
|
-
}
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
return rules.join('\n\n')
|
70
|
-
}
|
71
|
-
|
72
|
-
/**
|
73
|
-
* Gets all styles from a stylesheet that apply to the editor
|
74
|
-
* @param {string} css - The CSS content to parse
|
75
|
-
* @returns {string} The extracted CSS rules
|
76
|
-
*/
|
77
|
-
static getEditorStyles(css) {
|
78
|
-
return this.extractStyles(css)
|
79
|
-
}
|
80
|
-
}
|
@@ -1,306 +0,0 @@
|
|
1
|
-
export const EDITOR_JS_RESOURCES = [
|
2
|
-
"https://cdn.jsdelivr.net/npm/@editorjs/editorjs@2.28.2",
|
3
|
-
"https://cdn.jsdelivr.net/npm/@editorjs/paragraph@2.11.3",
|
4
|
-
"https://cdn.jsdelivr.net/npm/@editorjs/header@2.8.1",
|
5
|
-
"https://cdn.jsdelivr.net/npm/@editorjs/nested-list@1.4.2",
|
6
|
-
"https://cdn.jsdelivr.net/npm/@editorjs/quote@2.6.0",
|
7
|
-
"https://cdn.jsdelivr.net/npm/@editorjs/simple-image@1.6.0",
|
8
|
-
"https://cdn.jsdelivr.net/npm/@editorjs/table@2.3.0",
|
9
|
-
"https://cdn.jsdelivr.net/npm/@editorjs/embed@2.7.0"
|
10
|
-
]
|
11
|
-
|
12
|
-
// Allow applications to add their own resources
|
13
|
-
if (window.PANDA_CMS_EDITOR_JS_RESOURCES) {
|
14
|
-
EDITOR_JS_RESOURCES.push(...window.PANDA_CMS_EDITOR_JS_RESOURCES)
|
15
|
-
}
|
16
|
-
|
17
|
-
export const EDITOR_JS_CSS = `
|
18
|
-
.codex-editor {
|
19
|
-
position: relative;
|
20
|
-
}
|
21
|
-
.codex-editor::before {
|
22
|
-
content: '';
|
23
|
-
position: absolute;
|
24
|
-
left: 0;
|
25
|
-
top: 0;
|
26
|
-
bottom: 0;
|
27
|
-
width: 65px;
|
28
|
-
margin-right: 5px;
|
29
|
-
background-color: #f9fafb;
|
30
|
-
border-right: 2px dashed #e5e7eb;
|
31
|
-
z-index: 0;
|
32
|
-
}
|
33
|
-
.ce-block {
|
34
|
-
padding-left: 70px;
|
35
|
-
position: relative;
|
36
|
-
min-height: 40px;
|
37
|
-
margin: 0;
|
38
|
-
padding-bottom: 1em;
|
39
|
-
}
|
40
|
-
.ce-block__content {
|
41
|
-
position: relative;
|
42
|
-
max-width: none;
|
43
|
-
margin: 0;
|
44
|
-
}
|
45
|
-
.ce-paragraph {
|
46
|
-
padding: 0;
|
47
|
-
line-height: 1.6;
|
48
|
-
min-height: 1.6em;
|
49
|
-
margin: 0;
|
50
|
-
}
|
51
|
-
/* Override inherited heading styles */
|
52
|
-
.ce-header h1,
|
53
|
-
.ce-header h2,
|
54
|
-
.ce-header h3,
|
55
|
-
.ce-header h4,
|
56
|
-
.ce-header h5,
|
57
|
-
.ce-header h6 {
|
58
|
-
margin: 0;
|
59
|
-
padding: 0;
|
60
|
-
line-height: 1.6;
|
61
|
-
font-weight: 600;
|
62
|
-
}
|
63
|
-
.ce-header h1 { font-size: 2em; }
|
64
|
-
.ce-header h2 { font-size: 1.5em; }
|
65
|
-
.ce-header h3 { font-size: 1.17em; }
|
66
|
-
.ce-header h4 { font-size: 1em; }
|
67
|
-
.ce-header h5 { font-size: 0.83em; }
|
68
|
-
.ce-header h6 { font-size: 0.67em; }
|
69
|
-
|
70
|
-
.codex-editor__redactor {
|
71
|
-
padding-bottom: 150px !important;
|
72
|
-
min-height: 100px !important;
|
73
|
-
}
|
74
|
-
/* Base toolbar styles */
|
75
|
-
.ce-toolbar {
|
76
|
-
left: 0 !important;
|
77
|
-
right: auto !important;
|
78
|
-
background: none !important;
|
79
|
-
position: absolute !important;
|
80
|
-
width: 65px !important;
|
81
|
-
height: 40px !important;
|
82
|
-
display: flex !important;
|
83
|
-
align-items: center !important;
|
84
|
-
justify-content: flex-start !important;
|
85
|
-
padding: 0 !important;
|
86
|
-
margin-left: -70px !important;
|
87
|
-
margin-top: -5px !important;
|
88
|
-
opacity: 1 !important;
|
89
|
-
visibility: visible !important;
|
90
|
-
pointer-events: all !important;
|
91
|
-
z-index: 2 !important;
|
92
|
-
}
|
93
|
-
/* Ensure toolbar is visible for all blocks */
|
94
|
-
.ce-block .ce-toolbar {
|
95
|
-
display: flex !important;
|
96
|
-
opacity: 1 !important;
|
97
|
-
visibility: visible !important;
|
98
|
-
}
|
99
|
-
.ce-toolbar__content {
|
100
|
-
max-width: none;
|
101
|
-
left: 70px !important;
|
102
|
-
display: flex !important;
|
103
|
-
position: relative !important;
|
104
|
-
}
|
105
|
-
.ce-toolbar__actions {
|
106
|
-
position: relative !important;
|
107
|
-
left: 5px !important;
|
108
|
-
opacity: 1 !important;
|
109
|
-
visibility: visible !important;
|
110
|
-
background: transparent !important;
|
111
|
-
z-index: 2;
|
112
|
-
display: flex !important;
|
113
|
-
align-items: center !important;
|
114
|
-
gap: 5px !important;
|
115
|
-
height: 40px !important;
|
116
|
-
padding: 0 !important;
|
117
|
-
}
|
118
|
-
.ce-toolbar__plus {
|
119
|
-
position: relative !important;
|
120
|
-
left: 0px !important;
|
121
|
-
opacity: 1 !important;
|
122
|
-
visibility: visible !important;
|
123
|
-
background: transparent !important;
|
124
|
-
border: none !important;
|
125
|
-
z-index: 2;
|
126
|
-
display: block !important;
|
127
|
-
}
|
128
|
-
.ce-toolbar__settings-btn {
|
129
|
-
position: relative !important;
|
130
|
-
left: -10px !important;
|
131
|
-
opacity: 1 !important;
|
132
|
-
visibility: visible !important;
|
133
|
-
background: transparent !important;
|
134
|
-
border: none !important;
|
135
|
-
z-index: 2;
|
136
|
-
display: block !important;
|
137
|
-
}
|
138
|
-
/* Style the search input */
|
139
|
-
.ce-popover__search {
|
140
|
-
padding-left: 3px !important;
|
141
|
-
}
|
142
|
-
.ce-popover__search input {
|
143
|
-
outline: none !important;
|
144
|
-
box-shadow: none !important;
|
145
|
-
border: none !important;
|
146
|
-
}
|
147
|
-
.ce-popover__search input::placeholder {
|
148
|
-
content: 'Search';
|
149
|
-
}
|
150
|
-
/* Ensure popups still work */
|
151
|
-
.ce-popover {
|
152
|
-
z-index: 4;
|
153
|
-
}
|
154
|
-
.ce-inline-toolbar {
|
155
|
-
z-index: 3;
|
156
|
-
}
|
157
|
-
/* Override any hiding behavior */
|
158
|
-
.ce-toolbar--closed,
|
159
|
-
.ce-toolbar--opened,
|
160
|
-
.ce-toolbar--showed {
|
161
|
-
display: flex !important;
|
162
|
-
opacity: 1 !important;
|
163
|
-
visibility: visible !important;
|
164
|
-
}
|
165
|
-
/* Force toolbar to show on every block */
|
166
|
-
.ce-block:not(:focus):not(:hover) .ce-toolbar,
|
167
|
-
.ce-block--selected .ce-toolbar,
|
168
|
-
.ce-block--focused .ce-toolbar,
|
169
|
-
.ce-block--hover .ce-toolbar {
|
170
|
-
opacity: 1 !important;
|
171
|
-
visibility: visible !important;
|
172
|
-
display: flex !important;
|
173
|
-
}
|
174
|
-
|
175
|
-
/* Ensure last block has bottom spacing */
|
176
|
-
.ce-block:last-child {
|
177
|
-
padding-bottom: 2em;
|
178
|
-
}
|
179
|
-
|
180
|
-
/* Reset all block type margins */
|
181
|
-
.ce-header,
|
182
|
-
.ce-paragraph,
|
183
|
-
.ce-quote,
|
184
|
-
.ce-list {
|
185
|
-
margin: 0 !important;
|
186
|
-
padding: 0 !important;
|
187
|
-
}
|
188
|
-
`
|
189
|
-
|
190
|
-
export const getEditorConfig = (elementId, previousData, doc = document) => {
|
191
|
-
// Validate holder element exists
|
192
|
-
const holder = doc.getElementById(elementId)
|
193
|
-
if (!holder) {
|
194
|
-
throw new Error(`Editor holder element ${elementId} not found`)
|
195
|
-
}
|
196
|
-
|
197
|
-
// Get the correct window context
|
198
|
-
const win = doc.defaultView || window
|
199
|
-
|
200
|
-
// Ensure we have a clean holder element
|
201
|
-
holder.innerHTML = ""
|
202
|
-
|
203
|
-
const config = {
|
204
|
-
holder: elementId,
|
205
|
-
data: previousData || {},
|
206
|
-
placeholder: 'Click the + button to add content...',
|
207
|
-
inlineToolbar: true,
|
208
|
-
onChange: () => {
|
209
|
-
// Ensure the editor is properly initialized before handling changes
|
210
|
-
if (holder && holder.querySelector('.codex-editor')) {
|
211
|
-
const event = new Event('editor:change', { bubbles: true })
|
212
|
-
holder.dispatchEvent(event)
|
213
|
-
}
|
214
|
-
},
|
215
|
-
i18n: {
|
216
|
-
toolbar: {
|
217
|
-
filter: {
|
218
|
-
placeholder: 'Search'
|
219
|
-
}
|
220
|
-
}
|
221
|
-
},
|
222
|
-
tools: {
|
223
|
-
header: {
|
224
|
-
class: win.Header,
|
225
|
-
inlineToolbar: true,
|
226
|
-
config: {
|
227
|
-
placeholder: 'Enter a header',
|
228
|
-
levels: [1, 2, 3, 4, 5, 6],
|
229
|
-
defaultLevel: 2
|
230
|
-
}
|
231
|
-
},
|
232
|
-
paragraph: {
|
233
|
-
class: win.Paragraph,
|
234
|
-
inlineToolbar: true,
|
235
|
-
config: {
|
236
|
-
placeholder: 'Start writing or press Tab to add content...'
|
237
|
-
}
|
238
|
-
},
|
239
|
-
list: {
|
240
|
-
class: win.NestedList,
|
241
|
-
inlineToolbar: true,
|
242
|
-
config: {
|
243
|
-
defaultStyle: 'unordered',
|
244
|
-
enableLineBreaks: true
|
245
|
-
}
|
246
|
-
},
|
247
|
-
quote: {
|
248
|
-
class: win.Quote,
|
249
|
-
inlineToolbar: true,
|
250
|
-
config: {
|
251
|
-
quotePlaceholder: 'Enter a quote',
|
252
|
-
captionPlaceholder: 'Quote\'s author'
|
253
|
-
}
|
254
|
-
},
|
255
|
-
image: {
|
256
|
-
class: win.SimpleImage,
|
257
|
-
inlineToolbar: true,
|
258
|
-
config: {
|
259
|
-
placeholder: 'Paste an image URL...'
|
260
|
-
}
|
261
|
-
},
|
262
|
-
table: {
|
263
|
-
class: win.Table,
|
264
|
-
inlineToolbar: true,
|
265
|
-
config: {
|
266
|
-
rows: 2,
|
267
|
-
cols: 2
|
268
|
-
}
|
269
|
-
},
|
270
|
-
embed: {
|
271
|
-
class: win.Embed,
|
272
|
-
inlineToolbar: true,
|
273
|
-
config: {
|
274
|
-
services: {
|
275
|
-
youtube: true,
|
276
|
-
vimeo: true
|
277
|
-
}
|
278
|
-
}
|
279
|
-
}
|
280
|
-
}
|
281
|
-
}
|
282
|
-
|
283
|
-
// Remove any undefined tools from the config
|
284
|
-
config.tools = Object.fromEntries(
|
285
|
-
Object.entries(config.tools)
|
286
|
-
.filter(([_, value]) => value?.class !== undefined)
|
287
|
-
.map(([name, tool]) => {
|
288
|
-
if (!tool.class) {
|
289
|
-
throw new Error(`Tool ${name} has no class defined`)
|
290
|
-
}
|
291
|
-
return [name, tool]
|
292
|
-
})
|
293
|
-
)
|
294
|
-
|
295
|
-
// Allow applications to customize the config through Ruby
|
296
|
-
if (window.PANDA_CMS_EDITOR_JS_CONFIG) {
|
297
|
-
Object.assign(config.tools, window.PANDA_CMS_EDITOR_JS_CONFIG)
|
298
|
-
}
|
299
|
-
|
300
|
-
// Allow applications to customize the config through JavaScript
|
301
|
-
if (typeof window.customizeEditorJS === 'function') {
|
302
|
-
window.customizeEditorJS(config)
|
303
|
-
}
|
304
|
-
|
305
|
-
return config
|
306
|
-
}
|