playbook_ui 14.18.0.pre.alpha.play2034zonesandcolors7407 → 14.18.0.pre.alpha.play2042addturbosupporttopopoverkit7423

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +12 -0
  3. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +12 -0
  4. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +50 -1
  5. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_rails.md +4 -0
  6. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.md +1 -1
  7. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_react.md +5 -1
  8. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range_pattern.jsx +34 -0
  9. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range_pattern.md +14 -0
  10. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range_pattern_rails.html.erb +20 -0
  11. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range_pattern_rails.md +14 -0
  12. data/app/pb_kits/playbook/pb_date_picker/docs/example.yml +2 -0
  13. data/app/pb_kits/playbook/pb_date_picker/docs/index.js +1 -0
  14. data/app/pb_kits/playbook/pb_date_picker/plugins/quickPick.tsx +10 -0
  15. data/app/pb_kits/playbook/pb_draggable/docs/example.yml +1 -2
  16. data/app/pb_kits/playbook/pb_draggable/draggable.rb +1 -9
  17. data/app/pb_kits/playbook/pb_draggable/index.js +142 -139
  18. data/app/pb_kits/playbook/pb_filter/docs/_filter_within_turbo_frames.html.erb +41 -0
  19. data/app/pb_kits/playbook/pb_filter/docs/_filter_within_turbo_frames.md +1 -0
  20. data/app/pb_kits/playbook/pb_filter/docs/example.yml +1 -0
  21. data/app/pb_kits/playbook/pb_popover/docs/_popover_append_to.html.erb +46 -0
  22. data/app/pb_kits/playbook/pb_popover/docs/_popover_append_to.md +1 -0
  23. data/app/pb_kits/playbook/pb_popover/docs/example.yml +1 -0
  24. data/app/pb_kits/playbook/pb_popover/index.ts +13 -1
  25. data/app/pb_kits/playbook/pb_popover/popover.rb +2 -0
  26. data/dist/chunks/{_typeahead-CvkGr9xV.js → _typeahead-COZRQUuU.js} +1 -1
  27. data/dist/chunks/_weekday_stacked-BRT0ul9R.js +45 -0
  28. data/dist/chunks/{lib-yWHJ_8mm.js → lib-ClNg0TLS.js} +1 -1
  29. data/dist/chunks/{pb_form_validation-CFPfX89U.js → pb_form_validation-Btrgnox1.js} +1 -1
  30. data/dist/chunks/vendor.js +1 -1
  31. data/dist/playbook-doc.js +1 -1
  32. data/dist/playbook-rails-react-bindings.js +1 -1
  33. data/dist/playbook-rails.js +1 -1
  34. data/lib/playbook/version.rb +1 -1
  35. metadata +16 -11
  36. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones.html.erb +0 -43
  37. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones_colors.html.erb +0 -55
  38. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones_colors_rails.md +0 -1
  39. data/dist/chunks/_weekday_stacked-B-ran-L1.js +0 -45
  40. /data/app/pb_kits/playbook/pb_draggable/docs/{_draggable_drop_zones_react.md → _draggable_drop_zones.md} +0 -0
  41. /data/app/pb_kits/playbook/pb_draggable/docs/{_draggable_drop_zones_colors_react.md → _draggable_drop_zones_colors.md} +0 -0
@@ -1,26 +1,24 @@
1
1
  import PbEnhancedElement from "../pb_enhanced_element";
2
2
 
3
- const DRAGGABLE_SELECTOR = "[data-pb-draggable]";
3
+ const DRAGGABLE_SELECTOR = "[data-pb-draggable]";
4
4
  const DRAGGABLE_CONTAINER = ".pb_draggable_container";
5
- const NEEDS_CLONE = ["shadow", "outline"]; // clone only for these types
6
5
 
7
6
  export default class PbDraggable extends PbEnhancedElement {
8
- static get selector() { return DRAGGABLE_SELECTOR; }
7
+ static get selector() {
8
+ return DRAGGABLE_SELECTOR;
9
+ }
9
10
 
10
11
  connect() {
11
12
  this.state = {
12
- items: [],
13
- dragData: { id: "", initialGroup: "" },
14
- isDragging: "",
15
- activeContainer: "",
13
+ items: [],
14
+ dragData: { id: "", initialGroup: "" },
15
+ isDragging: "",
16
+ activeContainer: ""
16
17
  };
17
18
 
18
- this.draggedItem = null;
19
+ this.draggedItem = null;
19
20
  this.draggedItemId = null;
20
- this.dragGhost = null;
21
21
  this.hasMultipleContainers = false;
22
- this.dragZoneType = "";
23
- this.dragZoneColor = "";
24
22
 
25
23
  document.addEventListener("DOMContentLoaded", () => this.bindEventListeners());
26
24
  }
@@ -28,12 +26,13 @@ export default class PbDraggable extends PbEnhancedElement {
28
26
  setState(newState) {
29
27
  this.state = { ...this.state, ...newState };
30
28
  if (newState.items) {
31
- this.element.dispatchEvent(new CustomEvent("pb-draggable-reorder", {
29
+ const customEvent = new CustomEvent('pb-draggable-reorder', {
32
30
  detail: {
33
31
  reorderedItems: this.state.items,
34
- containerId: this.element.querySelector(DRAGGABLE_CONTAINER).id,
35
- },
36
- }));
32
+ containerId: this.element.querySelector(DRAGGABLE_CONTAINER).id
33
+ }
34
+ });
35
+ this.element.dispatchEvent(customEvent);
37
36
  }
38
37
  }
39
38
 
@@ -44,100 +43,71 @@ export default class PbDraggable extends PbEnhancedElement {
44
43
 
45
44
  // Needed to prevent images within draggable items from being independently draggable
46
45
  // Needed if using Image kit in draggable items
47
- this.element.querySelectorAll(".pb_draggable_item img")
48
- .forEach(img => img.setAttribute("draggable", "false"));
49
-
50
- this.element.querySelectorAll(".pb_draggable_item")
51
- .forEach(item => {
52
- item.addEventListener("dragstart", this.handleDragStart.bind(this));
53
- item.addEventListener("dragend", this.handleDragEnd.bind(this));
54
- item.addEventListener("dragenter", this.handleDragEnter.bind(this));
55
- });
46
+ this.element.querySelectorAll(".pb_draggable_item img").forEach(img => {
47
+ img.setAttribute("draggable", "false");
48
+ });
56
49
 
57
- containers.forEach(c => {
58
- c.addEventListener("dragover", this.handleDragOver.bind(this));
59
- c.addEventListener("drop", this.handleDrop.bind(this));
50
+ this.element.querySelectorAll(".pb_draggable_item").forEach(item => {
51
+ item.addEventListener("dragstart", this.handleDragStart.bind(this));
52
+ item.addEventListener("dragend", this.handleDragEnd.bind(this));
53
+ item.addEventListener("dragenter", this.handleDragEnter.bind(this));
54
+ });
55
+
56
+ containers.forEach(container => {
57
+ container.addEventListener("dragover", this.handleDragOver.bind(this));
58
+ container.addEventListener("drop", this.handleDrop.bind(this));
60
59
  });
61
60
  }
62
61
 
63
- /* ---------------- DRAG START ---------------- */
64
62
  handleDragStart(event) {
65
63
  // Needed to prevent images within draggable items from being independently draggable
66
64
  // Needed if using Image kit in draggable items
67
- if (event.target.tagName.toLowerCase() === "img") {
65
+ if (event.target.tagName.toLowerCase() === 'img') {
68
66
  event.preventDefault();
69
67
  return;
70
68
  }
71
69
 
72
- const container = event.target.closest(DRAGGABLE_CONTAINER);
73
- this.draggedItem = event.target;
70
+ const container = event.target.closest(DRAGGABLE_CONTAINER);
71
+ this.draggedItem = event.target;
74
72
  this.draggedItemId = event.target.id;
75
- this.dragZoneType = this.element.dataset.dropZoneType || "";
76
- this.dragZoneColor = this.element.dataset.dropZoneColor || "";
77
73
 
78
74
  this.setState({
79
- dragData: { id: this.draggedItemId, initialGroup: container.id },
80
- isDragging: this.draggedItemId,
75
+ dragData: { id: this.draggedItemId, initialGroup: container.id },
76
+ isDragging: this.draggedItemId
81
77
  });
82
78
 
83
- this.draggedItem.classList.add(
84
- "is_dragging",
85
- `drop_zone_${this.dragZoneType}`,
86
- `drop_zone_color_${this.dragZoneColor}`,
87
- );
88
-
79
+ event.target.classList.add("is_dragging");
89
80
  if (event.dataTransfer) {
90
- event.dataTransfer.effectAllowed = "move";
91
- event.dataTransfer.setData("text/plain", this.draggedItemId);
92
-
93
- /* ---------- custom ghost clone (shadow + outline only) ---------- */
94
- if (NEEDS_CLONE.includes(this.dragZoneType)) {
95
- const ghost = this.draggedItem.cloneNode(true);
96
- ghost.classList.remove(
97
- "is_dragging",
98
- `drop_zone_${this.dragZoneType}`,
99
- `drop_zone_color_${this.dragZoneColor}`,
100
- );
101
- const { width, height } = this.draggedItem.getBoundingClientRect();
102
- Object.assign(ghost.style, {
103
- border: "none",
104
- width: `${width}px`,
105
- height: `${height}px`,
106
- position: "absolute",
107
- top: "-9999px",
108
- left: "-9999px",
109
- boxSizing: "border-box",
110
- zIndex: "9999",
111
- });
112
- document.body.appendChild(ghost);
113
- this.dragGhost = ghost;
114
- event.dataTransfer.setDragImage(ghost, width / 2, height / 2);
115
- }
116
- /* ---------------------------------------------------------------- */
81
+ event.dataTransfer.effectAllowed = 'move';
82
+ event.dataTransfer.setData('text/plain', this.draggedItemId);
117
83
  }
118
84
 
119
- requestAnimationFrame(() => (event.target.style.opacity = "0.5"));
85
+ setTimeout(() => {
86
+ event.target.style.opacity = '0.5';
87
+ }, 0);
120
88
  }
121
89
 
122
- /* ---------------- DRAG ENTER ---------------- */
123
90
  handleDragEnter(event) {
124
91
  if (!this.draggedItem || event.target === this.draggedItem) return;
125
- this.hasMultipleContainers
126
- ? this.handleMultiContainerDragEnter(event)
127
- : this.handleSingleContainerDragEnter(event);
92
+
93
+ if (this.hasMultipleContainers) {
94
+ this.handleMultiContainerDragEnter(event);
95
+ } else {
96
+ this.handleSingleContainerDragEnter(event);
97
+ }
128
98
  }
129
99
 
130
100
  handleSingleContainerDragEnter(event) {
131
- const targetItem = event.target.closest(".pb_draggable_item");
132
- // If we're entering a container directly or there's no target item
101
+ const targetItem = event.target.closest('.pb_draggable_item');
133
102
  if (!targetItem) return;
134
103
 
135
104
  const container = targetItem.parentNode;
136
- const items = Array.from(container.children);
137
- const fromIdx = items.indexOf(this.draggedItem);
138
- const toIdx = items.indexOf(targetItem);
105
+ const items = Array.from(container.children);
139
106
 
140
- if (fromIdx > toIdx) {
107
+ const draggedIndex = items.indexOf(this.draggedItem);
108
+ const targetIndex = items.indexOf(targetItem);
109
+
110
+ if (draggedIndex > targetIndex) {
141
111
  container.insertBefore(this.draggedItem, targetItem);
142
112
  } else {
143
113
  container.insertBefore(this.draggedItem, targetItem.nextSibling);
@@ -146,113 +116,146 @@ export default class PbDraggable extends PbEnhancedElement {
146
116
 
147
117
  handleMultiContainerDragEnter(event) {
148
118
  const targetContainer = event.target.closest(DRAGGABLE_CONTAINER);
149
- const targetItem = event.target.closest(".pb_draggable_item");
119
+ const targetItem = event.target.closest('.pb_draggable_item');
120
+
150
121
  if (!targetContainer) return;
151
122
 
123
+ // If we're entering a container directly or there's no target item
152
124
  if (!targetItem) {
153
- const last = targetContainer.querySelector(".pb_draggable_item:last-child");
154
- last
155
- ? targetContainer.insertBefore(this.draggedItem, last.nextSibling)
156
- : targetContainer.appendChild(this.draggedItem);
125
+ const lastItem = targetContainer.querySelector('.pb_draggable_item:last-child');
126
+ if (lastItem) {
127
+ targetContainer.insertBefore(this.draggedItem, lastItem.nextSibling);
128
+ } else {
129
+ targetContainer.appendChild(this.draggedItem);
130
+ }
157
131
  return;
158
132
  }
159
133
 
160
- const items = Array.from(targetContainer.children);
161
- this.setState({ items: items.map(i => ({ id: i.id, container: targetContainer.id })) });
134
+ const container = targetItem.parentNode;
135
+ const items = Array.from(container.children);
136
+
137
+ const newItems = [...items].map(item => ({
138
+ id: item.id,
139
+ container: container.id
140
+ }));
141
+
142
+ this.setState({ items: newItems });
162
143
 
163
- const midY = targetItem.getBoundingClientRect().top +
164
- targetItem.getBoundingClientRect().height / 2;
144
+ const rect = targetItem.getBoundingClientRect();
145
+ const middleY = rect.top + rect.height / 2;
165
146
 
166
- if (event.clientY < midY) {
167
- targetContainer.insertBefore(this.draggedItem, targetItem);
147
+ if (event.clientY < middleY) {
148
+ container.insertBefore(this.draggedItem, targetItem);
168
149
  } else {
169
- targetContainer.insertBefore(this.draggedItem, targetItem.nextSibling);
150
+ container.insertBefore(this.draggedItem, targetItem.nextSibling);
170
151
  }
171
152
  }
172
153
 
173
- /* ---------------- DRAG OVER ---------------- */
174
154
  handleDragOver(event) {
175
155
  event.preventDefault();
176
156
  event.stopPropagation();
177
- this.hasMultipleContainers
178
- ? this.handleMultiContainerDragOver(event)
179
- : this.handleSingleContainerDragOver(event);
157
+
158
+ if (this.hasMultipleContainers) {
159
+ this.handleMultiContainerDragOver(event);
160
+ } else {
161
+ this.handleSingleContainerDragOver(event);
162
+ }
180
163
  }
181
164
 
182
165
  handleSingleContainerDragOver(event) {
183
166
  const container = event.target.closest(DRAGGABLE_CONTAINER);
184
- if (container) container.classList.add("active_container");
167
+ if (container) {
168
+ container.classList.add("active_container");
169
+ }
185
170
  }
186
171
 
187
172
  handleMultiContainerDragOver(event) {
188
- const container = event.target.matches(DRAGGABLE_CONTAINER)
189
- ? event.target
190
- : event.target.closest(DRAGGABLE_CONTAINER);
191
- if (!container) return;
192
-
193
- this.setState({ activeContainer: container.id });
194
- container.classList.add("active_container");
195
-
196
- const last = container.querySelector(".pb_draggable_item:last-child");
197
- if (!last || event.clientY > last.getBoundingClientRect().bottom) {
198
- if (this.draggedItem && this.draggedItem.parentNode !== container) {
199
- container.appendChild(this.draggedItem);
173
+ let container;
174
+ if (event.target.matches(DRAGGABLE_CONTAINER)) {
175
+ container = event.target;
176
+ } else {
177
+ container = event.target.closest(DRAGGABLE_CONTAINER);
178
+ }
179
+
180
+ if (container) {
181
+ this.setState({ activeContainer: container.id });
182
+ container.classList.add("active_container");
183
+
184
+ // If dragging over empty container or below last item
185
+ const lastItem = container.querySelector('.pb_draggable_item:last-child');
186
+ if (!lastItem || (lastItem && event.clientY > lastItem.getBoundingClientRect().bottom)) {
187
+ if (this.draggedItem && this.draggedItem.parentNode !== container) {
188
+ container.appendChild(this.draggedItem);
189
+ }
200
190
  }
201
191
  }
202
192
  }
203
193
 
204
- /* ---------------- DROP ---------------- */
205
194
  handleDrop(event) {
206
195
  event.preventDefault();
207
196
  event.stopPropagation();
208
197
 
209
- const container = event.target.matches(DRAGGABLE_CONTAINER)
210
- ? event.target
211
- : event.target.closest(DRAGGABLE_CONTAINER);
198
+ let container;
199
+ if (event.target.matches(DRAGGABLE_CONTAINER)) {
200
+ container = event.target;
201
+ } else {
202
+ container = event.target.closest(DRAGGABLE_CONTAINER);
203
+ }
204
+
212
205
  if (!container || !this.draggedItem) return;
213
206
 
214
207
  container.classList.remove("active_container");
215
- this.draggedItem.style.opacity = "1";
208
+ this.draggedItem.style.opacity = '1';
216
209
 
217
210
  // Handle empty containers
218
- if (this.hasMultipleContainers && !container.querySelector(".pb_draggable_item")) {
211
+ if (this.hasMultipleContainers && !container.querySelector('.pb_draggable_item')) {
219
212
  container.appendChild(this.draggedItem);
220
213
  }
221
214
 
222
215
  // Updated order of items as an array of item IDs
223
- const reorderedItems = Array.from(this.element.querySelectorAll(".pb_draggable_item"))
224
- .map(i => ({ id: i.id, container: i.closest(DRAGGABLE_CONTAINER).id }));
225
-
226
- container.dataset.reorderedItems = JSON.stringify(reorderedItems);
227
- this.element.dispatchEvent(new CustomEvent("pb-draggable-reorder", {
228
- detail: { reorderedItems, containerId: container.id },
216
+ const reorderedItems = Array.from(
217
+ this.element.querySelectorAll('.pb_draggable_item')
218
+ ).map(item => ({
219
+ id: item.id,
220
+ container: item.closest(DRAGGABLE_CONTAINER).id
229
221
  }));
230
222
 
231
- this.setState({ items: reorderedItems, isDragging: "", activeContainer: "" });
232
- this.draggedItem = null;
223
+ // Store reordered items in a data attribute on the container
224
+ container.setAttribute("data-reordered-items", JSON.stringify(reorderedItems));
225
+
226
+ const customEvent = new CustomEvent('pb-draggable-reorder', {
227
+ detail: {
228
+ reorderedItems,
229
+ containerId: container.id,
230
+ }
231
+ });
232
+
233
+ this.element.dispatchEvent(customEvent);
234
+
235
+ this.setState({
236
+ items: reorderedItems,
237
+ isDragging: "",
238
+ activeContainer: ""
239
+ });
240
+
241
+ this.draggedItem = null;
233
242
  this.draggedItemId = null;
234
243
  }
235
244
 
236
- /* ---------------- DRAG END ---------------- */
237
245
  handleDragEnd(event) {
238
- event.target.classList.remove(
239
- "is_dragging",
240
- `drop_zone_${this.dragZoneType}`,
241
- `drop_zone_color_${this.dragZoneColor}`,
242
- );
243
- event.target.style.opacity = "1";
244
-
245
- if (this.dragGhost) {
246
- document.body.removeChild(this.dragGhost);
247
- this.dragGhost = null;
248
- }
249
-
250
- this.setState({ isDragging: "", activeContainer: "" });
246
+ event.target.classList.remove("is_dragging");
247
+ event.target.style.opacity = '1';
251
248
 
252
- this.element.querySelectorAll(DRAGGABLE_CONTAINER)
253
- .forEach(c => c.classList.remove("active_container"));
249
+ this.setState({
250
+ isDragging: "",
251
+ activeContainer: ""
252
+ });
254
253
 
255
- this.draggedItem = null;
254
+ this.draggedItem = null;
256
255
  this.draggedItemId = null;
256
+
257
+ this.element.querySelectorAll(DRAGGABLE_CONTAINER).forEach(container => {
258
+ container.classList.remove("active_container");
259
+ });
257
260
  }
258
261
  }
@@ -0,0 +1,41 @@
1
+ <%=
2
+ pb_rails("filter", props: {
3
+ id: "filter_within_trubo_frames",
4
+ position: "top",
5
+ filters: [
6
+ { name: "name", value: "John Wick" },
7
+ { name: "city", value: "San Francisco"}
8
+ ],
9
+ sort_menu: [
10
+ { item: "Popularity", link: "?q[sorts]=managers_popularity+asc", active: true, direction: "desc" },
11
+ { item: "Mananger's Title", link: "?q[sorts]=managers_title+asc", active: false },
12
+ { item: "Manager's Name", link: "?q[sorts]=managers_name+asc", active: false },
13
+ ],
14
+ template: "default",
15
+ results: 1,
16
+ popover_props: { append_to: "parent" },
17
+ }) do
18
+ %>
19
+ <%
20
+ example_collection = [
21
+ OpenStruct.new(name: "USA", value: 1),
22
+ OpenStruct.new(name: "Canada", value: 2),
23
+ OpenStruct.new(name: "Brazil", value: 3),
24
+ OpenStruct.new(name: "Philippines", value: 4),
25
+ OpenStruct.new(name: "A galaxy far far away, like really far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far far away...", value: 5)
26
+ ]
27
+ %>
28
+ <%= pb_rails("form", props: { form_system_options: { scope: :example, method: :get } }) do |form| %>
29
+ <%= form.text_field :example_text_field, props: { label: true } %>
30
+ <%= form.collection_select :example_collection_select, example_collection, :value, :name, props: {max_width: "sm", label: true } %>
31
+
32
+ <%= form.actions do |action| %>
33
+ <%= action.submit props: {
34
+ text: "Apply",
35
+ data: {
36
+ disable_with: "pb_rails('icon', props: { icon: 'spinner', spin: true, fixed_width: true })Searching...".html_safe
37
+ },}%>
38
+ <%= action.button props: { type: "reset", text: "Clear", variant: "secondary" } %>
39
+ <% end %>
40
+ <% end %>
41
+ <% end %>
@@ -0,0 +1 @@
1
+ By default, the filter popover attaches to the `<body>`. To attach it elsewhere, like inside a Turbo Frame, pass the `append_to` prop through `popover_props`. Set it to `"parent"` to place the tooltip inside its parent element, or pass any CSS selector (`#id` or `.class`) to specify a custom container.
@@ -11,6 +11,7 @@ examples:
11
11
  - filter_max_height: Max Height for Popover Inside of Filter
12
12
  - filter_placement: Filter Placement
13
13
  - filter_popover_props: Popover Props
14
+ - filter_within_turbo_frames: Within Turbo Frames
14
15
 
15
16
  react:
16
17
  - filter_default: Default
@@ -0,0 +1,46 @@
1
+ <%= pb_rails("flex", props: {
2
+ dark: true,
3
+ orientation: "row",
4
+ vertical: "center",
5
+ margin_bottom: "md"
6
+ }) do %>
7
+ <%= pb_rails("body", props: { text: "Click info for more details" }) %>
8
+ &nbsp;
9
+ <%= pb_rails("circle_icon_button", props: {
10
+ variant: "secondary",
11
+ icon: "info",
12
+ id: "append-to-popover-1"
13
+ }) %>
14
+ <%= pb_rails("popover", props: {
15
+ trigger_element_id: "append-to-popover-1",
16
+ tooltip_id: "append-to-tooltip-1",
17
+ offset: true,
18
+ position: "top",
19
+ append_to: "parent",
20
+ }) do %>
21
+ I'm a popover. I have been appended to my parent element.
22
+ <% end %>
23
+ <% end %>
24
+
25
+ <%= pb_rails("flex", props: {
26
+ dark: true,
27
+ orientation: "row",
28
+ vertical: "center"
29
+ }) do %>
30
+ <%= pb_rails("body", props: { text: "Click info for more details" }) %>
31
+ &nbsp;
32
+ <%= pb_rails("circle_icon_button", props: {
33
+ variant: "secondary",
34
+ icon: "info",
35
+ id: "append-to-popover-2"
36
+ }) %>
37
+ <%= pb_rails("popover", props: {
38
+ trigger_element_id: "append-to-popover-2",
39
+ tooltip_id: "append-to-tooltip-2",
40
+ offset: true,
41
+ position: "top",
42
+ append_to: ".kit-show-wrapper",
43
+ }) do %>
44
+ I'm a popover. I have been appended to the .kit-show-wrapper.
45
+ <% end %>
46
+ <% end %>
@@ -0,0 +1 @@
1
+ By default, the popover tooltip attaches to the `<body>`. To attach it elsewhere, use the `append_to` prop. Set it to `"parent"` to place the tooltip inside its parent element, or pass any CSS selector (`#id` or `.class`) to specify a custom container.
@@ -6,6 +6,7 @@ examples:
6
6
  - popover_z_index: Set Z-Index
7
7
  - popover_scroll_height: Scroll and Height Settings
8
8
  - popover_actionable_content: With Actionable Content
9
+ - popover_append_to: Append To
9
10
 
10
11
  react:
11
12
  - popover_default: Default
@@ -13,7 +13,15 @@ export default class PbPopover extends PbEnhancedElement {
13
13
  }
14
14
 
15
15
  moveTooltip() {
16
- document.querySelector('body').appendChild(this.tooltip)
16
+ let container: HTMLElement | null;
17
+
18
+ if (this.appendTo === "parent") {
19
+ container = this.element.parentElement;
20
+ } else if (this.appendTo) {
21
+ container = document.querySelector(this.appendTo);
22
+ }
23
+
24
+ (container || document.body).appendChild(this.tooltip);
17
25
  }
18
26
 
19
27
  connect() {
@@ -108,4 +116,8 @@ export default class PbPopover extends PbEnhancedElement {
108
116
  get closeOnClick() {
109
117
  return this.element.dataset.pbPopoverCloseOnClick
110
118
  }
119
+
120
+ get appendTo() {
121
+ return this.element.dataset.pbPopoverAppendTo;
122
+ }
111
123
  }
@@ -3,6 +3,7 @@
3
3
  module Playbook
4
4
  module PbPopover
5
5
  class Popover < Playbook::KitBase
6
+ prop :append_to, type: Playbook::Props::String, default: "body"
6
7
  prop :position, type: Playbook::Props::Enum,
7
8
  values: %w[top bottom left right top-start top-end bottom-start bottom-end right-start right-end left-start left-end],
8
9
  default: "left"
@@ -47,6 +48,7 @@ module Playbook
47
48
 
48
49
  def data
49
50
  Hash(values[:data]).merge(
51
+ pb_popover_append_to: append_to,
50
52
  pb_popover_kit: true,
51
53
  pb_popover_position: position,
52
54
  pb_popover_trigger_element_id: trigger_element_id,
@@ -1,4 +1,4 @@
1
- import{jsx as jsx$1,Fragment,jsxs}from"react/jsx-runtime";import*as React from"react";import React__default,{createContext,useReducer,useRef,useState,useEffect,useMemo,useContext,createElement,forwardRef,useLayoutEffect,useCallback,useImperativeHandle,Component,Fragment as Fragment$1}from"react";import{s as getDefaultExportFromCjs,y as filter,z as omit,u as useCollapsible,A as get,k as getAllIcons,r as commonjsGlobal,x as colors$1,v as highchartsTheme,B as merge,t as highchartsDarkTheme,C as offset$2,E as shift$2,F as flip$2,G as computePosition$1,H as arrow$3,I as createCoords$1,J as round$1,K as max$1,L as min$1,M as rectToClientRect$1,N as getAugmentedNamespace,O as createPopper,Q as uniqueId,S as typography,T as cloneDeep,m as isEmpty$1,U as isString}from"./lib-yWHJ_8mm.js";import*as ReactDOM from"react-dom";import ReactDOM__default,{createPortal}from"react-dom";import{TrixEditor}from"react-trix";import Trix from"trix";import require$$0 from"react-is";const initialState={items:[],dragData:{id:"",initialGroup:""},isDragging:"",activeContainer:""};const reducer=(state,action)=>{switch(action.type){case"SET_ITEMS":return{...state,items:action.payload};case"SET_DRAG_DATA":return{...state,dragData:action.payload};case"SET_IS_DRAGGING":return{...state,isDragging:action.payload};case"SET_ACTIVE_CONTAINER":return{...state,activeContainer:action.payload};case"CHANGE_CATEGORY":return{...state,items:state.items.map((item=>item.id===action.payload.itemId?{...item,container:action.payload.container}:item))};case"REORDER_ITEMS":{const{dragId:dragId,targetId:targetId}=action.payload;const newItems=[...state.items];const draggedItem=newItems.find((item=>item.id===dragId));const targetItem=newItems.find((item=>item.id===targetId));if(!draggedItem||!targetItem||draggedItem.container!==targetItem.container){return state}if(dragId===targetId){return state}const draggedIndex=newItems.findIndex((item=>item.id===dragId));const targetIndex=newItems.findIndex((item=>item.id===targetId));if(draggedIndex===-1||targetIndex===-1){return state}newItems.splice(draggedIndex,1);newItems.splice(targetIndex,0,draggedItem);return{...state,items:newItems}}default:return state}};const DragContext=createContext({});const DraggableContext=()=>{const context=useContext(DragContext);if(context===void 0){throw new Error("DraggableContext must be used within a DraggableProvider")}return context};const DraggableProvider=({children:children,initialItems:initialItems,onReorder:onReorder,onDragStart:onDragStart,onDragEnter:onDragEnter,onDragEnd:onDragEnd,onDrop:onDrop,onDragOver:onDragOver,dropZone:dropZone={type:"ghost",color:"neutral",direction:"vertical"}})=>{const[state,dispatch]=useReducer(reducer,initialState);const initialItemsRef=useRef(initialItems);const[isDragging,setIsDragging]=useState(false);let dropZoneType="ghost";let dropZoneColor="neutral";let dropZoneDirection="vertical";if(typeof dropZone==="string"){dropZoneType=dropZone}else{dropZoneType=dropZone.type||"ghost";dropZoneColor=dropZone.type==="line"?dropZone.color||"primary":dropZone.color||"neutral";if(dropZoneType==="line"){dropZoneDirection=dropZone.direction||"vertical"}}useEffect((()=>{dispatch({type:"SET_ITEMS",payload:initialItems});initialItemsRef.current=initialItems}),[initialItems]);useEffect((()=>{if(onReorder){onReorder(state.items)}}),[state.items,onReorder]);const handleDragStart=(id,container)=>{setIsDragging(true);dispatch({type:"SET_DRAG_DATA",payload:{id:id,initialGroup:container}});dispatch({type:"SET_IS_DRAGGING",payload:id});dispatch({type:"SET_ACTIVE_CONTAINER",payload:container});if(onDragStart)onDragStart(id,container)};const handleDragEnter=(id,container)=>{if(!isDragging||container!==state.activeContainer)return;if(state.dragData.id===id)return;const draggedItem=state.items.find((item=>item.id===state.dragData.id));const targetItem=state.items.find((item=>item.id===id));if(!draggedItem||!targetItem||draggedItem.container!==targetItem.container){return}dispatch({type:"REORDER_ITEMS",payload:{dragId:state.dragData.id,targetId:id}});if(onDragEnter)onDragEnter(id,container)};const handleDragEnd=()=>{setIsDragging(false);dispatch({type:"SET_IS_DRAGGING",payload:""});dispatch({type:"SET_ACTIVE_CONTAINER",payload:""});if(onDragEnd)onDragEnd()};const handleDrop=container=>{const draggedItem=state.items.find((item=>item.id===state.dragData.id));if(draggedItem&&draggedItem.container!==container){dispatch({type:"CHANGE_CATEGORY",payload:{itemId:state.dragData.id,container:container}})}dispatch({type:"SET_IS_DRAGGING",payload:""});dispatch({type:"SET_ACTIVE_CONTAINER",payload:""});setIsDragging(false);if(onDrop)onDrop(container)};const handleDragOver=(e,container)=>{e.preventDefault();e.stopPropagation();dispatch({type:"SET_ACTIVE_CONTAINER",payload:container});if(onDragOver)onDragOver(e,container)};const contextValue=useMemo((()=>({items:state.items,dragData:state.dragData,isDragging:state.isDragging,activeContainer:state.activeContainer,dropZone:dropZoneType,dropZoneColor:dropZoneColor,...dropZoneType==="line"?{direction:dropZoneDirection}:{},handleDragStart:handleDragStart,handleDragEnter:handleDragEnter,handleDragEnd:handleDragEnd,handleDrop:handleDrop,handleDragOver:handleDragOver})),[state,dropZoneType,dropZoneColor,dropZoneDirection,handleDragStart,handleDragEnter,handleDragEnd,handleDrop,handleDragOver]);return jsx$1(DragContext.Provider,{value:contextValue,children:children})};var classnames$1={exports:{}};
1
+ import{jsx as jsx$1,Fragment,jsxs}from"react/jsx-runtime";import*as React from"react";import React__default,{createContext,useReducer,useRef,useState,useEffect,useMemo,useContext,createElement,forwardRef,useLayoutEffect,useCallback,useImperativeHandle,Component,Fragment as Fragment$1}from"react";import{s as getDefaultExportFromCjs,y as filter,z as omit,u as useCollapsible,A as get,k as getAllIcons,r as commonjsGlobal,x as colors$1,v as highchartsTheme,B as merge,t as highchartsDarkTheme,C as offset$2,E as shift$2,F as flip$2,G as computePosition$1,H as arrow$3,I as createCoords$1,J as round$1,K as max$1,L as min$1,M as rectToClientRect$1,N as getAugmentedNamespace,O as createPopper,Q as uniqueId,S as typography,T as cloneDeep,m as isEmpty$1,U as isString}from"./lib-ClNg0TLS.js";import*as ReactDOM from"react-dom";import ReactDOM__default,{createPortal}from"react-dom";import{TrixEditor}from"react-trix";import Trix from"trix";import require$$0 from"react-is";const initialState={items:[],dragData:{id:"",initialGroup:""},isDragging:"",activeContainer:""};const reducer=(state,action)=>{switch(action.type){case"SET_ITEMS":return{...state,items:action.payload};case"SET_DRAG_DATA":return{...state,dragData:action.payload};case"SET_IS_DRAGGING":return{...state,isDragging:action.payload};case"SET_ACTIVE_CONTAINER":return{...state,activeContainer:action.payload};case"CHANGE_CATEGORY":return{...state,items:state.items.map((item=>item.id===action.payload.itemId?{...item,container:action.payload.container}:item))};case"REORDER_ITEMS":{const{dragId:dragId,targetId:targetId}=action.payload;const newItems=[...state.items];const draggedItem=newItems.find((item=>item.id===dragId));const targetItem=newItems.find((item=>item.id===targetId));if(!draggedItem||!targetItem||draggedItem.container!==targetItem.container){return state}if(dragId===targetId){return state}const draggedIndex=newItems.findIndex((item=>item.id===dragId));const targetIndex=newItems.findIndex((item=>item.id===targetId));if(draggedIndex===-1||targetIndex===-1){return state}newItems.splice(draggedIndex,1);newItems.splice(targetIndex,0,draggedItem);return{...state,items:newItems}}default:return state}};const DragContext=createContext({});const DraggableContext=()=>{const context=useContext(DragContext);if(context===void 0){throw new Error("DraggableContext must be used within a DraggableProvider")}return context};const DraggableProvider=({children:children,initialItems:initialItems,onReorder:onReorder,onDragStart:onDragStart,onDragEnter:onDragEnter,onDragEnd:onDragEnd,onDrop:onDrop,onDragOver:onDragOver,dropZone:dropZone={type:"ghost",color:"neutral",direction:"vertical"}})=>{const[state,dispatch]=useReducer(reducer,initialState);const initialItemsRef=useRef(initialItems);const[isDragging,setIsDragging]=useState(false);let dropZoneType="ghost";let dropZoneColor="neutral";let dropZoneDirection="vertical";if(typeof dropZone==="string"){dropZoneType=dropZone}else{dropZoneType=dropZone.type||"ghost";dropZoneColor=dropZone.type==="line"?dropZone.color||"primary":dropZone.color||"neutral";if(dropZoneType==="line"){dropZoneDirection=dropZone.direction||"vertical"}}useEffect((()=>{dispatch({type:"SET_ITEMS",payload:initialItems});initialItemsRef.current=initialItems}),[initialItems]);useEffect((()=>{if(onReorder){onReorder(state.items)}}),[state.items,onReorder]);const handleDragStart=(id,container)=>{setIsDragging(true);dispatch({type:"SET_DRAG_DATA",payload:{id:id,initialGroup:container}});dispatch({type:"SET_IS_DRAGGING",payload:id});dispatch({type:"SET_ACTIVE_CONTAINER",payload:container});if(onDragStart)onDragStart(id,container)};const handleDragEnter=(id,container)=>{if(!isDragging||container!==state.activeContainer)return;if(state.dragData.id===id)return;const draggedItem=state.items.find((item=>item.id===state.dragData.id));const targetItem=state.items.find((item=>item.id===id));if(!draggedItem||!targetItem||draggedItem.container!==targetItem.container){return}dispatch({type:"REORDER_ITEMS",payload:{dragId:state.dragData.id,targetId:id}});if(onDragEnter)onDragEnter(id,container)};const handleDragEnd=()=>{setIsDragging(false);dispatch({type:"SET_IS_DRAGGING",payload:""});dispatch({type:"SET_ACTIVE_CONTAINER",payload:""});if(onDragEnd)onDragEnd()};const handleDrop=container=>{const draggedItem=state.items.find((item=>item.id===state.dragData.id));if(draggedItem&&draggedItem.container!==container){dispatch({type:"CHANGE_CATEGORY",payload:{itemId:state.dragData.id,container:container}})}dispatch({type:"SET_IS_DRAGGING",payload:""});dispatch({type:"SET_ACTIVE_CONTAINER",payload:""});setIsDragging(false);if(onDrop)onDrop(container)};const handleDragOver=(e,container)=>{e.preventDefault();e.stopPropagation();dispatch({type:"SET_ACTIVE_CONTAINER",payload:container});if(onDragOver)onDragOver(e,container)};const contextValue=useMemo((()=>({items:state.items,dragData:state.dragData,isDragging:state.isDragging,activeContainer:state.activeContainer,dropZone:dropZoneType,dropZoneColor:dropZoneColor,...dropZoneType==="line"?{direction:dropZoneDirection}:{},handleDragStart:handleDragStart,handleDragEnter:handleDragEnter,handleDragEnd:handleDragEnd,handleDrop:handleDrop,handleDragOver:handleDragOver})),[state,dropZoneType,dropZoneColor,dropZoneDirection,handleDragStart,handleDragEnter,handleDragEnd,handleDrop,handleDragOver]);return jsx$1(DragContext.Provider,{value:contextValue,children:children})};var classnames$1={exports:{}};
2
2
  /*!
3
3
  Copyright (c) 2018 Jed Watson.
4
4
  Licensed under the MIT License (MIT), see