playbook_ui 14.20.0.pre.alpha.PLAY2178advancedtablerowpinning7978 → 14.20.0.pre.alpha.PLAY2214checkboxhiddeninput8052

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +79 -89
  3. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +4 -1
  4. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +8 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +5 -2
  6. data/app/pb_kits/playbook/pb_advanced_table/index.js +2 -0
  7. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +4 -11
  8. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +28 -7
  9. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_custom.html.erb +1 -0
  10. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_custom_rails.md +1 -0
  11. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_form.html.erb +22 -0
  12. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_form.md +5 -0
  13. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +2 -48
  14. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate_rails.md +1 -0
  15. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_options.html.erb +1 -0
  16. data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +1 -0
  17. data/app/pb_kits/playbook/pb_checkbox/index.js +56 -0
  18. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +17 -58
  19. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +12 -3
  20. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.jsx +42 -0
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.md +1 -0
  22. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +2 -0
  23. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -1
  24. data/app/pb_kits/playbook/pb_dropdown/index.js +24 -0
  25. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +14 -10
  26. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +26 -15
  27. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -1
  28. data/app/pb_kits/playbook/pb_popover/index.ts +9 -4
  29. data/app/pb_kits/playbook/pb_table/docs/_table_with_selectable_rows.html.erb +3 -51
  30. data/app/pb_kits/playbook/pb_table/styles/_mobile_collapse.scss +1 -1
  31. data/dist/chunks/{_typeahead-CRW6dJbW.js → _typeahead-CoOpeYom.js} +1 -1
  32. data/dist/chunks/_weekday_stacked-BppvLxTS.js +45 -0
  33. data/dist/chunks/{lib-D5R1BjUn.js → lib-D7Va7yqa.js} +1 -1
  34. data/dist/chunks/{pb_form_validation-BZ2AVAi_.js → pb_form_validation-DSkdRDMf.js} +1 -1
  35. data/dist/chunks/vendor.js +1 -1
  36. data/dist/menu.yml +1 -1
  37. data/dist/playbook-doc.js +1 -1
  38. data/dist/playbook-rails-react-bindings.js +1 -1
  39. data/dist/playbook-rails.js +1 -1
  40. data/dist/playbook.css +1 -1
  41. data/lib/playbook/version.rb +1 -1
  42. metadata +13 -6
  43. data/dist/chunks/_weekday_stacked-yWpUc_c0.js +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8e79a2664649f32a2195a4e9199e868f6c1ebac20764479066f12c2dbbd7e9c
4
- data.tar.gz: da415e2485131587cc6a32a03c8145152df934616500a008ef06b4e49c6c049a
3
+ metadata.gz: 7ccd5f34dd1f2a70f68d8c16e9209ace9bd33d53b7952e0a01a6923c3f89552e
4
+ data.tar.gz: 43ebf8006b68996baadd8f823d964ef2908c8890bb5d7914d3102910af7c154a
5
5
  SHA512:
6
- metadata.gz: fd7c9331ca4274f9e6e95d186ada4456ddfa8240d1cc48249dd0b899931bf3db83576abfbfed0ebd4105e2159d57cc9efb75a3a16e2d33cc6a37262dede08746
7
- data.tar.gz: 9072a56af0c016514dd3a3f3d5b0e20fbdbaea20ca5b25d258f3c31c859fc0d4edc855c58ed297089453525b67df0789c6ff216b9dd500eaa5115664a4bba396
6
+ metadata.gz: f36f96c61a5b520710754222b4b8bcc111ac2ee5d5b62137f99e1e033f86601c0fed765c36d6b15a44bf40f345b3f299a8e611fd7fc80e40eeebd87ffea9dfa6
7
+ data.tar.gz: df815bdc4cdac84fc2fcb215e0c304f7656a01eb9e4260677fe3eff97ede106c2ff586ce428cb4fc9b1f5ccf5f044a645da5796ad4131276d54899ed650d2041
@@ -19,6 +19,71 @@ type RegularTableViewProps = {
19
19
  subRowHeaders?: string[]
20
20
  }
21
21
 
22
+ // Helper function for Table Rendering
23
+ const TableCellRenderer = ({
24
+ row,
25
+ collapsibleTrail = true,
26
+ loading = false,
27
+ stickyLeftColumn,
28
+ columnPinning
29
+ }: {
30
+ row: Row<GenericObject>
31
+ collapsibleTrail?: boolean
32
+ loading?: boolean | string
33
+ stickyLeftColumn?: string[]
34
+ columnPinning: { left: string[] }
35
+ }) => {
36
+ return (
37
+ <>
38
+ {row.getVisibleCells().map((cell: Cell<GenericObject, unknown>, i: number) => {
39
+ const isPinnedLeft = columnPinning.left.includes(cell.column.id);
40
+ const isLastCell = (() => {
41
+ const parent = cell.column.parent;
42
+ if (!parent) {
43
+ const last = row.getVisibleCells().at(-1);
44
+ return last?.column.id === cell.column.id;
45
+ }
46
+
47
+ const visibleSiblings = parent.columns.filter(col => col.getIsVisible());
48
+ return visibleSiblings.at(-1)?.id === cell.column.id;
49
+ })();
50
+
51
+ const { column } = cell;
52
+
53
+ return (
54
+ <td
55
+ align="right"
56
+ className={classnames(
57
+ `${cell.id}-cell position_relative`,
58
+ isChrome() ? "chrome-styles" : "",
59
+ isPinnedLeft && 'pinned-left',
60
+ stickyLeftColumn && stickyLeftColumn.length > 0 && isPinnedLeft && 'sticky-left',
61
+ isLastCell && 'last-cell',
62
+ )}
63
+ key={`${cell.id}-data`}
64
+ style={{
65
+ left: isPinnedLeft
66
+ ? i === 1 // Accounting for set min-width for first column
67
+ ? '180px'
68
+ : `${column.getStart("left")}px`
69
+ : undefined,
70
+ }}
71
+ >
72
+ {collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
73
+ <span id={`${cell.id}-span`}>
74
+ {loading ? (
75
+ <LoadingCell />
76
+ ) : (
77
+ flexRender(cell.column.columnDef.cell, cell.getContext())
78
+ )}
79
+ </span>
80
+ </td>
81
+ );
82
+ })}
83
+ </>
84
+ )
85
+ }
86
+
22
87
  export const RegularTableView = ({
23
88
  collapsibleTrail = true,
24
89
  subRowHeaders,
@@ -70,50 +135,13 @@ export const RegularTableView = ({
70
135
  zIndex: '3'
71
136
  }}
72
137
  >
73
- {row.getVisibleCells().map((cell: Cell<GenericObject, unknown>, i: number) => {
74
- const isPinnedLeft = columnPinning.left.includes(cell.column.id);
75
- const isLastCell = (() => {
76
- const parent = cell.column.parent;
77
- if (!parent) {
78
- const last = row.getVisibleCells().at(-1);
79
- return last?.column.id === cell.column.id;
80
- }
81
-
82
- const visibleSiblings = parent.columns.filter(col => col.getIsVisible());
83
- return visibleSiblings.at(-1)?.id === cell.column.id;
84
- })();
85
-
86
- const { column } = cell;
87
- return (
88
- <td
89
- align="right"
90
- className={classnames(
91
- `${cell.id}-cell position_relative`,
92
- isChrome() ? "chrome-styles" : "",
93
- isPinnedLeft && 'pinned-left',
94
- stickyLeftColumn && stickyLeftColumn.length > 0 && isPinnedLeft && 'sticky-left',
95
- isLastCell && 'last-cell',
96
- )}
97
- key={`${cell.id}-data`}
98
- style={{
99
- left: isPinnedLeft
100
- ? i === 1 //Accounting for set min-width for first column
101
- ? '180px'
102
- : `${column.getStart("left")}px`
103
- : undefined,
104
- }}
105
- >
106
- {collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
107
- <span id={`${cell.id}-span`}>
108
- {loading ? (
109
- <LoadingCell />
110
- ) : (
111
- flexRender(cell.column.columnDef.cell, cell.getContext())
112
- )}
113
- </span>
114
- </td>
115
- );
116
- })}
138
+ <TableCellRenderer
139
+ collapsibleTrail={collapsibleTrail}
140
+ columnPinning={columnPinning}
141
+ loading={loading}
142
+ row={row}
143
+ stickyLeftColumn={stickyLeftColumn}
144
+ />
117
145
  </tr>
118
146
  )
119
147
  }
@@ -167,51 +195,13 @@ export const RegularTableView = ({
167
195
  />
168
196
  </td>
169
197
  )}
170
-
171
- {row.getVisibleCells().map((cell: Cell<GenericObject, unknown>, i: number) => {
172
- const isPinnedLeft = columnPinning.left.includes(cell.column.id);
173
- const isLastCell = (() => {
174
- const parent = cell.column.parent;
175
- if (!parent) {
176
- const last = row.getVisibleCells().at(-1);
177
- return last?.column.id === cell.column.id;
178
- }
179
-
180
- const visibleSiblings = parent.columns.filter(col => col.getIsVisible());
181
- return visibleSiblings.at(-1)?.id === cell.column.id;
182
- })();
183
-
184
- const { column } = cell;
185
- return (
186
- <td
187
- align="right"
188
- className={classnames(
189
- `${cell.id}-cell position_relative`,
190
- isChrome() ? "chrome-styles" : "",
191
- isPinnedLeft && 'pinned-left',
192
- stickyLeftColumn && stickyLeftColumn.length > 0 && isPinnedLeft && 'sticky-left',
193
- isLastCell && 'last-cell',
194
- )}
195
- key={`${cell.id}-data`}
196
- style={{
197
- left: isPinnedLeft
198
- ? i === 1 //Accounting for set min-width for first column
199
- ? '180px'
200
- : `${column.getStart("left")}px`
201
- : undefined,
202
- }}
203
- >
204
- {collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
205
- <span id={`${cell.id}-span`}>
206
- {loading ? (
207
- <LoadingCell />
208
- ) : (
209
- flexRender(cell.column.columnDef.cell, cell.getContext())
210
- )}
211
- </span>
212
- </td>
213
- );
214
- })}
198
+ <TableCellRenderer
199
+ collapsibleTrail={collapsibleTrail}
200
+ columnPinning={columnPinning}
201
+ loading={loading}
202
+ row={row}
203
+ stickyLeftColumn={stickyLeftColumn}
204
+ />
215
205
  </tr>
216
206
 
217
207
  {/* Display LoadingInline if Row Data is querying and there are no children already */}
@@ -24,7 +24,10 @@ interface UseTableStateProps {
24
24
  loading?: boolean | string;
25
25
  pagination?: boolean;
26
26
  paginationProps?: GenericObject;
27
- pinnedRows?: any;
27
+ pinnedRows?: {
28
+ value?: RowPinningState;
29
+ onChange?: (value: RowPinningState) => void;
30
+ };
28
31
  virtualizedRows?: boolean;
29
32
  tableOptions?: GenericObject;
30
33
  onRowSelectionChange?: (arg: RowSelectionState) => void;
@@ -368,6 +368,10 @@
368
368
  box-shadow: 1px 0px 0px 0px var(--column-border-color) !important;
369
369
  }
370
370
 
371
+ .pb_table_td:nth-child(2) {
372
+ box-shadow: inset 1px 0px 0px 0px var(--column-border-color) !important;
373
+ }
374
+
371
375
  // Color for collapsible trail
372
376
  .collapsible-trail {
373
377
  background-color: $border_light !important;
@@ -564,6 +568,10 @@
564
568
  box-shadow: $shadow_deep !important;
565
569
  }
566
570
 
571
+ .pb_table_td:nth-child(2) {
572
+ box-shadow: 0 0 0 0 !important;
573
+ }
574
+
567
575
  .pb_advanced_table_header,
568
576
  .pb_advanced_table_body {
569
577
  th.sticky-left,
@@ -2,7 +2,7 @@ import React, { useRef, useEffect, useState, useCallback } from "react";
2
2
  import classnames from "classnames";
3
3
 
4
4
  import { GenericObject } from "../types";
5
- import { Row, RowSelectionState } from "@tanstack/react-table";
5
+ import { Row, RowSelectionState, RowPinningState } from "@tanstack/react-table";
6
6
 
7
7
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props";
8
8
  import { globalProps, GlobalProps } from "../utilities/globalProps";
@@ -52,7 +52,10 @@ type AdvancedTableProps = {
52
52
  onToggleExpansionClick?: (arg: Row<GenericObject>) => void
53
53
  pagination?: boolean,
54
54
  paginationProps?: GenericObject,
55
- pinnedRows?: any,
55
+ pinnedRows?: {
56
+ value?: RowPinningState;
57
+ onChange?: (value: RowPinningState) => void;
58
+ };
56
59
  responsive?: "scroll" | "none",
57
60
  selectableRows?: boolean,
58
61
  showActionsBar?: boolean,
@@ -569,6 +569,8 @@ class PbAdvancedTableActionBar {
569
569
  actionBar.style.height = 'auto';
570
570
  actionBar.style.overflow = 'visible';
571
571
  actionBar.style.opacity = '1';
572
+ actionBar.style.transitionProperty = 'all';
573
+ actionBar.style.transitionTimingFunction = 'ease-in-out';
572
574
  actionBar.classList.remove("p_none");
573
575
  actionBar.classList.add("p_xs", "is-visible", "show-action-card");
574
576
 
@@ -1,16 +1,9 @@
1
1
  <%= pb_content_tag(:label) do %>
2
2
  <%= content.presence || object.input %>
3
- <% if object.indeterminate %>
4
- <span data-pb-checkbox-icon-span="true" class="pb_checkbox_indeterminate">
5
- <%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon", fixed_width: true}) %>
6
- <%= pb_rails("icon", props: { icon: "check", classname: "check_icon hidden", fixed_width: true}) %>
7
- </span>
8
- <% else %>
9
- <span data-pb-checkbox-icon-span="true" class="pb_checkbox_checkmark">
10
- <%= pb_rails("icon", props: { icon: "check", classname: "check_icon", fixed_width: true}) %>
11
- <%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon hidden", fixed_width: true}) %>
12
- </span>
13
- <% end %>
3
+ <span data-pb-checkbox-icon-span="true" class="pb_checkbox_checkmark">
4
+ <%= pb_rails("icon", props: { icon: "check", classname: "check_icon", fixed_width: true}) %>
5
+ <%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon hidden", fixed_width: true}) %>
6
+ </span>
14
7
  <span class="pb_checkbox_label">
15
8
  <%= pb_rails("body", props: { status: object.checkbox_label_status, text: object.text, dark: object.dark, margin_right: object.form_spacing ? "xs" : "" }) %>
16
9
  </span>
@@ -5,7 +5,8 @@ module Playbook
5
5
  class Checkbox < Playbook::KitBase
6
6
  prop :error, type: Playbook::Props::Boolean, default: false
7
7
  prop :checked, type: Playbook::Props::Boolean, default: false
8
- prop :indeterminate, type: Playbook::Props::Boolean, default: false
8
+ prop :indeterminate_main, type: Playbook::Props::Boolean, default: false
9
+ prop :indeterminate_parent
9
10
  prop :text
10
11
  prop :value
11
12
  prop :name
@@ -17,19 +18,43 @@ module Playbook
17
18
  default: false
18
19
  prop :form_spacing, type: Playbook::Props::Boolean,
19
20
  default: false
21
+ prop :hidden_input, type: Playbook::Props::Boolean,
22
+ default: false
23
+ prop :hidden_value
20
24
 
21
25
  def classname
22
- generate_classname("pb_checkbox_kit", checked_class) + indeterminate_class + error_class
26
+ generate_classname("pb_checkbox_kit", checked_class) + error_class
23
27
  end
24
28
 
25
29
  def input
26
- check_box_tag(name, value, checked, input_options.merge(disabled: disabled))
30
+ inputs = []
31
+ effective_name = name || input_options[:name]
32
+ effective_value = value || input_options[:value] || "1"
33
+ is_checked = checked || input_options[:checked]
34
+
35
+ inputs << hidden_field_tag(effective_name, hidden_value || "0") if hidden_input && effective_name.present?
36
+
37
+ inputs << check_box_tag(
38
+ effective_name,
39
+ effective_value,
40
+ is_checked,
41
+ input_options.merge(disabled: disabled)
42
+ )
43
+
44
+ safe_join(inputs)
27
45
  end
28
46
 
29
47
  def checkbox_label_status
30
48
  error ? "negative" : nil
31
49
  end
32
50
 
51
+ def data
52
+ Hash(prop(:data)).merge(
53
+ pb_checkbox_indeterminate_main: indeterminate_main,
54
+ pb_checkbox_indeterminate_parent: indeterminate_parent
55
+ )
56
+ end
57
+
33
58
  private
34
59
 
35
60
  def error_class
@@ -39,10 +64,6 @@ module Playbook
39
64
  def checked_class
40
65
  checked ? "on" : "off"
41
66
  end
42
-
43
- def indeterminate_class
44
- indeterminate ? " indeterminate" : ""
45
- end
46
67
  end
47
68
  end
48
69
  end
@@ -1,3 +1,4 @@
1
1
  <%= pb_rails("checkbox", props: {text: "Custom Checkbox"}) do%>
2
+ <input type="hidden" name="custom-name" value="0" />
2
3
  <input type="checkbox" name="custom-name" value="custom-value"/>
3
4
  <% end %>
@@ -0,0 +1 @@
1
+ When using a custom checkbox wrapped in the Checkbox kit, hidden inputs are not automatically included and cannot be prop enabled. Manually add a hidden input before the checkbox if necessary to submit a value when the checkbox is unchecked (as is standard in Rails forms).
@@ -0,0 +1,22 @@
1
+
2
+ <%= pb_form_with(scope: :example, url: "", method: :get) do |form| %>
3
+ <%=pb_rails("flex", props: { gap: "sm", orientation: "column"}) do %>
4
+ <%= pb_rails("checkbox" , props: {
5
+ text: "1. pb_rails(\"checkbox\") Checkbox from kit",
6
+ value: "checkbox-value",
7
+ name: "checkbox-name",
8
+ hidden_input: true
9
+ }) %>
10
+ <%= form.check_box :example_checkbox,
11
+ data: { field1: "value1", field2: "value2" },
12
+ props: { text: "2. form.check_box Checkbox from Form Builder" },
13
+ unchecked_value: "no",
14
+ id: "checkbox-id",
15
+ name: "checkbox-name",
16
+ class: "checkbox-class"
17
+ %>
18
+ <%= form.actions do |action| %>
19
+ <%= action.button props: { type: "submit", text: "Submit", variant: "primary" } %>
20
+ <% end %>
21
+ <% end %>
22
+ <% end %>
@@ -0,0 +1,5 @@
1
+ The way to access hidden inputs for form submission depends on which version of the kit being used within the form context.
2
+
3
+ If using the Rails Checkbox version of the kit, set `hidden_input: true`. Inspect Checkbox #1 in the example above to see the hidden input in the DOM.
4
+
5
+ If using the Form Builder version of the kit (reference the [Form kit page](https://playbook.powerapp.cloud/kits/form) for more on these), the hidden input will appear if the input has a set `unchecked_value`. Inspect Checkbox #2 in the example above (and the two checkbox examples on the Form kit page) to see the hidden input in the DOM. See the [Rails check_box FormHelper docs](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box) for more.
@@ -9,11 +9,10 @@
9
9
  <tr>
10
10
  <th>
11
11
  <%= pb_rails("checkbox", props: {
12
- checked: true,
13
12
  text: "Uncheck All",
14
13
  value: "checkbox-value",
15
14
  name: "main-checkbox",
16
- indeterminate: true,
15
+ indeterminate_main: true,
17
16
  id: "indeterminate-checkbox"
18
17
  }) %>
19
18
  </th>
@@ -30,55 +29,10 @@
30
29
  value: checkbox[:id],
31
30
  name: "#{checkbox[:id]}-indeterminate-checkbox",
32
31
  id: "#{checkbox[:id]}-indeterminate-checkbox",
32
+ indeterminate_parent: "indeterminate-checkbox",
33
33
  }) %>
34
34
  </td>
35
35
  </tr>
36
36
  <% end %>
37
37
  </tbody>
38
38
  <% end %>
39
-
40
- <script>
41
- document.addEventListener('DOMContentLoaded', function() {
42
- const mainCheckboxWrapper = document.getElementById('indeterminate-checkbox');
43
- const mainCheckbox = document.getElementsByName("main-checkbox")[0];
44
- const childCheckboxes = document.querySelectorAll('input[type="checkbox"][id$="indeterminate-checkbox"]');
45
-
46
- const updateMainCheckbox = () => {
47
- // Count the number of checked child checkboxes
48
- const checkedCount = Array.from(childCheckboxes).filter(cb => cb.checked).length;
49
- // Determine if the main checkbox should be in an indeterminate state
50
- const indeterminate = checkedCount > 0 && checkedCount < childCheckboxes.length;
51
-
52
- // Set the main checkbox states
53
- mainCheckbox.indeterminate = indeterminate;
54
- mainCheckbox.checked = checkedCount > 0;
55
-
56
- // Determine the main checkbox label based on the number of checked checkboxes
57
- const text = checkedCount === 0 ? 'Check All' : 'Uncheck All';
58
-
59
- // Determine the icon class to add and remove based on the number of checked checkboxes
60
- const iconClassToAdd = checkedCount === 0 ? 'pb_checkbox_checkmark' : 'pb_checkbox_indeterminate';
61
- const iconClassToRemove = checkedCount === 0 ? 'pb_checkbox_indeterminate' : 'pb_checkbox_checkmark';
62
-
63
- // Update main checkbox label
64
- mainCheckboxWrapper.getElementsByClassName('pb_body_kit')[0].textContent = text;
65
-
66
- // Add and remove the icon class to the main checkbox wrapper
67
- mainCheckboxWrapper.querySelector('[data-pb-checkbox-icon-span]').classList.add(iconClassToAdd);
68
- mainCheckboxWrapper.querySelector('[data-pb-checkbox-icon-span]').classList.remove(iconClassToRemove);
69
-
70
- // Toggle the visibility of the checkbox icon based on the indeterminate state
71
- mainCheckboxWrapper.getElementsByClassName("indeterminate_icon")[0].classList.toggle('hidden', !indeterminate);
72
- mainCheckboxWrapper.getElementsByClassName("check_icon")[0].classList.toggle('hidden', indeterminate);
73
- };
74
-
75
- mainCheckbox.addEventListener('change', function() {
76
- childCheckboxes.forEach(cb => cb.checked = this.checked);
77
- updateMainCheckbox();
78
- });
79
-
80
- childCheckboxes.forEach(cb => {
81
- cb.addEventListener('change', updateMainCheckbox);
82
- });
83
- });
84
- </script>
@@ -0,0 +1 @@
1
+ If you want to use indeterminate, "check/uncheck all" checkboxes, add `indeterminate_main: true` and an `id` to the main checkbox. Then, add an `indeterminate_parent` prop with the main checkbox's `id` to the children checkboxes.
@@ -1,5 +1,6 @@
1
1
  <%= pb_rails("checkbox" , props: {
2
2
  text: "Checkbox with Options",
3
+ hidden_input: true,
3
4
  input_options: {
4
5
  id: "checkbox-id",
5
6
  name: "checkbox-name",
@@ -7,6 +7,7 @@ examples:
7
7
  - checkbox_options: Checkbox w/ Options
8
8
  - checkbox_indeterminate: Indeterminate Checkbox
9
9
  - checkbox_disabled: Disabled Checkbox
10
+ - checkbox_form: Form and Hidden Input
10
11
 
11
12
  react:
12
13
  - checkbox_default: Default
@@ -0,0 +1,56 @@
1
+ import PbEnhancedElement from "../pb_enhanced_element"
2
+
3
+ const INDETERMINATE_MAIN_CHECKBOX_SELECTOR = "[data-pb-checkbox-indeterminate-main='true']"
4
+
5
+ export default class PbCheckbox extends PbEnhancedElement {
6
+ static get selector() {
7
+ return INDETERMINATE_MAIN_CHECKBOX_SELECTOR
8
+ }
9
+
10
+ connect() {
11
+ const mainCheckboxWrapper = this.element;
12
+ const mainCheckbox = mainCheckboxWrapper.querySelector('input')
13
+ const childCheckboxes = document.querySelectorAll(`[data-pb-checkbox-indeterminate-parent="${this.element.id}"] input[type="checkbox"]`);
14
+
15
+ const updateMainCheckbox = () => {
16
+ // Count the number of checked child checkboxes
17
+ const checkedCount = Array.from(childCheckboxes).filter(cb => cb.checked).length;
18
+ // Determine if the main checkbox should be in an indeterminate state
19
+ const indeterminate = checkedCount > 0 && checkedCount < childCheckboxes.length;
20
+
21
+ // Set the main checkbox states
22
+ mainCheckbox.indeterminate = indeterminate;
23
+ mainCheckbox.checked = checkedCount > 0;
24
+
25
+ // Determine the main checkbox label based on the number of checked checkboxes
26
+ const text = checkedCount === 0 ? 'Check All' : 'Uncheck All';
27
+
28
+ // Determine the icon class to add and remove based on the number of checked checkboxes
29
+ const iconClassToAdd = checkedCount === 0 ? 'pb_checkbox_checkmark' : 'pb_checkbox_indeterminate';
30
+ const iconClassToRemove = checkedCount === 0 ? 'pb_checkbox_indeterminate' : 'pb_checkbox_checkmark';
31
+
32
+ // Update main checkbox label
33
+ mainCheckboxWrapper.getElementsByClassName('pb_body_kit')[0].textContent = text;
34
+
35
+ // Add and remove the icon class to the main checkbox wrapper
36
+ mainCheckboxWrapper.querySelector('[data-pb-checkbox-icon-span]').classList.add(iconClassToAdd);
37
+ mainCheckboxWrapper.querySelector('[data-pb-checkbox-icon-span]').classList.remove(iconClassToRemove);
38
+
39
+ // Toggle the visibility of the checkbox icon based on the indeterminate state
40
+ mainCheckboxWrapper.getElementsByClassName("indeterminate_icon")[0].classList.toggle('hidden', !indeterminate);
41
+ mainCheckboxWrapper.getElementsByClassName("check_icon")[0].classList.toggle('hidden', indeterminate);
42
+ };
43
+
44
+ // Set indeterminate icon on main checkbox if initial children checkboxes are checked
45
+ updateMainCheckbox();
46
+
47
+ this.element.querySelector('input').addEventListener('change', function() {
48
+ childCheckboxes.forEach(cb => cb.checked = this.checked);
49
+ updateMainCheckbox();
50
+ });
51
+
52
+ childCheckboxes.forEach(cb => {
53
+ cb.addEventListener('change', updateMainCheckbox);
54
+ });
55
+ }
56
+ }