playbook_ui 16.8.0.pre.rc.1 → 16.8.0.pre.rc.2
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/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +1 -1
- data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_styles.scss +262 -43
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.html.erb +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.md +12 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_simple.html.erb +9 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_simple.md +8 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/kit.schema.json +18 -9
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +162 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +71 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_rails.js +202 -0
- data/dist/chunks/vendor.js +1 -1
- data/dist/menu.yml +1 -2
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a14d120d9cb3e0ab1458d5b4839ae3487e39835478477c1367b778fef93ada0
|
|
4
|
+
data.tar.gz: 894570ff3e83416909da2778cf54b9c609ee6cc639d4a408e2d06181209903b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9eaf2abe5bb9fe7ecdc3dfac5e408e6cb6a25cbb6c969592b7233bba1c2b838f60984c3c92764b77483fb290f922554afeac8981a8a5f57437ceb9bbf5da2f61
|
|
7
|
+
data.tar.gz: 155aaa6c9bb92a86fcdfbe35c7b62708d320940330b61aea3fbe6a56d5249de38f6bf1c8ba9d467e01f9ce5fc732e57ba636a9270ad1c54c2d93cbad2e645b4b
|
|
@@ -142,7 +142,7 @@ const RichTextEditor = (props: RichTextEditorProps): React.ReactElement => {
|
|
|
142
142
|
// Determine if toolbar should be shown
|
|
143
143
|
const shouldShowToolbar = focus && advancedEditor ? showToolbarOnFocus : advancedEditorToolbar
|
|
144
144
|
|
|
145
|
-
const labelFor = advancedEditor ? fieldId : (id ? id : (inputOptions
|
|
145
|
+
const labelFor = advancedEditor ? fieldId : (id ? id : (inputOptions?.id ? `${inputOptions.id}_trix` : undefined))
|
|
146
146
|
|
|
147
147
|
return (
|
|
148
148
|
<div
|
|
@@ -8,38 +8,53 @@
|
|
|
8
8
|
@import "../tokens/transition";
|
|
9
9
|
@import "previewer_mixin";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
11
|
+
// Rails TipTap root: flex/grid children default to min-width: auto, so the toolbar’s
|
|
12
|
+
// intrinsic width can force horizontal page/dialog scroll. Pin the kit to the parent width.
|
|
13
|
+
[data-pb-rte-tiptap="true"] {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
display: block;
|
|
16
|
+
max-width: 100%;
|
|
17
|
+
min-width: 0;
|
|
18
|
+
width: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.pb_rich_text_editor_kit {
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
max-width: 100%;
|
|
24
|
+
min-width: 0;
|
|
25
|
+
|
|
26
|
+
&.inline {
|
|
27
|
+
.toolbar {
|
|
28
|
+
opacity: 0;
|
|
29
|
+
transition: all 0.3s ease-in-out 0s;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:focus-within .toolbar {
|
|
33
|
+
opacity: 100;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.ProseMirror {
|
|
37
|
+
border: 1px solid transparent;
|
|
38
|
+
transition: all 0.3s ease-in-out 0s;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&:focus-within .ProseMirror {
|
|
42
|
+
border: 1px solid $input_border_default;
|
|
43
|
+
border-top: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&:hover {
|
|
47
|
+
.toolbar {
|
|
48
|
+
opacity: 100;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.ProseMirror {
|
|
52
|
+
border: 1px solid $input_border_default;
|
|
53
|
+
border-top: none;
|
|
42
54
|
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
43
58
|
.toolbar_button {
|
|
44
59
|
display: flex;
|
|
45
60
|
align-items: center;
|
|
@@ -73,18 +88,76 @@
|
|
|
73
88
|
}
|
|
74
89
|
}
|
|
75
90
|
|
|
91
|
+
// Active state for toolbar (Rails kit uses pb_button_kit pb_button_link; override link variant when active)
|
|
92
|
+
.toolbar button.pb_button_kit.is-active {
|
|
93
|
+
color: $primary;
|
|
94
|
+
background-color: $bg_light;
|
|
95
|
+
}
|
|
96
|
+
|
|
76
97
|
.pb_rich_text_editor_tiptap_toolbar_sticky {
|
|
77
98
|
position: sticky;
|
|
78
99
|
top: 0;
|
|
79
100
|
z-index: 10;
|
|
80
101
|
}
|
|
102
|
+
|
|
103
|
+
.rte-editor-wrap {
|
|
104
|
+
box-sizing: border-box;
|
|
105
|
+
max-width: 100%;
|
|
106
|
+
min-width: 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.pb_rich_text_editor_advanced_container {
|
|
110
|
+
max-width: 100%;
|
|
111
|
+
min-width: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
81
114
|
.toolbar {
|
|
82
115
|
border-radius: $border_rad_heaviest $border_rad_heaviest 0 0;
|
|
83
116
|
border: 1px solid $input_border_default;
|
|
84
117
|
overflow-x: auto;
|
|
118
|
+
// Single horizontal row + scroll in narrow modals/sidebars (wrap used to stack controls vertically).
|
|
85
119
|
&_block {
|
|
120
|
+
align-items: center;
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-wrap: nowrap;
|
|
86
123
|
gap: $space_xs;
|
|
124
|
+
min-width: 0;
|
|
125
|
+
overflow-x: auto;
|
|
126
|
+
-webkit-overflow-scrolling: touch;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// React: full-width row with history on the far right; horizontal scroll stays on .toolbar.
|
|
130
|
+
&:not(.rte-rails-toolbar-layout) {
|
|
131
|
+
> .pb_flex_kit.pb_flex_kit_justify_content_between {
|
|
132
|
+
box-sizing: border-box;
|
|
133
|
+
column-gap: $space_sm;
|
|
134
|
+
flex-wrap: nowrap;
|
|
135
|
+
justify-content: space-between;
|
|
136
|
+
min-width: 100%;
|
|
137
|
+
width: max-content;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
> .pb_flex_kit > .pb_flex_item_kit.toolbar_block {
|
|
141
|
+
flex: 0 0 auto;
|
|
142
|
+
min-width: 0;
|
|
143
|
+
overflow-x: visible;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
> .pb_flex_kit > .pb_flex_item_kit:last-child {
|
|
147
|
+
flex: 0 0 auto;
|
|
148
|
+
}
|
|
87
149
|
}
|
|
150
|
+
|
|
151
|
+
// Vertical section separators use ::before/::after with height: 100%. With
|
|
152
|
+
// align-items: center on .toolbar_block the kit’s cross size was 0, so the
|
|
153
|
+
// lines disappeared (master only had gap on .toolbar_block, default stretch).
|
|
154
|
+
.pb_section_separator_kit.pb_section_separator_vertical {
|
|
155
|
+
align-self: center;
|
|
156
|
+
flex-shrink: 0;
|
|
157
|
+
height: $space_xl;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// React ToolbarDropdown — match master (fixed width trigger; prod playbook.cloud).
|
|
88
161
|
.editor-dropdown-button {
|
|
89
162
|
background: transparent;
|
|
90
163
|
border: none;
|
|
@@ -93,23 +166,115 @@
|
|
|
93
166
|
font-weight: $light;
|
|
94
167
|
padding: ($space_xs - 1) 0px;
|
|
95
168
|
width: $space_xl * 3;
|
|
169
|
+
|
|
96
170
|
&:focus-visible {
|
|
97
171
|
box-shadow: unset;
|
|
98
172
|
}
|
|
99
173
|
}
|
|
174
|
+
|
|
175
|
+
&.rte-rails-toolbar-layout {
|
|
176
|
+
max-width: 100%;
|
|
177
|
+
min-width: 0;
|
|
178
|
+
|
|
179
|
+
.rte-rails-toolbar-row {
|
|
180
|
+
align-items: center;
|
|
181
|
+
box-sizing: border-box;
|
|
182
|
+
column-gap: $space_sm;
|
|
183
|
+
display: flex;
|
|
184
|
+
flex-wrap: nowrap;
|
|
185
|
+
justify-content: space-between;
|
|
186
|
+
min-width: 100%;
|
|
187
|
+
padding: $space_xxs $space_sm;
|
|
188
|
+
width: max-content;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.rte-toolbar-left {
|
|
192
|
+
align-items: center;
|
|
193
|
+
display: flex;
|
|
194
|
+
flex: 0 0 auto;
|
|
195
|
+
flex-wrap: nowrap;
|
|
196
|
+
gap: $space_xs;
|
|
197
|
+
min-width: 0;
|
|
198
|
+
overflow-x: visible;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.rte-toolbar-right {
|
|
202
|
+
align-items: center;
|
|
203
|
+
display: flex;
|
|
204
|
+
flex: 0 0 auto;
|
|
205
|
+
gap: $space_xs;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Align dropdown trigger with icon row (React wraps Popover + SectionSeparator in one flex line).
|
|
209
|
+
.pb_popover_reference_wrapper {
|
|
210
|
+
align-items: center;
|
|
211
|
+
display: inline-flex;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Override master’s fixed-width React trigger: Rails block-style label + icons needs flexible width.
|
|
215
|
+
.editor-dropdown-button {
|
|
216
|
+
justify-content: flex-start;
|
|
217
|
+
letter-spacing: normal;
|
|
218
|
+
line-height: 1;
|
|
219
|
+
max-width: 100%;
|
|
220
|
+
min-height: unset;
|
|
221
|
+
padding: ($space_xs - 1) $space_xs;
|
|
222
|
+
text-align: left;
|
|
223
|
+
width: auto;
|
|
224
|
+
|
|
225
|
+
.pb_button_content {
|
|
226
|
+
align-items: center;
|
|
227
|
+
display: inline-flex;
|
|
228
|
+
line-height: 1;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.pb_button_content > .pb_flex_kit {
|
|
232
|
+
align-items: center;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.rte-block-style-trigger-inner {
|
|
236
|
+
align-items: center;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.rte-block-style-trigger-icon,
|
|
240
|
+
.rte-block-style-chevron {
|
|
241
|
+
display: inline-flex;
|
|
242
|
+
flex-shrink: 0;
|
|
243
|
+
line-height: 0;
|
|
244
|
+
|
|
245
|
+
.pb_icon_kit {
|
|
246
|
+
align-items: center;
|
|
247
|
+
display: flex;
|
|
248
|
+
line-height: 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
svg {
|
|
252
|
+
display: block;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.rte-block-style-trigger-label {
|
|
257
|
+
align-items: center;
|
|
258
|
+
display: inline-flex;
|
|
259
|
+
line-height: 1.2;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
&:focus-visible {
|
|
263
|
+
box-shadow: unset;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
100
267
|
}
|
|
101
268
|
|
|
269
|
+
// TipTap content — match master (prod playbook React tab).
|
|
102
270
|
.ProseMirror {
|
|
103
271
|
background: $white;
|
|
104
272
|
border: 1px solid $input_border_default;
|
|
105
273
|
border-radius: $border_rad_heaviest;
|
|
106
274
|
height: 100%;
|
|
107
|
-
padding: 1rem 1.5rem 1.5rem 1.5rem;
|
|
108
275
|
line-height: $lh_loose;
|
|
276
|
+
padding: 1rem 1.5rem 1.5rem 1.5rem;
|
|
109
277
|
@include transition_default;
|
|
110
|
-
:first-child {
|
|
111
|
-
margin-top: 0;
|
|
112
|
-
}
|
|
113
278
|
|
|
114
279
|
h4,
|
|
115
280
|
h5,
|
|
@@ -161,9 +326,32 @@
|
|
|
161
326
|
ul {
|
|
162
327
|
@include preview_tiptap_ul;
|
|
163
328
|
}
|
|
329
|
+
|
|
330
|
+
// After heading mixins: first block should not pick up extra top margin (wins over `p { margin-top: 1rem }`).
|
|
331
|
+
> :first-child {
|
|
332
|
+
margin-top: 0;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Toolbar + editor stack: toolbar keeps its border; editor has no top stroke (classic layout).
|
|
337
|
+
// Avoid relying on a wrapper-only frame — partial deploys then looked “borderless” because
|
|
338
|
+
// ProseMirror had been forced to border: none.
|
|
339
|
+
.pb_rich_text_editor_advanced_container.toolbar-active {
|
|
340
|
+
.ProseMirror {
|
|
341
|
+
border-top: none;
|
|
342
|
+
border-top-left-radius: initial;
|
|
343
|
+
border-top-right-radius: initial;
|
|
344
|
+
}
|
|
164
345
|
}
|
|
165
346
|
}
|
|
166
347
|
|
|
348
|
+
// Rails-only: outer kit sets `data-pb-rte-tiptap` — roomier padding + wrapping for vanilla TipTap in forms/dialogs.
|
|
349
|
+
[data-pb-rte-tiptap="true"] .pb_rich_text_editor_kit .ProseMirror {
|
|
350
|
+
overflow-wrap: anywhere;
|
|
351
|
+
padding: 1.25rem 1.5rem 1.5rem 1.5rem;
|
|
352
|
+
word-break: break-word;
|
|
353
|
+
}
|
|
354
|
+
|
|
167
355
|
.pb_tiptap_toolbar_dropdown_list_item {
|
|
168
356
|
&.is-active,
|
|
169
357
|
&:active {
|
|
@@ -188,20 +376,51 @@
|
|
|
188
376
|
}
|
|
189
377
|
}
|
|
190
378
|
}
|
|
191
|
-
|
|
379
|
+
|
|
380
|
+
// Rails RTE: block-style menu uses Nav (popover) instead of React NavItem class hook.
|
|
381
|
+
.pb_rich_text_editor_kit .pb_popover_tooltip .pb_nav_list_item_link.is-active {
|
|
382
|
+
background-color: $bg_light;
|
|
383
|
+
border-radius: unset !important;
|
|
384
|
+
color: $primary;
|
|
385
|
+
|
|
386
|
+
.pb_nav_list_item_text,
|
|
387
|
+
.pb_nav_list_item_icon_left {
|
|
388
|
+
color: $primary !important;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.pb_rich_text_editor_kit .pb_popover_tooltip .pb_nav_list_kit_item:hover .pb_nav_list_item_link:not(.is-active) {
|
|
393
|
+
background-color: $neutral_subtle;
|
|
394
|
+
border-radius: unset !important;
|
|
395
|
+
|
|
396
|
+
.pb_nav_list_item_text,
|
|
397
|
+
.pb_nav_list_item_icon_left {
|
|
398
|
+
background-color: unset;
|
|
399
|
+
color: $text_lt_light !important;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// No toolbar: ring the whole control.
|
|
404
|
+
.pb_rich_text_editor_advanced_container:not(.toolbar-active) {
|
|
192
405
|
transition: box-shadow 0.3s ease-in-out, border-radius 0.3s ease-in-out;
|
|
193
406
|
&:focus-visible,
|
|
194
407
|
&:focus-within {
|
|
195
|
-
outline: unset;
|
|
196
|
-
box-shadow: 0 0 0 1px $input_border_state;
|
|
197
408
|
border-radius: $border_rad_heaviest;
|
|
409
|
+
box-shadow: 0 0 0 1px $input_border_state;
|
|
410
|
+
outline: unset;
|
|
198
411
|
transition: box-shadow 0.3s ease-in-out, border-radius 0.3s ease-in-out;
|
|
199
412
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Toolbar + editor: use border color (not an outer box-shadow) so the bottom isn’t doubled.
|
|
416
|
+
.pb_rich_text_editor_advanced_container.toolbar-active {
|
|
417
|
+
&:focus-within {
|
|
418
|
+
.toolbar {
|
|
419
|
+
border-color: $input_border_state;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.ProseMirror {
|
|
423
|
+
border-color: $input_border_state;
|
|
205
424
|
}
|
|
206
425
|
}
|
|
207
426
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= pb_rails("rich_text_editor", props: { input_options: { id: 'hidden_input_id', name: "hidden_input_name" }, value: "Add your text here. You can format your text, add links, quotes, and bullets." }) %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
The Rails rich text editor is a TipTap surface with no React. The UI (toolbar, block-style menu, formatting actions) is rendered with Playbook Rails kits (`pb_rails`). The editor document is a vanilla TipTap `Editor` instance; HTML is synced to a hidden `<input>` so standard Rails forms can submit the value.
|
|
2
|
+
|
|
3
|
+
### How TipTap is loaded (Rails)
|
|
4
|
+
|
|
5
|
+
- The kit’s module script (`rich_text_editor_rails.js`) uses `import()` with **full URLs** on [esm.sh](https://esm.sh) (e.g. `@tiptap/core@2.8.0`). esm.sh resolves dependencies server-side, so **no `<script type="importmap">`** is required—this avoids conflicts when the host page already has an import map (e.g. Vite in dev, or another app map) because Firefox only applies one native map.
|
|
6
|
+
- You do not need TipTap in your app’s npm dependencies or Gemfile for this kit; the browser loads modules from esm.sh when the page runs.
|
|
7
|
+
- Ensure **CSP** allows loading scripts from `https://esm.sh` (and esm.sh’s redirected module URLs) if you use a strict `script-src` / `connect-src`.
|
|
8
|
+
|
|
9
|
+
### Relation to the React implementation
|
|
10
|
+
|
|
11
|
+
- Same core: both use TipTap v2 on top of ProseMirror; styling lives in Playbook SCSS (`_tiptap_styles.scss`) so the editor chrome lines up between platforms.
|
|
12
|
+
- Different shell: Rails uses ERB + Playbook Rails components + inline module script. React uses `RichTextEditor` / `_tiptap_editor.tsx` and TipTap wired through the bundled Playbook React package—see Advanced Default for that stack and when you need TipTap installed in your JavaScript bundle.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
### Simple toolbar (`simple: true`)
|
|
2
|
+
|
|
3
|
+
Pass **`simple: true`** for a compact toolbar: **Bold**, **Italic**, **Undo**, and **Redo** (same history controls as the full toolbar—plain buttons, not popovers).
|
|
4
|
+
|
|
5
|
+
- No block-style dropdown (no “Paragraph” / headings / lists in the menu).
|
|
6
|
+
- No **`pb_popover`** on the toolbar—useful in **native `<dialog>`** modals, turbo-loaded panels, or other tight layouts where the full block menu is awkward to position.
|
|
7
|
+
|
|
8
|
+
The underlying TipTap document still accepts the same HTML as the default Rails editor; `simple` only changes which **toolbar controls** are shown.
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
"name": "RichTextEditor",
|
|
4
4
|
"description": "RichTextEditor component",
|
|
5
5
|
"platforms": [
|
|
6
|
-
"react"
|
|
6
|
+
"react",
|
|
7
|
+
"rails"
|
|
7
8
|
],
|
|
8
9
|
"props": {
|
|
9
10
|
"advancedEditor": {
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
"inputOptions": {
|
|
34
35
|
"type": "GenericObject",
|
|
35
36
|
"platforms": [
|
|
36
|
-
"react"
|
|
37
|
+
"react",
|
|
38
|
+
"rails"
|
|
37
39
|
]
|
|
38
40
|
},
|
|
39
41
|
"inline": {
|
|
@@ -45,7 +47,8 @@
|
|
|
45
47
|
"label": {
|
|
46
48
|
"type": "string",
|
|
47
49
|
"platforms": [
|
|
48
|
-
"react"
|
|
50
|
+
"react",
|
|
51
|
+
"rails"
|
|
49
52
|
]
|
|
50
53
|
},
|
|
51
54
|
"extensions": {
|
|
@@ -69,7 +72,8 @@
|
|
|
69
72
|
"placeholder": {
|
|
70
73
|
"type": "string",
|
|
71
74
|
"platforms": [
|
|
72
|
-
"react"
|
|
75
|
+
"react",
|
|
76
|
+
"rails"
|
|
73
77
|
]
|
|
74
78
|
},
|
|
75
79
|
"inputHeight": {
|
|
@@ -97,14 +101,18 @@
|
|
|
97
101
|
"requiredIndicator": {
|
|
98
102
|
"type": "boolean",
|
|
99
103
|
"platforms": [
|
|
100
|
-
"react"
|
|
101
|
-
|
|
104
|
+
"react",
|
|
105
|
+
"rails"
|
|
106
|
+
],
|
|
107
|
+
"default": false
|
|
102
108
|
},
|
|
103
109
|
"simple": {
|
|
104
110
|
"type": "boolean",
|
|
105
111
|
"platforms": [
|
|
106
|
-
"react"
|
|
107
|
-
|
|
112
|
+
"react",
|
|
113
|
+
"rails"
|
|
114
|
+
],
|
|
115
|
+
"default": false
|
|
108
116
|
},
|
|
109
117
|
"sticky": {
|
|
110
118
|
"type": "boolean",
|
|
@@ -121,7 +129,8 @@
|
|
|
121
129
|
"value": {
|
|
122
130
|
"type": "string",
|
|
123
131
|
"platforms": [
|
|
124
|
-
"react"
|
|
132
|
+
"react",
|
|
133
|
+
"rails"
|
|
125
134
|
]
|
|
126
135
|
},
|
|
127
136
|
"maxWidth": {
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<%# TipTap loads via full URL dynamic import() in rich_text_editor_rails.js (esm.sh) — no import map, so no clash with Vite/host maps. %>
|
|
2
|
+
<%= pb_content_tag(:div, id: object.container_id, class: object.classname, data: { pb_rte_tiptap: true, input_id: object.input_id, initial_html: object.initial_html, rte_simple: object.simple }) do %>
|
|
3
|
+
<div class="pb_rich_text_editor_kit">
|
|
4
|
+
<% if object.label.present? %>
|
|
5
|
+
<label for="<%= object.input_id %>">
|
|
6
|
+
<% if object.required_indicator %>
|
|
7
|
+
<%= pb_rails("caption", props: { color: "lighter", text: object.label, dark: object.dark }) %><span style="color: #DA0014;"> *</span>
|
|
8
|
+
<% else %>
|
|
9
|
+
<%= pb_rails("caption", props: { color: "lighter", text: object.label, dark: object.dark }) %>
|
|
10
|
+
<% end %>
|
|
11
|
+
</label>
|
|
12
|
+
<% end %>
|
|
13
|
+
<input type="hidden" name="<%= object.input_name %>" id="<%= object.input_id %>" value="" />
|
|
14
|
+
<div class="pb_rich_text_editor_advanced_container toolbar-active<%= " pb_rich_text_editor_rte--simple" if object.simple %>">
|
|
15
|
+
<% if object.simple %>
|
|
16
|
+
<%# Compact toolbar: Bold/Italic + Undo/Redo — no block-style Popover (avoids dialog positioning issues). %>
|
|
17
|
+
<div class="pb_background_kit pb_background_color_white toolbar rte-rails-toolbar-layout rte-rails-toolbar-layout--simple" id="<%= object.toolbar_id %>">
|
|
18
|
+
<div class="rte-rails-toolbar-row">
|
|
19
|
+
<div class="toolbar_block rte-toolbar-left">
|
|
20
|
+
<button type="button" class="toolbar_button" data-action="bold" title="Bold" role="button" tabindex="0">
|
|
21
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
22
|
+
<%= pb_rails("icon", props: { icon: "bold", size: "lg" }) %>
|
|
23
|
+
<% end %>
|
|
24
|
+
</button>
|
|
25
|
+
<button type="button" class="toolbar_button" data-action="italic" title="Italic" role="button" tabindex="0">
|
|
26
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
27
|
+
<%= pb_rails("icon", props: { icon: "italic", size: "lg" }) %>
|
|
28
|
+
<% end %>
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="toolbar_block rte-toolbar-right">
|
|
32
|
+
<button type="button" class="toolbar_button" data-action="undo" title="Undo" role="button" tabindex="0">
|
|
33
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
34
|
+
<%= pb_rails("icon", props: { icon: "undo", size: "lg" }) %>
|
|
35
|
+
<% end %>
|
|
36
|
+
</button>
|
|
37
|
+
<button type="button" class="toolbar_button" data-action="redo" title="Redo" role="button" tabindex="0">
|
|
38
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
39
|
+
<%= pb_rails("icon", props: { icon: "redo", size: "lg" }) %>
|
|
40
|
+
<% end %>
|
|
41
|
+
</button>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
<% else %>
|
|
46
|
+
<% block_style_options = [
|
|
47
|
+
{ value: "paragraph", text: "Paragraph", icon: "paragraph" },
|
|
48
|
+
{ value: "heading-1", text: "Heading 1", icon: "h1" },
|
|
49
|
+
{ value: "heading-2", text: "Heading 2", icon: "h2" },
|
|
50
|
+
{ value: "heading-3", text: "Heading 3", icon: "h3" },
|
|
51
|
+
{ value: "bulletList", text: "Bullet List", icon: "list" },
|
|
52
|
+
{ value: "orderedList", text: "Ordered List", icon: "list-ol" },
|
|
53
|
+
{ value: "blockquote", text: "Block Quote", icon: "block-quote" },
|
|
54
|
+
] %>
|
|
55
|
+
<div class="pb_background_kit pb_background_color_white toolbar rte-rails-toolbar-layout" id="<%= object.toolbar_id %>">
|
|
56
|
+
<div class="rte-rails-toolbar-row">
|
|
57
|
+
<div class="toolbar_block rte-toolbar-left">
|
|
58
|
+
<span class="pb_popover_reference_wrapper">
|
|
59
|
+
<%# Button kit wraps content in <span class="pb_button_content"> — block <div>s inside are invalid and break layout. Single <span> row + JS sync from templates. %>
|
|
60
|
+
<%= pb_rails("button", props: {
|
|
61
|
+
id: object.rte_block_style_trigger_id,
|
|
62
|
+
variant: "secondary",
|
|
63
|
+
classname: "editor-dropdown-button",
|
|
64
|
+
html_options: {
|
|
65
|
+
type: "button",
|
|
66
|
+
"aria-label": "Text style",
|
|
67
|
+
"aria-haspopup": "true",
|
|
68
|
+
},
|
|
69
|
+
}) do %>
|
|
70
|
+
<span class="pb_flex_kit pb_flex_kit_orientation_row pb_flex_kit_justify_content_left pb_flex_kit_align_items_center pb_flex_kit_spacing_none pb_flex_kit_gap_xs gap_xs rte-block-style-trigger-inner" data-rte-block-trigger>
|
|
71
|
+
<span class="rte-block-style-trigger-icon">
|
|
72
|
+
<%= pb_rails("icon", props: { icon: "paragraph", size: "lg" }) %>
|
|
73
|
+
</span>
|
|
74
|
+
<span class="rte-block-style-trigger-label">Paragraph</span>
|
|
75
|
+
<span class="display_inline_flex rte-block-style-chevron">
|
|
76
|
+
<%= pb_rails("icon", props: { icon: "chevron-down", fixed_width: true }) %>
|
|
77
|
+
</span>
|
|
78
|
+
</span>
|
|
79
|
+
<% end %>
|
|
80
|
+
</span>
|
|
81
|
+
<%= pb_rails("popover", props: {
|
|
82
|
+
trigger_element_id: object.rte_block_style_trigger_id,
|
|
83
|
+
tooltip_id: object.rte_block_style_tooltip_id,
|
|
84
|
+
position: "bottom",
|
|
85
|
+
padding: "none",
|
|
86
|
+
close_on_click: "any",
|
|
87
|
+
offset: true,
|
|
88
|
+
}) do %>
|
|
89
|
+
<%= pb_rails("nav", props: { variant: "subtle", padding_top: "xs", padding_bottom: "xs" }) do %>
|
|
90
|
+
<% block_style_options.each do |opt| %>
|
|
91
|
+
<%= pb_rails("nav/item", props: {
|
|
92
|
+
link: "##{opt[:value]}",
|
|
93
|
+
text: opt[:text],
|
|
94
|
+
icon_left: opt[:icon],
|
|
95
|
+
margin: "none",
|
|
96
|
+
padding_top: "xxs",
|
|
97
|
+
padding_bottom: "xxs",
|
|
98
|
+
}) %>
|
|
99
|
+
<% end %>
|
|
100
|
+
<% end %>
|
|
101
|
+
<% end %>
|
|
102
|
+
<%= pb_rails("section_separator", props: { orientation: "vertical" }) %>
|
|
103
|
+
<button type="button" class="toolbar_button" data-action="bold" title="Bold" role="button" tabindex="0">
|
|
104
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
105
|
+
<%= pb_rails("icon", props: { icon: "bold", size: "lg" }) %>
|
|
106
|
+
<% end %>
|
|
107
|
+
</button>
|
|
108
|
+
<button type="button" class="toolbar_button" data-action="italic" title="Italic" role="button" tabindex="0">
|
|
109
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
110
|
+
<%= pb_rails("icon", props: { icon: "italic", size: "lg" }) %>
|
|
111
|
+
<% end %>
|
|
112
|
+
</button>
|
|
113
|
+
<button type="button" class="toolbar_button" data-action="strike" title="Strikethrough" role="button" tabindex="0">
|
|
114
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
115
|
+
<%= pb_rails("icon", props: { icon: "strikethrough", size: "lg" }) %>
|
|
116
|
+
<% end %>
|
|
117
|
+
</button>
|
|
118
|
+
<%= pb_rails("section_separator", props: { orientation: "vertical" }) %>
|
|
119
|
+
<button type="button" class="toolbar_button" data-action="codeBlock" title="Code block" role="button" tabindex="0">
|
|
120
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
121
|
+
<%= pb_rails("icon", props: { icon: "code", size: "lg" }) %>
|
|
122
|
+
<% end %>
|
|
123
|
+
</button>
|
|
124
|
+
<button type="button" class="toolbar_button" data-action="link" title="Link" role="button" tabindex="0">
|
|
125
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
126
|
+
<%= pb_rails("icon", props: { icon: "link", size: "lg" }) %>
|
|
127
|
+
<% end %>
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="toolbar_block rte-toolbar-right">
|
|
131
|
+
<button type="button" class="toolbar_button" data-action="undo" title="Undo" role="button" tabindex="0">
|
|
132
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
133
|
+
<%= pb_rails("icon", props: { icon: "undo", size: "lg" }) %>
|
|
134
|
+
<% end %>
|
|
135
|
+
</button>
|
|
136
|
+
<button type="button" class="toolbar_button" data-action="redo" title="Redo" role="button" tabindex="0">
|
|
137
|
+
<%= pb_rails("flex", props: { align: "center", justify: "center", classname: "toolbar_button_icon" }) do %>
|
|
138
|
+
<%= pb_rails("icon", props: { icon: "redo", size: "lg" }) %>
|
|
139
|
+
<% end %>
|
|
140
|
+
</button>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
<div id="<%= object.container_id %>-block-icon-templates" hidden aria-hidden="true">
|
|
145
|
+
<% block_style_options.each do |opt| %>
|
|
146
|
+
<span data-block-template-for="<%= opt[:value] %>" data-label="<%= opt[:text] %>">
|
|
147
|
+
<%= pb_rails("icon", props: { icon: opt[:icon], size: "lg" }) %>
|
|
148
|
+
</span>
|
|
149
|
+
<% end %>
|
|
150
|
+
</div>
|
|
151
|
+
<% end %>
|
|
152
|
+
<div class="rte-editor-wrap">
|
|
153
|
+
<div id="<%= object.editor_node_id %>"></div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
<% end %>
|
|
158
|
+
|
|
159
|
+
<%# Module script: rich_text_editor_rails.js %>
|
|
160
|
+
<script type="module">
|
|
161
|
+
<%= File.read(Playbook.kit_path("rich_text_editor", "rich_text_editor_rails.js")).html_safe %>
|
|
162
|
+
</script>
|