macron-cms 1.0.0 → 2.0.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/README.md +298 -21
- data/app/assets/javascripts/macron/cms/codemirror.js +6 -0
- data/app/assets/javascripts/macron/cms/editor.js +101 -0
- data/app/assets/stylesheets/macron/cms/application.css +1 -1
- data/app/assets/stylesheets/macron/cms/codemirror.css +64 -0
- data/app/assets/tailwind/macron/cms/application.css +1 -0
- data/app/components/macron/cms/widget_component.rb +25 -3
- data/app/controllers/macron/cms/article_contents_controller.rb +117 -10
- data/app/controllers/macron/cms/articles_controller.rb +16 -2
- data/app/helpers/macron/cms/content_helper.rb +31 -0
- data/app/helpers/macron/cms/fields_helper.rb +11 -1
- data/app/models/macron/cms/article.rb +36 -0
- data/app/models/macron/cms/article_content.rb +33 -1
- data/app/views/layouts/macron/cms/application.html.erb +6 -18
- data/app/views/macron/cms/articles/_content_blocks_section.html.erb +531 -0
- data/app/views/macron/cms/articles/_form.html.erb +121 -0
- data/app/views/macron/cms/articles/show.html.erb +11 -298
- data/app/views/macron/cms/content_types/show.html.erb +33 -9
- data/app/views/macron/cms/fields/_color.html.erb +35 -0
- data/app/views/macron/cms/fields/_dropdown.html.erb +28 -0
- data/app/views/macron/cms/fields/_file.html.erb +27 -0
- data/app/views/macron/cms/fields/_json.html.erb +17 -0
- data/app/views/macron/cms/fields/_markdown.html.erb +17 -0
- data/app/views/macron/cms/shared/_flash.html.erb +34 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20261028000007_add_background_fields_to_macron_cms_articles.rb +6 -0
- data/db/migrate/20261028000008_add_text_color_to_macron_cms_articles.rb +5 -0
- data/lib/macron/cms/version.rb +1 -1
- metadata +55 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8377dd550eb5e06468380e45e6b112ea5fccdf5a791d4c382a54cbf5c8273a22
|
|
4
|
+
data.tar.gz: da9ca19a2ca4191b3591d59a594e436ae3db02686170f2186257e93c8d7d8e62
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0379d659ad66a227790269275c41272f6a88076d06ecc88c59299b2db776f00fc1cbae6180a4cc1fa0d5bab115fae1dcb8c9223a044b4a3be9dadad262cb645f'
|
|
7
|
+
data.tar.gz: 4d1dffb4a71491a89d10050f67ba69c313ce9fc0e3d1571071540ebc15f312aa75ba17b1ad840bff823b676c3621bc268143b4457e425e47ed0decb12c0912e1
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MacronCMS
|
|
2
2
|
|
|
3
|
-
[](#version)
|
|
4
4
|
[](https://rubyonrails.org)
|
|
5
5
|
[](MIT-LICENSE)
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
10
10
|
|
|
11
11
|
## Overview
|
|
12
12
|
|
|
13
|
-
**MacronCMS** brings simplicity and developer joy back to content management, without sacrificing the power needed to create dynamic digital experiences. Built natively for Ruby on Rails, it gives developers total freedom to define content structures with Rails models while effortlessly powering modern front-end presentations.
|
|
13
|
+
**MacronCMS** brings simplicity and developer joy back to content management, without sacrificing the power needed to create dynamic digital experiences. Built natively for Ruby on Rails 8, it gives developers total freedom to define content structures with Rails models while effortlessly powering modern front-end presentations.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -22,11 +22,35 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
22
22
|
- **Deletion Safeguards**: Browser confirmation dialogs on delete actions with article titles to prevent accidental deletion.
|
|
23
23
|
- **Host App Integration**: Quick links between the CMS admin console and public-facing host views.
|
|
24
24
|
|
|
25
|
-
### 🧱 Dynamic Article Content Blocks &
|
|
26
|
-
- **Multiple Blocks Per Article**: Associate an arbitrary series of content blocks with an article (`Macron::Cms::ArticleContent`). Add multiple blocks of the same content type (e.g.
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
25
|
+
### 🧱 Dynamic Article Content Blocks & In-Place Inserters
|
|
26
|
+
- **Multiple Blocks Per Article**: Associate an arbitrary series of content blocks with an article (`Macron::Cms::ArticleContent`). Add multiple blocks of the same content type (e.g. headings, markdown blocks, images) with distinct values.
|
|
27
|
+
- **In-Place Block Inserters**: Add new blocks directly above, between, or below existing blocks on the Article management screen with one click.
|
|
28
|
+
- **Inline Turbo Stream Updates**: Add, edit, and reorder content blocks with instantaneous Turbo Stream updates—no full-page reloads.
|
|
29
|
+
- **Dynamic Field Generation**: Field controls are generated dynamically based on the content type schema and validated before saving into the block's `payload` JSONB column.
|
|
30
|
+
- **7 Extensible Field Kinds**:
|
|
31
|
+
- `string`: Single-line text input.
|
|
32
|
+
- `text`: Multiline text area.
|
|
33
|
+
- `dropdown`: Select menu configured with custom option lists from the schema YAML.
|
|
34
|
+
- `color`: Interactive color picker with hex value support.
|
|
35
|
+
- `markdown`: CodeMirror-backed markdown editor with formatted preview on the article show page.
|
|
36
|
+
- `file`: Direct file attachment powered by Active Storage.
|
|
37
|
+
- `json`: Dedicated JSON data editor.
|
|
38
|
+
|
|
39
|
+
### 🎨 Article Background & Typography Customization
|
|
40
|
+
- **Custom Background Colors & Images**: Configure an optional background color or attach a background image directly to any article via Active Storage, with live image previews and one-click purge.
|
|
41
|
+
- **CSS Background Display Controls**: Fine-tune background display settings per article:
|
|
42
|
+
- **Size**: `cover`, `contain`, `auto`
|
|
43
|
+
- **Repeat**: `no-repeat`, `repeat`, `repeat-x`, `repeat-y`
|
|
44
|
+
- **Position**: `center`, `top`, `bottom`, `left`, `right`
|
|
45
|
+
- **Attachment**: `scroll`, `fixed`
|
|
46
|
+
- **Article-Wide Text Color Propagation**: Set a custom text color at the article level that automatically propagates to the host view's `<body>` and header, as well as to child `WidgetComponent` instances (headings, paragraphs, and markdown prose).
|
|
47
|
+
- **Public Style Helper**: Use `cms_article_background_style(@article)` to generate clean inline CSS applied directly to the host application's `<body>` tag.
|
|
48
|
+
|
|
49
|
+
### 📝 CodeMirror Markdown & Rich Media
|
|
50
|
+
- **CodeMirror Markdown Editor**: Integrated CodeMirror editor in the CMS admin for writing Markdown content with syntax highlighting.
|
|
51
|
+
- **Formatted Markdown Previews**: Formatted GitHub Flavored Markdown (GFM) previews rendered directly on the article management page.
|
|
52
|
+
- **Sandboxed Embeds**: Render third-party media and raw HTML snippets safely inside isolated `<iframe>` elements using `srcdoc` to prevent Active Storage download blocks and script collisions.
|
|
53
|
+
- **Responsive Media Controls**: Configure image and embed widgets with responsive width options (`25%`, `33%`, `50%`, `66%`, `75%`, `100%`) and alignment (`left`, `center`, `right`).
|
|
30
54
|
|
|
31
55
|
### ↕️ Drag-and-Drop & Button Reordering
|
|
32
56
|
- **HTML5 Drag-and-Drop**: Easily re-arrange article content blocks on the Article show page with immediate position updates and asynchronous persistence.
|
|
@@ -38,7 +62,7 @@ A simple, flexible CMS engine for Ruby on Rails, designed for building highly in
|
|
|
38
62
|
- **Rake Import Pipeline**: Idempotently sync YAML definitions into database records (`Macron::Cms::Content`) with automatic schema introspection and admin dashboard views.
|
|
39
63
|
|
|
40
64
|
### ⚡ Front-End ViewComponent Widgets
|
|
41
|
-
- **`Macron::Cms::WidgetComponent`**: Base component class providing automatic method accessors for
|
|
65
|
+
- **`Macron::Cms::WidgetComponent`**: Base component class providing automatic method accessors for schema fields, payload values, attached files (`file`, `upload`), and article-level styles (`article_text_color`).
|
|
42
66
|
- **Seamless Rendering Helpers**: Render all article content blocks ordered by `sort_order` with `<%= cms_render_article_contents(@article) %>`, or render individual blocks via `<%= cms_render_widget(article_content) %>`.
|
|
43
67
|
- **Informative Error Handling**: Raises `Macron::Cms::MissingWidgetError` if a content type does not have a corresponding `Widgets::<Name>Component` in the host application.
|
|
44
68
|
|
|
@@ -85,7 +109,15 @@ Or install it directly:
|
|
|
85
109
|
$ gem install macron-cms
|
|
86
110
|
```
|
|
87
111
|
|
|
88
|
-
###
|
|
112
|
+
### 1. Active Storage Setup
|
|
113
|
+
|
|
114
|
+
MacronCMS utilizes Active Storage for block file attachments (such as images) and article background images. If your application does not already have Active Storage installed, set it up:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
$ bin/rails active_storage:install
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. Engine Mounting & Migrations
|
|
89
121
|
|
|
90
122
|
Mount the engine inside your host application's `config/routes.rb`:
|
|
91
123
|
|
|
@@ -111,9 +143,11 @@ $ bin/rails db:migrate
|
|
|
111
143
|
Scaffold content types using the built-in Rails generator:
|
|
112
144
|
|
|
113
145
|
```bash
|
|
114
|
-
$ bin/rails generate macron:cms:content_type heading title:string level:
|
|
146
|
+
$ bin/rails generate macron:cms:content_type heading title:string level:dropdown color:color
|
|
115
147
|
$ bin/rails generate macron:cms:content_type paragraph content:text
|
|
116
|
-
$ bin/rails generate macron:cms:content_type
|
|
148
|
+
$ bin/rails generate macron:cms:content_type markdown content:markdown
|
|
149
|
+
$ bin/rails generate macron:cms:content_type image file:file width:dropdown align:dropdown caption:string
|
|
150
|
+
$ bin/rails generate macron:cms:content_type embed html:text width:dropdown align:dropdown
|
|
117
151
|
```
|
|
118
152
|
|
|
119
153
|
This generates:
|
|
@@ -121,7 +155,106 @@ This generates:
|
|
|
121
155
|
2. `app/components/widgets/<name>_component.rb` (the ViewComponent class)
|
|
122
156
|
3. `app/components/widgets/<name>_component.html.erb` (the ViewComponent template)
|
|
123
157
|
|
|
124
|
-
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### 2. Supported Field Kinds
|
|
161
|
+
|
|
162
|
+
| Kind | Description | Config Options | Accessor in Widget |
|
|
163
|
+
|---|---|---|---|
|
|
164
|
+
| `string` | Single-line text input | — | `field_name` |
|
|
165
|
+
| `text` | Multiline textarea | — | `field_name` |
|
|
166
|
+
| `dropdown` | Select dropdown menu | `options: [...]` in YAML schema | `field_name` |
|
|
167
|
+
| `color` | Color picker input (hex color code) | — | `field_name` |
|
|
168
|
+
| `markdown` | Multiline markdown with CodeMirror editor | — | `field_name` |
|
|
169
|
+
| `file` | File upload backed by Active Storage | — | `file` or `upload` (Active Storage attachment) |
|
|
170
|
+
| `json` | Formatted JSON editor | — | `field_name` |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### 3. Schema Examples
|
|
175
|
+
|
|
176
|
+
#### Section Heading (`config/content_types/heading.yml`)
|
|
177
|
+
```yaml
|
|
178
|
+
name: Heading
|
|
179
|
+
description: Section heading with customizable level and color
|
|
180
|
+
fields:
|
|
181
|
+
- name: title
|
|
182
|
+
kind: string
|
|
183
|
+
- name: level
|
|
184
|
+
kind: dropdown
|
|
185
|
+
options:
|
|
186
|
+
- h1
|
|
187
|
+
- h2
|
|
188
|
+
- h3
|
|
189
|
+
- h4
|
|
190
|
+
- h5
|
|
191
|
+
- h6
|
|
192
|
+
- name: color
|
|
193
|
+
kind: color
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### GitHub-Flavored Markdown (`config/content_types/markdown.yml`)
|
|
197
|
+
```yaml
|
|
198
|
+
name: Markdown
|
|
199
|
+
description: Rich formatted content using GitHub-flavored Markdown
|
|
200
|
+
fields:
|
|
201
|
+
- name: content
|
|
202
|
+
kind: markdown
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### Responsive Image with Active Storage (`config/content_types/image.yml`)
|
|
206
|
+
```yaml
|
|
207
|
+
name: Image
|
|
208
|
+
description: Image with configurable responsive width, alignment, and optional caption
|
|
209
|
+
fields:
|
|
210
|
+
- name: file
|
|
211
|
+
kind: file
|
|
212
|
+
- name: width
|
|
213
|
+
kind: dropdown
|
|
214
|
+
options:
|
|
215
|
+
- 25%
|
|
216
|
+
- 33%
|
|
217
|
+
- 50%
|
|
218
|
+
- 66%
|
|
219
|
+
- 75%
|
|
220
|
+
- 100%
|
|
221
|
+
- name: align
|
|
222
|
+
kind: dropdown
|
|
223
|
+
options:
|
|
224
|
+
- left
|
|
225
|
+
- center
|
|
226
|
+
- right
|
|
227
|
+
- name: caption
|
|
228
|
+
kind: string
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### Sandboxed Embed / HTML (`config/content_types/embed.yml`)
|
|
232
|
+
```yaml
|
|
233
|
+
name: Embed
|
|
234
|
+
description: Embed external rich media or raw HTML snippets safely
|
|
235
|
+
fields:
|
|
236
|
+
- name: html
|
|
237
|
+
kind: text
|
|
238
|
+
- name: width
|
|
239
|
+
kind: dropdown
|
|
240
|
+
options:
|
|
241
|
+
- 25%
|
|
242
|
+
- 33%
|
|
243
|
+
- 50%
|
|
244
|
+
- 66%
|
|
245
|
+
- 75%
|
|
246
|
+
- 100%
|
|
247
|
+
- name: align
|
|
248
|
+
kind: dropdown
|
|
249
|
+
options:
|
|
250
|
+
- left
|
|
251
|
+
- center
|
|
252
|
+
- right
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
### 4. Importing Content Types
|
|
125
258
|
|
|
126
259
|
Import all YAML content type definitions into the database (`Macron::Cms::Content`):
|
|
127
260
|
|
|
@@ -137,23 +270,44 @@ $ bin/rails macron:cms:content:import
|
|
|
137
270
|
|
|
138
271
|
The import task is idempotent and creates or updates existing records. Once imported, content types can be inspected at `/cms/content_types` and added as content blocks to articles in `/cms/articles/:id`.
|
|
139
272
|
|
|
140
|
-
|
|
273
|
+
---
|
|
141
274
|
|
|
142
|
-
|
|
275
|
+
### 5. Front-End Integration & ViewComponent Widgets
|
|
143
276
|
|
|
144
|
-
|
|
145
|
-
<% cms_seo_meta(@article) %>
|
|
277
|
+
#### Host Application View (`app/views/articles/show.html.erb`)
|
|
146
278
|
|
|
147
|
-
|
|
148
|
-
<h1 class="text-4xl font-bold"><%= @article.title %></h1>
|
|
279
|
+
Use `cms_seo_meta` for SEO metadata and `cms_article_background_style` to apply custom background colors, images, and text colors directly to the layout:
|
|
149
280
|
|
|
150
|
-
|
|
281
|
+
```erb
|
|
282
|
+
<% cms_seo_meta(@article) %>
|
|
283
|
+
<% if cms_article_background_style(@article).present? %>
|
|
284
|
+
<% content_for :body_style, cms_article_background_style(@article) %>
|
|
285
|
+
<% end %>
|
|
286
|
+
|
|
287
|
+
<article class="max-w-4xl mx-auto px-4 py-12">
|
|
288
|
+
<header class="pb-8 mb-8 border-b border-zinc-200">
|
|
289
|
+
<h1 class="text-4xl font-bold" style="<%= "color: #{@article.text_color};" if @article.text_color.present? %>">
|
|
290
|
+
<%= @article.title %>
|
|
291
|
+
</h1>
|
|
292
|
+
</header>
|
|
293
|
+
|
|
294
|
+
<!-- Render all content blocks in order -->
|
|
295
|
+
<div class="space-y-6">
|
|
151
296
|
<%= cms_render_article_contents(@article) %>
|
|
152
297
|
</div>
|
|
153
298
|
</article>
|
|
154
299
|
```
|
|
155
300
|
|
|
156
|
-
|
|
301
|
+
In your application layout (`app/views/layouts/application.html.erb`), apply the `:body_style` slot:
|
|
302
|
+
|
|
303
|
+
```erb
|
|
304
|
+
<body class="min-h-screen text-zinc-900 <%= 'bg-zinc-50' unless content_for?(:body_style) %>"
|
|
305
|
+
style="<%= yield :body_style if content_for?(:body_style) %>">
|
|
306
|
+
<%= yield %>
|
|
307
|
+
</body>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### Heading Widget Component Example
|
|
157
311
|
|
|
158
312
|
```ruby
|
|
159
313
|
# app/components/widgets/heading_component.rb
|
|
@@ -162,13 +316,136 @@ module Widgets
|
|
|
162
316
|
def tag_name
|
|
163
317
|
level.presence || "h2"
|
|
164
318
|
end
|
|
319
|
+
|
|
320
|
+
def resolved_color
|
|
321
|
+
color.presence || article_text_color
|
|
322
|
+
end
|
|
165
323
|
end
|
|
166
324
|
end
|
|
167
325
|
```
|
|
168
326
|
|
|
169
327
|
```erb
|
|
170
328
|
<%# app/components/widgets/heading_component.html.erb %>
|
|
171
|
-
<%= content_tag(tag_name, title, class: "font-bold
|
|
329
|
+
<%= content_tag(tag_name, title, class: "font-bold tracking-tight", style: ("color: #{resolved_color};" if resolved_color.present?)) %>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Markdown Widget Component Example
|
|
333
|
+
|
|
334
|
+
Render Markdown with Kramdown and inherit article-level text styling:
|
|
335
|
+
|
|
336
|
+
```ruby
|
|
337
|
+
# app/components/widgets/markdown_component.rb
|
|
338
|
+
module Widgets
|
|
339
|
+
class MarkdownComponent < Macron::Cms::WidgetComponent
|
|
340
|
+
def rendered_markdown
|
|
341
|
+
raw = field_value(:content).to_s
|
|
342
|
+
return "".html_safe if raw.blank?
|
|
343
|
+
|
|
344
|
+
Kramdown::Document.new(raw, input: "GFM").to_html.html_safe
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def text_style
|
|
348
|
+
"color: #{article_text_color};" if article_text_color.present?
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
```erb
|
|
355
|
+
<%# app/components/widgets/markdown_component.html.erb %>
|
|
356
|
+
<div class="prose max-w-none text-inherit dark:prose-invert" style="<%= text_style %>">
|
|
357
|
+
<%= rendered_markdown %>
|
|
358
|
+
</div>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### Image Widget Component Example
|
|
362
|
+
|
|
363
|
+
Access Active Storage attachments using `file` or `upload`:
|
|
364
|
+
|
|
365
|
+
```ruby
|
|
366
|
+
# app/components/widgets/image_component.rb
|
|
367
|
+
module Widgets
|
|
368
|
+
class ImageComponent < Macron::Cms::WidgetComponent
|
|
369
|
+
def attached_file
|
|
370
|
+
file
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def width_class
|
|
374
|
+
case width
|
|
375
|
+
when "25%" then "max-w-xs"
|
|
376
|
+
when "33%" then "max-w-sm"
|
|
377
|
+
when "50%" then "max-w-md"
|
|
378
|
+
when "66%" then "max-w-lg"
|
|
379
|
+
when "75%" then "max-w-2xl"
|
|
380
|
+
else "w-full"
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def align_class
|
|
385
|
+
case align
|
|
386
|
+
when "left" then "mr-auto"
|
|
387
|
+
when "right" then "ml-auto"
|
|
388
|
+
else "mx-auto"
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
```erb
|
|
396
|
+
<%# app/components/widgets/image_component.html.erb %>
|
|
397
|
+
<% if attached_file.present? %>
|
|
398
|
+
<figure class="<%= width_class %> <%= align_class %> my-6">
|
|
399
|
+
<%= image_tag attached_file, class: "rounded-lg shadow-sm w-full h-auto" %>
|
|
400
|
+
<% if caption.present? %>
|
|
401
|
+
<figcaption class="mt-2 text-center text-sm text-zinc-500"><%= caption %></figcaption>
|
|
402
|
+
<% end %>
|
|
403
|
+
</figure>
|
|
404
|
+
<% end %>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
#### Sandboxed Embed Widget Component Example
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
# app/components/widgets/embed_component.rb
|
|
411
|
+
module Widgets
|
|
412
|
+
class EmbedComponent < Macron::Cms::WidgetComponent
|
|
413
|
+
def embed_html
|
|
414
|
+
field_value(:html).to_s
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def width_class
|
|
418
|
+
case width
|
|
419
|
+
when "25%" then "max-w-xs"
|
|
420
|
+
when "33%" then "max-w-sm"
|
|
421
|
+
when "50%" then "max-w-md"
|
|
422
|
+
when "66%" then "max-w-lg"
|
|
423
|
+
when "75%" then "max-w-2xl"
|
|
424
|
+
else "w-full"
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def align_class
|
|
429
|
+
case align
|
|
430
|
+
when "left" then "mr-auto"
|
|
431
|
+
when "right" then "ml-auto"
|
|
432
|
+
else "mx-auto"
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
```erb
|
|
440
|
+
<%# app/components/widgets/embed_component.html.erb %>
|
|
441
|
+
<div class="<%= width_class %> <%= align_class %> my-6">
|
|
442
|
+
<iframe
|
|
443
|
+
srcdoc="<%= embed_html %>"
|
|
444
|
+
sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
|
|
445
|
+
class="w-full border-0 rounded overflow-hidden"
|
|
446
|
+
loading="lazy">
|
|
447
|
+
</iframe>
|
|
448
|
+
</div>
|
|
172
449
|
```
|
|
173
450
|
|
|
174
451
|
---
|