playbook_ui 16.7.0 → 16.8.0.pre.alpha.PLAY2935formbuilderrequiredindicatorbug16780
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_advanced_table/Components/RegularTableView.tsx +2 -0
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +2 -0
- data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +5 -1
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +24 -0
- data/app/pb_kits/playbook/pb_advanced_table/Utilities/ColumnLayoutHelper.ts +138 -0
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +144 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling.jsx +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling.md +6 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_width.jsx +57 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_width.md +66 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_enable_toggle_expansion_rails.html.erb +62 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_enable_toggle_expansion_rails.md +7 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_header.jsx +12 -4
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_header.md +4 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_header_rails.html.erb +16 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_header_rails.md +4 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_scroll_limitation.jsx +68 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sticky_scroll_limitation.md +7 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header.html.erb +16 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header.jsx +12 -5
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header_rails.md +4 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header_react.md +5 -3
- data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.json +180 -5839
- data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.overrides.json +5 -30
- data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_column_definitions_styling.json +4 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +3 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_card/_card.tsx +1 -1
- data/app/pb_kits/playbook/pb_card/card.html.erb +1 -1
- data/app/pb_kits/playbook/pb_currency/_currency.tsx +9 -6
- data/app/pb_kits/playbook/pb_currency/currency.rb +5 -10
- data/app/pb_kits/playbook/pb_currency/currency.test.js +44 -1
- data/app/pb_kits/playbook/pb_date/docs/_playground.json +13 -17
- data/app/pb_kits/playbook/pb_date/docs/_playground.overrides.json +13 -16
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +3 -2
- data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +38 -23
- data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +2 -1
- data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +31 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_dialog_submission.jsx +2 -2
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.html.erb +0 -2
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.jsx +0 -2
- data/app/pb_kits/playbook/pb_date_picker/docs/_playground.json +136 -42
- data/app/pb_kits/playbook/pb_date_picker/docs/_playground.overrides.json +113 -45
- data/app/pb_kits/playbook/pb_date_range_inline/docs/_playground.json +48 -6
- data/app/pb_kits/playbook/pb_date_range_inline/docs/_playground.overrides.json +57 -0
- data/app/pb_kits/playbook/pb_date_range_stacked/docs/_playground.json +28 -5
- data/app/pb_kits/playbook/pb_date_range_stacked/docs/_playground.overrides.json +38 -0
- data/app/pb_kits/playbook/pb_date_stacked/docs/_playground.json +1 -1
- data/app/pb_kits/playbook/pb_date_stacked/docs/_playground.overrides.json +1 -1
- data/app/pb_kits/playbook/pb_date_time/docs/_playground.json +16 -3
- data/app/pb_kits/playbook/pb_date_time/docs/_playground.overrides.json +16 -3
- data/app/pb_kits/playbook/pb_date_time_stacked/docs/_playground.json +11 -15
- data/app/pb_kits/playbook/pb_date_time_stacked/docs/_playground.overrides.json +11 -15
- data/app/pb_kits/playbook/pb_date_year_stacked/docs/_playground.json +4 -4
- data/app/pb_kits/playbook/pb_date_year_stacked/docs/_playground.overrides.json +4 -4
- data/app/pb_kits/playbook/pb_detail/docs/_playground.json +12 -18
- data/app/pb_kits/playbook/pb_detail/docs/_playground.overrides.json +13 -12
- data/app/pb_kits/playbook/pb_dialog/docs/_playground.json +108 -42
- data/app/pb_kits/playbook/pb_dialog/docs/_playground.overrides.json +88 -40
- data/app/pb_kits/playbook/pb_distribution_bar/docs/_playground.json +65 -7
- data/app/pb_kits/playbook/pb_distribution_bar/docs/_playground.overrides.json +45 -0
- data/app/pb_kits/playbook/pb_draggable/_draggable.scss +19 -0
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_rails.md +2 -0
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_react.md +1 -0
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list_rails.md +2 -0
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list_react.md +3 -1
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list_rails.md +3 -1
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list_react.md +3 -1
- data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +16 -0
- data/app/pb_kits/playbook/pb_draggable/draggable_container.html.erb +3 -1
- data/app/pb_kits/playbook/pb_draggable/draggable_item.html.erb +1 -0
- data/app/pb_kits/playbook/pb_draggable/index.js +149 -7
- data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableContainer.tsx +1 -0
- data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableItem.tsx +67 -1
- data/app/pb_kits/playbook/pb_draggable/touchDrag.test.js +38 -0
- data/app/pb_kits/playbook/pb_draggable/utilities/touchDrag.ts +173 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_playground.json +318 -21
- data/app/pb_kits/playbook/pb_dropdown/docs/_playground.overrides.json +192 -19
- data/app/pb_kits/playbook/pb_empty_state/docs/_playground.json +77 -12
- data/app/pb_kits/playbook/pb_empty_state/docs/_playground.overrides.json +79 -0
- data/app/pb_kits/playbook/pb_file_upload/docs/_playground.json +98 -13
- data/app/pb_kits/playbook/pb_file_upload/docs/_playground.overrides.json +99 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +20 -19
- data/app/pb_kits/playbook/pb_icon/_icon.scss +2 -1
- data/app/pb_kits/playbook/pb_icon/docs/example.yml +0 -2
- data/app/pb_kits/playbook/pb_icon/docs/index.js +0 -1
- data/app/pb_kits/playbook/pb_link/docs/_playground.json +81 -40
- data/app/pb_kits/playbook/pb_link/docs/_playground.overrides.json +88 -30
- data/app/pb_kits/playbook/pb_list/_list_item.tsx +4 -1
- data/app/pb_kits/playbook/pb_list/item.html.erb +1 -1
- data/app/pb_kits/playbook/pb_pb_circle_chart/docs/_pb_circle_chart_centered_data.html.erb +90 -0
- data/app/pb_kits/playbook/pb_pb_circle_chart/docs/_pb_circle_chart_centered_data.jsx +100 -0
- data/app/pb_kits/playbook/pb_pb_circle_chart/docs/_pb_circle_chart_centered_data.md +1 -0
- data/app/pb_kits/playbook/pb_pb_circle_chart/docs/_pb_circle_chart_default.jsx +1 -1
- data/app/pb_kits/playbook/pb_pb_circle_chart/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_pb_circle_chart/docs/index.js +2 -1
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_playground.json +4 -2
- 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/app/pb_kits/playbook/pb_table/docs/_table_sticky.html.erb +85 -83
- data/app/pb_kits/playbook/pb_table/docs/_table_sticky.jsx +88 -86
- data/app/pb_kits/playbook/pb_table/docs/_table_sticky.md +3 -1
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_external_filter_rails.md +1 -1
- data/app/pb_kits/playbook/pb_text_input/_text_input.scss +37 -0
- data/app/pb_kits/playbook/pb_title/docs/_playground.json +72 -23
- data/app/pb_kits/playbook/pb_title/docs/_playground.overrides.json +80 -16
- data/app/pb_kits/playbook/pb_tooltip/_tooltip.scss +133 -102
- data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +54 -41
- data/app/pb_kits/playbook/pb_tooltip/tooltip.test.jsx +60 -2
- data/dist/chunks/{_pb_line_graph-CIWJe3Gr.js → _pb_line_graph-BgsTI0CL.js} +1 -1
- data/dist/chunks/_typeahead-DA__Kgp5.js +5 -0
- data/dist/chunks/{globalProps-CqO4Tko1.js → globalProps-DOB47YGB.js} +1 -1
- data/dist/chunks/{lib-czQnE40X.js → lib-BzglXly2.js} +2 -2
- data/dist/chunks/vendor.js +4 -4
- data/dist/menu.yml +71 -132
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/forms/builder/form_field_builder.rb +2 -0
- data/lib/playbook/version.rb +2 -2
- metadata +31 -10
- data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.html.erb +0 -1
- data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.jsx +0 -21
- data/app/pb_kits/playbook/pb_icon/docs/_icon_fa_kit.md +0 -7
- data/dist/chunks/_typeahead-B_Ac4z84.js +0 -1
|
@@ -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>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Playbook
|
|
4
|
+
module PbRichTextEditor
|
|
5
|
+
# Rails rich text editor: TipTap (vanilla JS), no React. Content syncs to a hidden input for form submission.
|
|
6
|
+
class RichTextEditor < Playbook::KitBase
|
|
7
|
+
prop :value
|
|
8
|
+
prop :placeholder
|
|
9
|
+
prop :input_options, type: Playbook::Props::HashProp, default: {}
|
|
10
|
+
prop :label
|
|
11
|
+
prop :required_indicator, type: Playbook::Props::Boolean, default: false
|
|
12
|
+
# When true, TipTap toolbar matches React `simple`: Bold + Italic only (no block-style Popover).
|
|
13
|
+
# Use in modals or narrow layouts where the block dropdown misbehaves.
|
|
14
|
+
prop :simple, type: Playbook::Props::Boolean, default: false
|
|
15
|
+
|
|
16
|
+
# Match React default (globalProps maxWidth "md").
|
|
17
|
+
def max_width
|
|
18
|
+
v = values[:max_width] || values["max_width"]
|
|
19
|
+
v.nil? || v == "" ? "md" : v
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def classname
|
|
23
|
+
generate_classname("pb_rich_text_editor_kit", "rte-container")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def input_id
|
|
27
|
+
return input_options[:id].presence if input_options[:id].present?
|
|
28
|
+
return "#{id}-input" if id.present?
|
|
29
|
+
|
|
30
|
+
# Unique per kit instance — multiple editors without a kit `id` would otherwise share the same DOM id.
|
|
31
|
+
"rich_text_editor-input-#{object_id}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def input_name
|
|
35
|
+
return input_options[:name].presence if input_options[:name].present?
|
|
36
|
+
return "#{id}_content" if id.present?
|
|
37
|
+
|
|
38
|
+
# Last-resort default; two editors with neither `id` nor `input_options[:name]` still collide — set one of them.
|
|
39
|
+
"content"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initial_html
|
|
43
|
+
raw = value.present? ? value.to_s.strip : ""
|
|
44
|
+
return "<p></p>" if raw.blank?
|
|
45
|
+
|
|
46
|
+
raw.start_with?("<") ? raw : "<p>#{raw}</p>"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def container_id
|
|
50
|
+
id.present? ? "rte-tiptap-#{id}" : "rte-tiptap-#{input_id.gsub(/[^a-z0-9_-]/i, '')}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def editor_node_id
|
|
54
|
+
"#{container_id}-editor"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def toolbar_id
|
|
58
|
+
"#{container_id}-toolbar"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Stable DOM ids for TipTap toolbar popover (used in ERB + module script; must be kit methods — not ERB locals).
|
|
62
|
+
def rte_block_style_trigger_id
|
|
63
|
+
"#{toolbar_id}-block-trigger"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def rte_block_style_tooltip_id
|
|
67
|
+
"#{toolbar_id}-block-tooltip"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Rails TipTap: dynamic import() from esm.sh (no import map — avoids 2nd map ignored by Firefox vs Vite/host).
|
|
2
|
+
// Idempotent: data-pb-rte-initialized / data-pb-rte-pending.
|
|
3
|
+
|
|
4
|
+
const RTE_TIPTAP_VERSION = "2.8.0";
|
|
5
|
+
const RTE_TIPTAP_ESM = (pkg) => `https://esm.sh/${pkg}@${RTE_TIPTAP_VERSION}`;
|
|
6
|
+
|
|
7
|
+
async function initPlaybookRichTextEditorRails(container) {
|
|
8
|
+
if (!container || container.dataset.pbRteInitialized || container.dataset.pbRtePending) return;
|
|
9
|
+
container.dataset.pbRtePending = "true";
|
|
10
|
+
|
|
11
|
+
const inputId = container.dataset.inputId;
|
|
12
|
+
let initialHtml = container.dataset.initialHtml || "<p></p>";
|
|
13
|
+
if (initialHtml && !initialHtml.trim().startsWith("<")) {
|
|
14
|
+
initialHtml = "<p>" + initialHtml + "</p>";
|
|
15
|
+
}
|
|
16
|
+
const containerId = container.id;
|
|
17
|
+
const hiddenInput = document.getElementById(inputId);
|
|
18
|
+
const editorNode = document.getElementById(`${containerId}-editor`);
|
|
19
|
+
const toolbar = document.getElementById(`${containerId}-toolbar`);
|
|
20
|
+
const rteSimple = container.dataset.rteSimple === "true";
|
|
21
|
+
const blockTooltipId = `${containerId}-toolbar-block-tooltip`;
|
|
22
|
+
const iconTemplatesRoot = rteSimple
|
|
23
|
+
? null
|
|
24
|
+
: document.getElementById(`${containerId}-block-icon-templates`);
|
|
25
|
+
|
|
26
|
+
if (!editorNode || !hiddenInput || !toolbar) {
|
|
27
|
+
delete container.dataset.pbRtePending;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function syncToHiddenInput(editor) {
|
|
32
|
+
if (editor && hiddenInput) {
|
|
33
|
+
hiddenInput.value = editor.getHTML();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const { Editor } = await import(RTE_TIPTAP_ESM("@tiptap/core"));
|
|
39
|
+
const { default: StarterKit } = await import(RTE_TIPTAP_ESM("@tiptap/starter-kit"));
|
|
40
|
+
const { default: Link } = await import(RTE_TIPTAP_ESM("@tiptap/extension-link"));
|
|
41
|
+
|
|
42
|
+
const editor = new Editor({
|
|
43
|
+
element: editorNode,
|
|
44
|
+
extensions: [
|
|
45
|
+
StarterKit.configure({ heading: { levels: [1, 2, 3] } }),
|
|
46
|
+
Link.configure({ openOnClick: false, HTMLAttributes: { target: "_blank", rel: "noopener" } }),
|
|
47
|
+
],
|
|
48
|
+
content: initialHtml,
|
|
49
|
+
editable: true,
|
|
50
|
+
onUpdate: ({ editor: ed }) => syncToHiddenInput(ed),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
syncToHiddenInput(editor);
|
|
54
|
+
|
|
55
|
+
const actionToChain = {
|
|
56
|
+
bold: "toggleBold",
|
|
57
|
+
italic: "toggleItalic",
|
|
58
|
+
strike: "toggleStrike",
|
|
59
|
+
codeBlock: "toggleCodeBlock",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getCurrentBlockValue = () => {
|
|
63
|
+
let value = "paragraph";
|
|
64
|
+
if (editor.isActive("heading", { level: 1 })) value = "heading-1";
|
|
65
|
+
else if (editor.isActive("heading", { level: 2 })) value = "heading-2";
|
|
66
|
+
else if (editor.isActive("heading", { level: 3 })) value = "heading-3";
|
|
67
|
+
else if (editor.isActive("bulletList")) value = "bulletList";
|
|
68
|
+
else if (editor.isActive("orderedList")) value = "orderedList";
|
|
69
|
+
else if (editor.isActive("blockquote")) value = "blockquote";
|
|
70
|
+
return value;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const syncBlockTrigger = () => {
|
|
74
|
+
if (rteSimple) return;
|
|
75
|
+
const current = getCurrentBlockValue();
|
|
76
|
+
const triggerRoot = toolbar.querySelector("[data-rte-block-trigger]");
|
|
77
|
+
let tpl =
|
|
78
|
+
iconTemplatesRoot &&
|
|
79
|
+
[...iconTemplatesRoot.children].find(
|
|
80
|
+
(el) => el.getAttribute("data-block-template-for") === current
|
|
81
|
+
);
|
|
82
|
+
if (!tpl && iconTemplatesRoot) {
|
|
83
|
+
tpl = [...iconTemplatesRoot.children].find(
|
|
84
|
+
(el) => el.getAttribute("data-block-template-for") === "paragraph"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (triggerRoot && tpl) {
|
|
88
|
+
const iconWrap = triggerRoot.querySelector(".rte-block-style-trigger-icon");
|
|
89
|
+
const labelEl = triggerRoot.querySelector(".rte-block-style-trigger-label");
|
|
90
|
+
if (iconWrap) iconWrap.innerHTML = tpl.innerHTML;
|
|
91
|
+
if (labelEl) labelEl.textContent = tpl.getAttribute("data-label") || "";
|
|
92
|
+
}
|
|
93
|
+
const tooltip = document.getElementById(blockTooltipId);
|
|
94
|
+
if (tooltip) {
|
|
95
|
+
tooltip.querySelectorAll("a.pb_nav_list_item_link").forEach((a) => {
|
|
96
|
+
const href = a.getAttribute("href") || "";
|
|
97
|
+
const v = href.startsWith("#") ? href.slice(1) : "";
|
|
98
|
+
a.classList.toggle("is-active", v === current);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const applyBlockType = (value) => {
|
|
104
|
+
const chain = editor.chain().focus();
|
|
105
|
+
if (value === "paragraph") chain.setParagraph().run();
|
|
106
|
+
else if (value === "heading-1") chain.toggleHeading({ level: 1 }).run();
|
|
107
|
+
else if (value === "heading-2") chain.toggleHeading({ level: 2 }).run();
|
|
108
|
+
else if (value === "heading-3") chain.toggleHeading({ level: 3 }).run();
|
|
109
|
+
else if (value === "bulletList") chain.toggleBulletList().run();
|
|
110
|
+
else if (value === "orderedList") chain.toggleOrderedList().run();
|
|
111
|
+
else if (value === "blockquote") chain.toggleBlockquote().run();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const updateActiveStates = () => {
|
|
115
|
+
syncBlockTrigger();
|
|
116
|
+
toolbar.querySelectorAll("button[data-action]").forEach((btn) => {
|
|
117
|
+
const action = btn.dataset.action;
|
|
118
|
+
let active = false;
|
|
119
|
+
if (action === "bold") active = editor.isActive("bold");
|
|
120
|
+
else if (action === "italic") active = editor.isActive("italic");
|
|
121
|
+
else if (action === "strike") active = editor.isActive("strike");
|
|
122
|
+
else if (action === "codeBlock") active = editor.isActive("codeBlock");
|
|
123
|
+
else if (action === "link") active = editor.isActive("link");
|
|
124
|
+
btn.classList.toggle("is-active", active);
|
|
125
|
+
});
|
|
126
|
+
toolbar.querySelectorAll("button[data-action='undo']").forEach((btn) => {
|
|
127
|
+
btn.disabled = !editor.can().undo();
|
|
128
|
+
});
|
|
129
|
+
toolbar.querySelectorAll("button[data-action='redo']").forEach((btn) => {
|
|
130
|
+
btn.disabled = !editor.can().redo();
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
if (!rteSimple) {
|
|
135
|
+
const blockStyleTooltip = document.getElementById(blockTooltipId);
|
|
136
|
+
if (blockStyleTooltip) {
|
|
137
|
+
blockStyleTooltip.addEventListener("click", (e) => {
|
|
138
|
+
const a = e.target.closest("a[href^='#']");
|
|
139
|
+
if (!a || !blockStyleTooltip.contains(a)) return;
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
const href = a.getAttribute("href") || "";
|
|
142
|
+
const v = href.startsWith("#") ? href.slice(1) : "";
|
|
143
|
+
if (!v) return;
|
|
144
|
+
applyBlockType(v);
|
|
145
|
+
updateActiveStates();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
toolbar.addEventListener("click", (e) => {
|
|
151
|
+
const btn = e.target.closest("button[data-action]");
|
|
152
|
+
if (!btn) return;
|
|
153
|
+
e.preventDefault();
|
|
154
|
+
const action = btn.dataset.action;
|
|
155
|
+
|
|
156
|
+
if (action === "undo") {
|
|
157
|
+
editor.chain().focus().undo().run();
|
|
158
|
+
} else if (action === "redo") {
|
|
159
|
+
editor.chain().focus().redo().run();
|
|
160
|
+
} else if (action === "link") {
|
|
161
|
+
const previousUrl = editor.getAttributes("link").href || "";
|
|
162
|
+
const url = window.prompt("URL", previousUrl);
|
|
163
|
+
if (url === null) return;
|
|
164
|
+
if (url === "") {
|
|
165
|
+
editor.chain().focus().extendMarkRange("link").unsetLink().run();
|
|
166
|
+
} else {
|
|
167
|
+
editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
const chainMethod = actionToChain[action];
|
|
171
|
+
if (chainMethod && typeof editor.chain().focus()[chainMethod] === "function") {
|
|
172
|
+
editor.chain().focus()[chainMethod]().run();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
updateActiveStates();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
editor.on("selectionUpdate", updateActiveStates);
|
|
179
|
+
editor.on("transaction", updateActiveStates);
|
|
180
|
+
updateActiveStates();
|
|
181
|
+
|
|
182
|
+
container.dataset.pbRteInitialized = "true";
|
|
183
|
+
} finally {
|
|
184
|
+
delete container.dataset.pbRtePending;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function mountAllPlaybookRichTextEditorRails() {
|
|
189
|
+
document.querySelectorAll("[data-pb-rte-tiptap]").forEach((el) => {
|
|
190
|
+
void initPlaybookRichTextEditorRails(el);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (document.readyState === "loading") {
|
|
195
|
+
document.addEventListener("DOMContentLoaded", mountAllPlaybookRichTextEditorRails);
|
|
196
|
+
} else {
|
|
197
|
+
mountAllPlaybookRichTextEditorRails();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
document.addEventListener("turbo:load", mountAllPlaybookRichTextEditorRails);
|
|
201
|
+
|
|
202
|
+
export { initPlaybookRichTextEditorRails, mountAllPlaybookRichTextEditorRails };
|
|
@@ -1,83 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
<div style="max-height: 320px; overflow-y: auto;">
|
|
2
|
+
<%= pb_rails("table", props: { sticky: true }) do %>
|
|
3
|
+
<thead>
|
|
4
|
+
<tr>
|
|
5
|
+
<th>Column 1</th>
|
|
6
|
+
<th>Column 2</th>
|
|
7
|
+
<th>Column 3</th>
|
|
8
|
+
<th>Column 4</th>
|
|
9
|
+
<th>Column 5</th>
|
|
10
|
+
</tr>
|
|
11
|
+
</thead>
|
|
12
|
+
<tbody>
|
|
13
|
+
<tr>
|
|
14
|
+
<td>Value 1</td>
|
|
15
|
+
<td>Value 2</td>
|
|
16
|
+
<td>Value 3</td>
|
|
17
|
+
<td>Value 4</td>
|
|
18
|
+
<td>Value 5</td>
|
|
19
|
+
</tr>
|
|
20
|
+
<tr>
|
|
21
|
+
<td>Value 1</td>
|
|
22
|
+
<td>Value 2</td>
|
|
23
|
+
<td>Value 3</td>
|
|
24
|
+
<td>Value 4</td>
|
|
25
|
+
<td>Value 5</td>
|
|
26
|
+
</tr>
|
|
27
|
+
<tr>
|
|
28
|
+
<td>Value 1</td>
|
|
29
|
+
<td>Value 2</td>
|
|
30
|
+
<td>Value 3</td>
|
|
31
|
+
<td>Value 4</td>
|
|
32
|
+
<td>Value 5</td>
|
|
33
|
+
</tr>
|
|
34
|
+
<tr>
|
|
35
|
+
<td>Value 1</td>
|
|
36
|
+
<td>Value 2</td>
|
|
37
|
+
<td>Value 3</td>
|
|
38
|
+
<td>Value 4</td>
|
|
39
|
+
<td>Value 5</td>
|
|
40
|
+
</tr>
|
|
41
|
+
<tr>
|
|
42
|
+
<td>Value 1</td>
|
|
43
|
+
<td>Value 2</td>
|
|
44
|
+
<td>Value 3</td>
|
|
45
|
+
<td>Value 4</td>
|
|
46
|
+
<td>Value 5</td>
|
|
47
|
+
</tr>
|
|
48
|
+
<tr>
|
|
49
|
+
<td>Value 1</td>
|
|
50
|
+
<td>Value 2</td>
|
|
51
|
+
<td>Value 3</td>
|
|
52
|
+
<td>Value 4</td>
|
|
53
|
+
<td>Value 5</td>
|
|
54
|
+
</tr>
|
|
55
|
+
<tr>
|
|
56
|
+
<td>Value 1</td>
|
|
57
|
+
<td>Value 2</td>
|
|
58
|
+
<td>Value 3</td>
|
|
59
|
+
<td>Value 4</td>
|
|
60
|
+
<td>Value 5</td>
|
|
61
|
+
</tr>
|
|
62
|
+
<tr>
|
|
63
|
+
<td>Value 1</td>
|
|
64
|
+
<td>Value 2</td>
|
|
65
|
+
<td>Value 3</td>
|
|
66
|
+
<td>Value 4</td>
|
|
67
|
+
<td>Value 5</td>
|
|
68
|
+
</tr>
|
|
69
|
+
<tr>
|
|
70
|
+
<td>Value 1</td>
|
|
71
|
+
<td>Value 2</td>
|
|
72
|
+
<td>Value 3</td>
|
|
73
|
+
<td>Value 4</td>
|
|
74
|
+
<td>Value 5</td>
|
|
75
|
+
</tr>
|
|
76
|
+
<tr>
|
|
77
|
+
<td>Value 1</td>
|
|
78
|
+
<td>Value 2</td>
|
|
79
|
+
<td>Value 3</td>
|
|
80
|
+
<td>Value 4</td>
|
|
81
|
+
<td>Value 5</td>
|
|
82
|
+
</tr>
|
|
83
|
+
</tbody>
|
|
84
|
+
<% end %>
|
|
85
|
+
</div>
|