playbook_ui 18.0.0.pre.alpha.PLAY323219731 → 18.0.0.pre.alpha.PLAY323219732
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/TipTap/Toolbar.tsx +72 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_styles.scss +12 -32
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_rails.js +44 -0
- data/dist/ai/forms.json +1 -1
- data/dist/ai/index.json +2 -2
- data/dist/chunks/vendor.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 702601751c0fd5c028b371a8daee1fa45544f6a1b212e924e91a68436f663b3e
|
|
4
|
+
data.tar.gz: 42d98672288c51cf43e762b28d094eec228145dddbb62a56869baab16149897c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e8582dd5511729d758e3110a3224b9a0d55c1f37f10d5c28fd3e16475d72694b4116cff6783631fc2f92d63a85eebbd3bd0c576f5674f9d03ef8ed92ea1cef4d
|
|
7
|
+
data.tar.gz: 856c48ef6c7bf02b8d69ea3c8e418bf151936c8c3fe5a28c4227e8e337ce91cdf820e7c26d50150ad532e991cb086c057f2108e0b5053b189502724a84842615
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
import Background from "../../pb_background/_background";
|
|
3
3
|
import Flex from "../../pb_flex/_flex";
|
|
4
4
|
import SectionSeparator from "../../pb_section_separator/_section_separator";
|
|
@@ -11,13 +11,71 @@ import { ToolbarTypes } from "./EditorTypes";
|
|
|
11
11
|
import ToolbarHistoryItems from "./ToolbarHistory";
|
|
12
12
|
import MoreExtensionsDropdown from "./MoreExtensionsDropdown";
|
|
13
13
|
|
|
14
|
+
const STACK_HYSTERESIS_PX = 32;
|
|
15
|
+
|
|
14
16
|
const ToolbarSeparator = (): React.ReactElement => (
|
|
15
17
|
<div className="toolbar_separator">
|
|
16
18
|
<SectionSeparator orientation="vertical" />
|
|
17
19
|
</div>
|
|
18
20
|
);
|
|
19
21
|
|
|
22
|
+
const toolsAreWrapped = (root: HTMLElement | null): boolean => {
|
|
23
|
+
if (!root) return false;
|
|
24
|
+
|
|
25
|
+
const format = root.querySelector<HTMLElement>(".toolbar_group_format");
|
|
26
|
+
const tools = root.querySelector<HTMLElement>(".toolbar_group_tools");
|
|
27
|
+
if (!format || !tools) return false;
|
|
28
|
+
|
|
29
|
+
return tools.offsetTop > format.offsetTop + 1;
|
|
30
|
+
};
|
|
31
|
+
|
|
20
32
|
const EditorToolbar = ({ editor, extensions, simple, sticky }: any): React.ReactElement => {
|
|
33
|
+
const toolbarInnerRef = useRef<HTMLDivElement>(null);
|
|
34
|
+
const stackedAtWidthRef = useRef(0);
|
|
35
|
+
const [toolsStacked, setToolsStacked] = useState(false);
|
|
36
|
+
|
|
37
|
+
useLayoutEffect(() => {
|
|
38
|
+
if (simple) return;
|
|
39
|
+
|
|
40
|
+
const root = toolbarInnerRef.current;
|
|
41
|
+
if (!root) return;
|
|
42
|
+
|
|
43
|
+
const toolbar = root.closest(".toolbar");
|
|
44
|
+
|
|
45
|
+
const update = () => {
|
|
46
|
+
const wrapped = toolsAreWrapped(root);
|
|
47
|
+
const width = toolbar?.clientWidth ?? root.clientWidth;
|
|
48
|
+
|
|
49
|
+
setToolsStacked((stacked) => {
|
|
50
|
+
if (!stacked && wrapped) {
|
|
51
|
+
stackedAtWidthRef.current = width;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (stacked && !wrapped && width > stackedAtWidthRef.current + STACK_HYSTERESIS_PX) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (stacked && wrapped) {
|
|
60
|
+
stackedAtWidthRef.current = Math.min(stackedAtWidthRef.current, width);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return stacked;
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
update();
|
|
68
|
+
const rafId = requestAnimationFrame(update);
|
|
69
|
+
const observer = new ResizeObserver(update);
|
|
70
|
+
observer.observe(root);
|
|
71
|
+
if (toolbar) observer.observe(toolbar);
|
|
72
|
+
|
|
73
|
+
return () => {
|
|
74
|
+
cancelAnimationFrame(rafId);
|
|
75
|
+
observer.disconnect();
|
|
76
|
+
};
|
|
77
|
+
}, [extensions, simple]);
|
|
78
|
+
|
|
21
79
|
const simpleToolbaritems = [
|
|
22
80
|
{
|
|
23
81
|
icon: "bold",
|
|
@@ -33,10 +91,16 @@ const EditorToolbar = ({ editor, extensions, simple, sticky }: any): React.React
|
|
|
33
91
|
},
|
|
34
92
|
]
|
|
35
93
|
|
|
94
|
+
const toolbarClassName = [
|
|
95
|
+
"toolbar",
|
|
96
|
+
toolsStacked && "toolbar_tools_stacked",
|
|
97
|
+
sticky && "pb_rich_text_editor_tiptap_toolbar_sticky",
|
|
98
|
+
].filter(Boolean).join(" ");
|
|
99
|
+
|
|
36
100
|
return (
|
|
37
|
-
<Background
|
|
38
|
-
|
|
39
|
-
|
|
101
|
+
<Background
|
|
102
|
+
backgroundColor="white"
|
|
103
|
+
className={toolbarClassName}
|
|
40
104
|
>
|
|
41
105
|
{
|
|
42
106
|
simple ? (
|
|
@@ -63,7 +127,10 @@ const EditorToolbar = ({ editor, extensions, simple, sticky }: any): React.React
|
|
|
63
127
|
</div>
|
|
64
128
|
</div>
|
|
65
129
|
) : (
|
|
66
|
-
<div
|
|
130
|
+
<div
|
|
131
|
+
className="toolbar_inner"
|
|
132
|
+
ref={toolbarInnerRef}
|
|
133
|
+
>
|
|
67
134
|
<Flex
|
|
68
135
|
align="center"
|
|
69
136
|
className="toolbar_controls"
|
|
@@ -131,7 +131,8 @@
|
|
|
131
131
|
container-type: inline-size;
|
|
132
132
|
position: relative;
|
|
133
133
|
|
|
134
|
-
// Prefer one row; undo/redo stay top-right.
|
|
134
|
+
// Prefer one row; undo/redo stay top-right.
|
|
135
|
+
// `.toolbar_tools_stacked` is set in JS when tools wrap to a second row.
|
|
135
136
|
&_inner {
|
|
136
137
|
box-sizing: border-box;
|
|
137
138
|
min-height: $space_lg + $space_xxs + $space_xxs * 2; // 36px button + vertical padding
|
|
@@ -140,8 +141,7 @@
|
|
|
140
141
|
position: relative;
|
|
141
142
|
}
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
&:has(.toolbar_group_more) .toolbar_inner {
|
|
144
|
+
&.toolbar_tools_stacked .toolbar_inner {
|
|
145
145
|
padding-right: $space_sm;
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -177,18 +177,12 @@
|
|
|
177
177
|
|
|
178
178
|
&_tools {
|
|
179
179
|
box-sizing: border-box;
|
|
180
|
+
flex-grow: 1;
|
|
180
181
|
flex-wrap: nowrap;
|
|
181
182
|
max-width: 100%;
|
|
182
183
|
min-width: 0;
|
|
183
184
|
order: 2;
|
|
184
185
|
overflow-x: auto;
|
|
185
|
-
|
|
186
|
-
// Hide leading pipe by default — tools often wrap before container queries
|
|
187
|
-
// force a full-width stack, which left a lone "|" at the start of row 2.
|
|
188
|
-
// Only show it when the bar is wide enough for a reliable single row.
|
|
189
|
-
> .toolbar_separator:first-child {
|
|
190
|
-
display: none;
|
|
191
|
-
}
|
|
192
186
|
}
|
|
193
187
|
|
|
194
188
|
&_more {
|
|
@@ -196,30 +190,27 @@
|
|
|
196
190
|
}
|
|
197
191
|
}
|
|
198
192
|
|
|
199
|
-
|
|
193
|
+
&.toolbar_tools_stacked .toolbar_group_tools > .toolbar_separator:first-child {
|
|
194
|
+
display: none;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// With "more": format|more on row 1, tools on row 2 until wide
|
|
200
198
|
&_controls:has(.toolbar_group_more) {
|
|
201
199
|
.toolbar_group_more {
|
|
202
200
|
order: 2;
|
|
203
201
|
}
|
|
204
202
|
|
|
205
203
|
.toolbar_group_tools {
|
|
206
|
-
flex:
|
|
204
|
+
flex-basis: 100%;
|
|
205
|
+
flex-grow: 1;
|
|
207
206
|
order: 3;
|
|
208
207
|
}
|
|
209
208
|
}
|
|
210
209
|
|
|
211
210
|
@container rte-toolbar (min-width: 42rem) {
|
|
212
|
-
&:has(.toolbar_group_more) .toolbar_inner {
|
|
213
|
-
padding-right: $history-gutter;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.toolbar_group_tools > .toolbar_separator:first-child {
|
|
217
|
-
display: flex;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
211
|
.toolbar_controls:has(.toolbar_group_more) {
|
|
221
212
|
.toolbar_group_tools {
|
|
222
|
-
flex:
|
|
213
|
+
flex-basis: auto;
|
|
223
214
|
order: 2;
|
|
224
215
|
overflow-x: visible;
|
|
225
216
|
}
|
|
@@ -230,17 +221,6 @@
|
|
|
230
221
|
}
|
|
231
222
|
}
|
|
232
223
|
|
|
233
|
-
// No "more": full-width tools row + no undo gutter when stacked
|
|
234
|
-
@container rte-toolbar (max-width: 40rem) {
|
|
235
|
-
&:not(:has(.toolbar_group_more)) .toolbar_inner {
|
|
236
|
-
padding-right: $space_sm;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
.toolbar_controls:not(:has(.toolbar_group_more)) .toolbar_group_tools {
|
|
240
|
-
flex: 1 1 100%;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
224
|
// Separators: slightly inset from button height; xxs gap left/right
|
|
245
225
|
&_separator {
|
|
246
226
|
align-items: stretch;
|
|
@@ -103,6 +103,49 @@ function runToolbarAction(editor, action, { alignment, prompt = window.prompt, n
|
|
|
103
103
|
if (chainMethod && typeof chain[chainMethod] === "function") chain[chainMethod]().run();
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
const TOOLBAR_STACK_HYSTERESIS_PX = 32;
|
|
107
|
+
|
|
108
|
+
function syncToolbarToolsStacked(toolbar, state = { stacked: false, stackedAtWidth: 0 }) {
|
|
109
|
+
if (!toolbar) return state;
|
|
110
|
+
|
|
111
|
+
const format = toolbar.querySelector(".toolbar_group_format");
|
|
112
|
+
const tools = toolbar.querySelector(".toolbar_group_tools");
|
|
113
|
+
if (!format || !tools) return state;
|
|
114
|
+
|
|
115
|
+
const wrapped = tools.offsetTop > format.offsetTop + 1;
|
|
116
|
+
const width = toolbar.clientWidth;
|
|
117
|
+
let { stacked, stackedAtWidth } = state;
|
|
118
|
+
|
|
119
|
+
if (!stacked && wrapped) {
|
|
120
|
+
stacked = true;
|
|
121
|
+
stackedAtWidth = width;
|
|
122
|
+
} else if (stacked && !wrapped && width > stackedAtWidth + TOOLBAR_STACK_HYSTERESIS_PX) {
|
|
123
|
+
stacked = false;
|
|
124
|
+
} else if (stacked && wrapped) {
|
|
125
|
+
stackedAtWidth = Math.min(stackedAtWidth, width);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
toolbar.classList.toggle("toolbar_tools_stacked", stacked);
|
|
129
|
+
return { stacked, stackedAtWidth };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function watchToolbarToolsStacked(toolbar) {
|
|
133
|
+
if (!toolbar) return;
|
|
134
|
+
|
|
135
|
+
let state = { stacked: false, stackedAtWidth: 0 };
|
|
136
|
+
const update = () => {
|
|
137
|
+
state = syncToolbarToolsStacked(toolbar, state);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
update();
|
|
141
|
+
requestAnimationFrame(update);
|
|
142
|
+
|
|
143
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
144
|
+
|
|
145
|
+
const observer = new ResizeObserver(update);
|
|
146
|
+
observer.observe(toolbar);
|
|
147
|
+
}
|
|
148
|
+
|
|
106
149
|
async function initPlaybookRichTextEditorRails(container) {
|
|
107
150
|
if (!container || container.dataset.pbRteInitialized || container.dataset.pbRtePending) return;
|
|
108
151
|
container.dataset.pbRtePending = "true";
|
|
@@ -403,6 +446,7 @@ async function initPlaybookRichTextEditorRails(container) {
|
|
|
403
446
|
editor.on("selectionUpdate", updateActiveStates);
|
|
404
447
|
editor.on("transaction", updateActiveStates);
|
|
405
448
|
updateActiveStates();
|
|
449
|
+
watchToolbarToolsStacked(toolbar);
|
|
406
450
|
|
|
407
451
|
container.dataset.pbRteInitialized = "true";
|
|
408
452
|
} finally {
|
data/dist/ai/forms.json
CHANGED
data/dist/ai/index.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "18.0.0-alpha.
|
|
2
|
+
"version": "18.0.0-alpha.PLAY323219732",
|
|
3
3
|
"metadataVersion": 1,
|
|
4
4
|
"forms": "forms.json",
|
|
5
|
-
"generated": "2026-10-01T20:
|
|
5
|
+
"generated": "2026-10-01T20:51:10.526Z",
|
|
6
6
|
"schemas": {
|
|
7
7
|
"globalProps": "global-props.schema.json",
|
|
8
8
|
"globalEventProps": "global-event-props.schema.json",
|