playbook_ui 14.19.0.pre.alpha.PLAY2033atactionbarrails7730 → 14.19.0.pre.alpha.PLAY2033atactionbarrails7751

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 654590a50901aacde66d35bd4be201e411e132b400b2914cc5e037d3429caded
4
- data.tar.gz: 8d0e41a47e517cf306c76815921ff9aa1f994491a7596b3f259fedd4e0d6e865
3
+ metadata.gz: 17e10404283155ad2d2767e2acdf52c0bfae50af100e565e31adf7f403b3ac9a
4
+ data.tar.gz: fb8e75bd6a39e92574e9755db531c9bc09fa1c79d3a33537458535c57613b329
5
5
  SHA512:
6
- metadata.gz: e2016cd384a81cd7d9f3d4f93e474f4bb5843d3f199b9e1989dfefd41eaa2cd943aee4823e70fde27d2c503f2e4c90e7a3503f176b5d7d5e07f0fa194d866454
7
- data.tar.gz: 7be919bf509875aeb4beb91b4f25f6b163c2b2c5a20a7a510df6204216592e33c6d512991c2c73eec9a5c462b40bd29417c335c497269b13b582db3938445a52
6
+ metadata.gz: e1779f126d2b890116939dcb70297381a7a470f1c2fdc6e5efbc628d196ac4316c25d7ea1da587fe6b70f12ddb5d075b026622f812bf96e98079a49356d3d6be
7
+ data.tar.gz: bb1ef7cb05876677bc259b0efb1801415d2c0d288550142e1e59af0a81a1b12bc5e49e2b0646e891a4b2224221780a37b288a8fbc34a90d2117648b893320dec
@@ -1,21 +1,18 @@
1
1
  <%= pb_content_tag do %>
2
- <% table_id = object.id || "table-#{SecureRandom.hex(8)}" %>
3
- <div id="<%= table_id %>" class="<%= object.classname %>">
4
- <% if object.selectable_rows && object.show_actions_bar %>
5
- <%= pb_rails("advanced_table/table_action_bar", props: {
6
- actions: object.actions,
7
- is_visible: false,
8
- selected_count: 0
9
- }) %>
2
+ <% if object.selectable_rows && object.show_actions_bar %>
3
+ <%= pb_rails("advanced_table/table_action_bar", props: {
4
+ actions: object.actions,
5
+ is_visible: false,
6
+ selected_count: 0
7
+ }) %>
8
+ <% end %>
9
+
10
+ <%= pb_rails("table", props: { size: "sm", data_table: true, number_spacing:"tabular", responsive:"none", dark: dark, classname: object.loading ? "content-loading" : "" }.merge(object.table_props)) do %>
11
+ <% if content.present? %>
12
+ <% content.presence %>
13
+ <% else %>
14
+ <%= pb_rails("advanced_table/table_header", props: { column_definitions: object.column_definitions, enable_toggle_expansion: object.enable_toggle_expansion, responsive: object.responsive, loading: object.loading, selectable_rows: object.selectable_rows }) %>
15
+ <%= pb_rails("advanced_table/table_body", props: { id: object.id, table_data: object.table_data, column_definitions: object.column_definitions, responsive: object.responsive, loading: object.loading, selectable_rows: object.selectable_rows, enable_toggle_expansion: object.enable_toggle_expansion }) %>
10
16
  <% end %>
11
-
12
- <%= pb_rails("table", props: { size: "sm", data_table: true, number_spacing:"tabular", responsive:"none", dark: dark, classname: object.loading ? "content-loading" : "" }.merge(object.table_props)) do %>
13
- <% if content.present? %>
14
- <% content.presence %>
15
- <% else %>
16
- <%= pb_rails("advanced_table/table_header", props: { column_definitions: object.column_definitions, enable_toggle_expansion: object.enable_toggle_expansion, responsive: object.responsive, loading: object.loading, selectable_rows: object.selectable_rows }) %>
17
- <%= pb_rails("advanced_table/table_body", props: { id: object.id, table_data: object.table_data, column_definitions: object.column_definitions, responsive: object.responsive, loading: object.loading, selectable_rows: object.selectable_rows, enable_toggle_expansion: object.enable_toggle_expansion }) %>
18
- <% end %>
19
- <% end %>
20
- </div>
17
+ <% end %>
21
18
  <% end %>
@@ -26,7 +26,7 @@ module Playbook
26
26
  prop :selectable_rows, type: Playbook::Props::Boolean,
27
27
  default: false
28
28
  prop :show_actions_bar, type: Playbook::Props::Boolean,
29
- default: false
29
+ default: true
30
30
  prop :actions, type: Playbook::Props::Array,
31
31
  default: []
32
32
 
@@ -1,6 +1,4 @@
1
- <%
2
- # Define column definitions
3
- column_definitions = [
1
+ <% column_definitions = [
4
2
  {
5
3
  accessor: "year",
6
4
  label: "Year",
@@ -32,15 +30,22 @@ column_definitions = [
32
30
  }
33
31
  ]
34
32
 
35
- # Define actions for the selection bar
36
33
  actions = [
37
34
  pb_rails("circle_icon_button", props: {
38
35
  icon: "file-csv",
39
- variant: "link"
36
+ variant: "link",
37
+ id: "export-selected-rows-btn",
38
+ data: {
39
+ action_type: "export"
40
+ }
40
41
  }),
41
42
  pb_rails("circle_icon_button", props: {
42
43
  icon: "trash-alt",
43
- variant: "link"
44
+ variant: "link",
45
+ id: "delete-selected-rows-btn",
46
+ data: {
47
+ action_type: "delete"
48
+ }
44
49
  })
45
50
  ]
46
51
  %>
@@ -54,3 +59,80 @@ actions = [
54
59
  actions: actions,
55
60
  show_actions_bar: true
56
61
  }) %>
62
+
63
+ <script>
64
+ // Handle action clicks using the data-selected-rows attribute
65
+ window.handleActionClick = function(actionType) {
66
+ const tableContainer = document.getElementById('selectable_rows_with_actions');
67
+ if (!tableContainer) return;
68
+
69
+ // Get selected rows from the data attribute
70
+ const selectedRowsJSON = tableContainer.getAttribute('data-selected-rows');
71
+ let selectedRowIds = [];
72
+
73
+ try {
74
+ // Parse the JSON string from the data attribute
75
+ if (selectedRowsJSON) {
76
+ selectedRowIds = JSON.parse(selectedRowsJSON);
77
+ }
78
+ } catch (e) {
79
+ // Fallback if JSON parsing fails
80
+ const checkboxes = tableContainer.querySelectorAll('input[type="checkbox"]:checked');
81
+ const selectedCheckboxes = Array.from(checkboxes).filter(checkbox =>
82
+ checkbox.id !== 'select-all-rows' &&
83
+ !checkbox.closest('#select-all-rows')
84
+ );
85
+ selectedRowIds = selectedCheckboxes.map(checkbox => checkbox.id);
86
+ }
87
+
88
+ // Show appropriate message
89
+ if (!selectedRowIds || selectedRowIds.length === 0) {
90
+ alert('No Selection Made');
91
+ } else {
92
+ if (actionType === 'export') {
93
+ alert(`Row ids ${selectedRowIds.join(', ')} will be exported!`);
94
+ } else if (actionType === 'delete') {
95
+ alert(`Row ids ${selectedRowIds.join(', ')} will be deleted!`);
96
+ }
97
+ }
98
+ };
99
+
100
+ // Add event listeners when the DOM is ready
101
+ document.addEventListener('DOMContentLoaded', function() {
102
+ // Get the buttons
103
+ const exportBtn = document.getElementById('export-selected-rows-btn');
104
+ const deleteBtn = document.getElementById('delete-selected-rows-btn');
105
+
106
+ // Add click event listeners
107
+ if (exportBtn) {
108
+ exportBtn.addEventListener('click', function(e) {
109
+ e.preventDefault();
110
+ window.handleActionClick('export');
111
+ });
112
+ }
113
+
114
+ if (deleteBtn) {
115
+ deleteBtn.addEventListener('click', function(e) {
116
+ e.preventDefault();
117
+ window.handleActionClick('delete');
118
+ });
119
+ }
120
+
121
+ // Optional: Event delegation through the action bar
122
+ const actionBar = document.querySelector('.row-selection-actions-card');
123
+ if (actionBar) {
124
+ actionBar.addEventListener('click', function(e) {
125
+ const exportButton = e.target.closest('#export-selected-rows-btn');
126
+ const deleteButton = e.target.closest('#delete-selected-rows-btn');
127
+
128
+ if (exportButton) {
129
+ e.preventDefault();
130
+ window.handleActionClick('export');
131
+ } else if (deleteButton) {
132
+ e.preventDefault();
133
+ window.handleActionClick('delete');
134
+ }
135
+ });
136
+ }
137
+ });
138
+ </script>
@@ -0,0 +1,3 @@
1
+ Custom actions content can be rendered within the Actions Bar as shown in this doc example. The component passed to `actions` will be rendered on the right of the actionsBar.
2
+
3
+ You can utilize script tags with your actions to provide your buttons with any clickable events needed.
@@ -0,0 +1,40 @@
1
+ <% column_definitions = [
2
+ {
3
+ accessor: "year",
4
+ label: "Year",
5
+ cellAccessors: ["quarter", "month", "day"],
6
+ },
7
+ {
8
+ accessor: "newEnrollments",
9
+ label: "New Enrollments",
10
+ },
11
+ {
12
+ accessor: "scheduledMeetings",
13
+ label: "Scheduled Meetings",
14
+ },
15
+ {
16
+ accessor: "attendanceRate",
17
+ label: "Attendance Rate",
18
+ },
19
+ {
20
+ accessor: "completedClasses",
21
+ label: "Completed Classes",
22
+ },
23
+ {
24
+ accessor: "classCompletionRate",
25
+ label: "Class Completion Rate",
26
+ },
27
+ {
28
+ accessor: "graduatedStudents",
29
+ label: "Graduated Students",
30
+ }
31
+ ] %>
32
+
33
+ <%= pb_rails("advanced_table", props: {
34
+ id: "selectable_rows_with_actions",
35
+ table_data: @table_data_no_subrows,
36
+ column_definitions: column_definitions,
37
+ selectable_rows: true,
38
+ enable_toggle_expansion: "none",
39
+ show_actions_bar: false
40
+ }) %>
@@ -0,0 +1 @@
1
+ `show_actions_bar` is an optional prop that renders the header at the top showing the row count. This is set to `true` by default but can be toggled off by setting it to `false`
@@ -16,6 +16,7 @@ examples:
16
16
  - advanced_table_selectable_rows_rails: Selectable Rows
17
17
  - advanced_table_selectable_rows_no_subrows_rails: Selectable Rows (No Subrows)
18
18
  - advanced_table_selectable_rows_actions_rails: Selectable Rows (With Actions)
19
+ - advanced_table_selectable_rows_header_rails: Selectable Rows (No Actions Bar)
19
20
 
20
21
  react:
21
22
  - advanced_table_default: Default (Required Props)
@@ -95,6 +95,9 @@ export default class PbAdvancedTable extends PbEnhancedElement {
95
95
  actionBar.style.height = height;
96
96
  actionBar.classList.add("is-visible");
97
97
  actionBar.classList.add("show-action-card");
98
+ actionBar.classList.replace("p_none", "p_xs");
99
+ // This is for keeping the border around the card when showing the action bar
100
+ actionBar.classList.replace("pb_card_kit_deselected_border_none", "pb_card_kit_deselected");
98
101
  actionBar.style.overflow = "hidden";
99
102
 
100
103
  // Complete animation after delay
@@ -124,10 +127,15 @@ export default class PbAdvancedTable extends PbEnhancedElement {
124
127
  window.setTimeout(() => {
125
128
  actionBar.classList.remove("is-visible");
126
129
  actionBar.classList.remove("show-action-card");
130
+ actionBar.classList.replace("p_xs", "p_none");
131
+ // This is for removing the border when hiding the action bar
132
+ actionBar.classList.replace( "pb_card_kit_deselected", "pb_card_kit_deselected_border_none");
127
133
  }, 300);
128
134
  }
129
135
 
130
- // Check if row is expanded
136
+ // Check if the row is expanded or collapsed
137
+ // This is used to determine the background color of the row
138
+ // when the checkbox is checked or unchecked
131
139
  isRowExpanded(rowEl) {
132
140
  const closeIcon = rowEl.querySelector(UP_ARROW_SELECTOR);
133
141
  return closeIcon?.style.display === "none" || !closeIcon;
@@ -355,7 +363,8 @@ export default class PbAdvancedTable extends PbEnhancedElement {
355
363
  });
356
364
 
357
365
  // Remove visibility classes
358
- actionBar.classList.remove("is-visible", "show-action-card");
366
+ actionBar.classList.remove("p_xs", "is-visible", "show-action-card");
367
+ actionBar.classList.add("p_none");
359
368
 
360
369
  // Add CSS rules
361
370
  const styleId = `action-bar-styles-${tableId}`;
@@ -388,7 +397,8 @@ export default class PbAdvancedTable extends PbEnhancedElement {
388
397
  actionBar.style.height = 'auto';
389
398
  actionBar.style.overflow = 'visible';
390
399
  actionBar.style.opacity = '1';
391
- actionBar.classList.add("is-visible", "show-action-card");
400
+ actionBar.classList.remove("p_none");
401
+ actionBar.classList.add("p_xs", "is-visible", "show-action-card");
392
402
 
393
403
  // Update the count
394
404
  const countElement = actionBar.querySelector(".selected-count");
@@ -400,7 +410,8 @@ export default class PbAdvancedTable extends PbEnhancedElement {
400
410
  actionBar.style.height = '0px';
401
411
  actionBar.style.overflow = 'hidden';
402
412
  actionBar.style.opacity = '0';
403
- actionBar.classList.remove("is-visible", "show-action-card");
413
+ actionBar.classList.remove("p_xs", "is-visible", "show-action-card");
414
+ actionBar.classList.add("p_none");
404
415
  }
405
416
  });
406
417
  });
@@ -696,21 +707,31 @@ document.addEventListener('DOMContentLoaded', () => {
696
707
  });
697
708
 
698
709
  // Remove any visibility classes
699
- actionBar.classList.remove("is-visible", "show-action-card");
710
+ actionBar.classList.remove("p_xs", "is-visible", "show-action-card");
711
+ actionBar.classList.add("p_none");
700
712
 
701
- // Direct DOM manipulation for checkboxes
713
+ // Direct DOM manipulation for checkboxes - exclude select-all checkbox
702
714
  const checkboxes = table.querySelectorAll('input[type="checkbox"]');
703
715
  checkboxes.forEach(checkbox => {
704
716
  checkbox.addEventListener('change', function() {
705
- // Count selected checkboxes
706
- const selectedCount = Array.from(checkboxes).filter(cb => cb.checked).length;
717
+ // Get all checked checkboxes
718
+ const allCheckedCheckboxes = Array.from(checkboxes).filter(cb => cb.checked);
719
+
720
+ // Filter out the select-all checkbox
721
+ const selectedRowCheckboxes = allCheckedCheckboxes.filter(cb => {
722
+ return cb.id !== 'select-all-rows' && !cb.closest('#select-all-rows');
723
+ });
724
+
725
+ // Get the selected count (excluding select-all)
726
+ const selectedCount = selectedRowCheckboxes.length;
707
727
 
708
728
  if (selectedCount > 0) {
709
729
  // Show action bar directly
710
730
  actionBar.style.height = 'auto';
711
731
  actionBar.style.overflow = 'visible';
712
732
  actionBar.style.opacity = '1';
713
- actionBar.classList.add("is-visible", "show-action-card");
733
+ actionBar.classList.remove("p_none");
734
+ actionBar.classList.add("p_xs", "is-visible", "show-action-card");
714
735
 
715
736
  // Update the count
716
737
  const countElement = actionBar.querySelector(".selected-count");
@@ -722,7 +743,8 @@ document.addEventListener('DOMContentLoaded', () => {
722
743
  actionBar.style.height = '0px';
723
744
  actionBar.style.overflow = 'hidden';
724
745
  actionBar.style.opacity = '0';
725
- actionBar.classList.remove("is-visible", "show-action-card");
746
+ actionBar.classList.add("p_none");
747
+ actionBar.classList.remove("p_xs", "is-visible", "show-action-card");
726
748
  }
727
749
  });
728
750
  });
@@ -6,6 +6,7 @@ import Flex from '../../pb_flex/_flex'
6
6
  import FlexItem from '../../pb_flex/_flex_item'
7
7
  import Avatar from '../../pb_avatar/_avatar'
8
8
  import User from '../../pb_user/_user'
9
+ import Body from '../../pb_body/_body'
9
10
 
10
11
  const DropdownWithCustomDisplay = (props) => {
11
12
  const [selectedOption, setSelectedOption] = useState();
@@ -50,10 +51,20 @@ const DropdownWithCustomDisplay = (props) => {
50
51
  <>
51
52
  {
52
53
  selectedOption && (
54
+ <Flex align="center">
53
55
  <Avatar
54
56
  name={selectedOption.label}
55
57
  size="xs"
56
58
  />
59
+ <Body
60
+ marginX="xs"
61
+ text={selectedOption.label}
62
+ />
63
+ <Badge
64
+ text={selectedOption.status}
65
+ variant={selectedOption.status == "Offline" ? "neutral" : selectedOption.status == "Online" ? "success" : "warning"}
66
+ />
67
+ </Flex>
57
68
  )
58
69
  }
59
70
  </>
@@ -1,4 +1,4 @@
1
- Optionally utilize `customDisplay` on the `Dropdown.Trigger` subcomponent to customize its content after an option is selected. The component passed to customDisplay will be rendered to the left of the default text-based display. In this example the Avatar kit is being used.
1
+ Optionally utilize `customDisplay` on the `Dropdown.Trigger` subcomponent to customize its content after an option is selected. Pass in any combination of kits to create a custom display. When a user clicks on an option, the kits passed into `customDisplay` will display as the selected option.
2
2
 
3
3
  The `placeholder` prop can also be used to customize the placeholder text for the default `Dropdown.Trigger`.
4
4
 
@@ -38,7 +38,11 @@
38
38
 
39
39
  <%
40
40
  custom_display = capture do
41
- pb_rails("avatar", props: { name: "Courtney Long", size: "xs" })
41
+ pb_rails("flex", props: { align: "center" }) do
42
+ concat(pb_rails("avatar", props: { name: "", size: "xs", id: "dropdown-avatar" }))
43
+ concat(pb_rails("body", props: { text: "", size: "xs", margin_x: "xs", id: "dropdown-avatar-name" }))
44
+ concat(pb_rails("badge", props: { text: "", id: "dropdown-avatar-status" }))
45
+ end
42
46
  end
43
47
  %>
44
48
 
@@ -62,4 +66,31 @@
62
66
  <% end %>
63
67
  <% end %>
64
68
  <% end %>
65
- <% end %>
69
+ <% end %>
70
+
71
+
72
+ <script>
73
+ document.addEventListener("pb:dropdown:selected", (e) => {
74
+ const option = e.detail;
75
+ const dropdown = e.target;
76
+
77
+ const display = dropdown.querySelector("#dropdown_trigger_custom_display");
78
+ if (!display) return;
79
+
80
+ const nameEl = display.querySelector("#dropdown-avatar-name");
81
+ if (nameEl) nameEl.textContent = option.label;
82
+
83
+ const avatarEl = display.querySelector("#dropdown-avatar").querySelector(".avatar_wrapper");
84
+ const initials = (option.label[0] + option.label.split(" ").pop()[0]).toUpperCase();
85
+ if (avatarEl) {
86
+ avatarEl.dataset.name = option.label;
87
+ avatarEl.setAttribute("data-initials", initials);
88
+ }
89
+ const badgeEl = display.querySelector("#dropdown-avatar-status");
90
+ const variant = option.status === "Online" ? "success" : option.status === "Offline" ? "neutral" : "warning";
91
+ if (badgeEl) {
92
+ badgeEl.querySelector("span").textContent = option.status;
93
+ badgeEl.className = 'pb_badge_kit_' + variant;
94
+ }
95
+ });
96
+ </script>
@@ -1,4 +1,6 @@
1
- Optionally utilize `custom_display` on the `dropdown/dropdown_trigger` subcomponent to customize its content after an option is selected. The component passed to custom_display will be rendered to the left of the default text-based display. In this example the Avatar kit is being used.
1
+ Optionally utilize `custom_display` on the `dropdown/dropdown_trigger` subcomponent to customize its content after an option is selected. Pass in any combination of kits to create a custom display. When a user clicks on an option, the kits passed into `custom_display` will display as the selected option.
2
+
3
+ Make use of a script to help set the custom_display with the correct value. By using the pb:dropdown:selected event listener, you can target the kits with a querySelector and update them dynamically with the values needed to match the selected option. Make sure to add an ID to the kits being passed in.
2
4
 
3
5
  The `placeholder` prop can also be used to customize the placeholder text for the default `dropdown/dropdown_trigger`.
4
6
 
@@ -142,9 +142,22 @@ export default class PbDropdown extends PbEnhancedElement {
142
142
  const customDisplayElement = this.element.querySelector(
143
143
  "#dropdown_trigger_custom_display"
144
144
  );
145
+
145
146
  if (triggerElement) {
146
147
  const selectedLabel = JSON.parse(value).label;
147
- triggerElement.textContent = selectedLabel;
148
+ if (customDisplayElement) {
149
+ triggerElement.textContent = ""
150
+ this.element.setAttribute("data-option-selected", value);
151
+ const selectedObj = JSON.parse(value);
152
+ this.element.dispatchEvent(
153
+ new CustomEvent("pb:dropdown:selected", {
154
+ detail: selectedObj,
155
+ bubbles: true,
156
+ })
157
+ );
158
+ } else {
159
+ triggerElement.textContent = selectedLabel
160
+ }
148
161
  if (customDisplayElement) {
149
162
  customDisplayElement.style.display = "block";
150
163
  customDisplayElement.style.paddingRight = "8px";
@@ -73,7 +73,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
73
73
  );
74
74
 
75
75
  const customDisplayPlaceholder = selected?.label ? (
76
- <b>{selected.label}</b>
76
+ ""
77
77
  ) : autocomplete ? (
78
78
  ""
79
79
  ) : placeholder ? (