playbook_ui 14.19.0 → 14.20.0.pre.alpha.revert4453PBNTR933reactdraggablebugdragbtwnexamples7854

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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +11 -1
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/TableActionBar.tsx +175 -16
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +56 -25
  5. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +23 -13
  6. data/app/pb_kits/playbook/pb_advanced_table/Utilities/VisibilityTree.ts +47 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +6 -10
  8. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +7 -2
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility.jsx +57 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility.md +4 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_custom.jsx +62 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_custom.md +1 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_multi.jsx +82 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_multi.md +1 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_with_state.jsx +66 -0
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_with_state.md +3 -0
  17. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +4 -0
  18. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +5 -1
  19. data/app/pb_kits/playbook/pb_advanced_table/scss_partials/advanced_table_sticky_mixin.scss +1 -0
  20. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +17 -58
  21. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +1 -1
  22. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +77 -19
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_rails.html.erb +31 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_rails.md +5 -0
  25. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select.jsx +56 -0
  26. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select.md +3 -0
  27. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_display.jsx +58 -0
  28. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_display.md +3 -0
  29. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_display_rails.html.erb +20 -0
  30. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_display_rails.md +1 -0
  31. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_rails.html.erb +19 -0
  32. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_rails.md +3 -0
  33. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_autocomplete.html.erb +20 -0
  34. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_autocomplete.jsx +57 -0
  35. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_autocomplete.md +1 -0
  36. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_custom_options.html.erb +50 -0
  37. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_custom_options.jsx +105 -0
  38. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_default.html.erb +22 -0
  39. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_default.jsx +67 -0
  40. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.jsx +11 -0
  41. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.md +1 -1
  42. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.html.erb +33 -2
  43. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.md +3 -1
  44. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +11 -1
  45. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +5 -0
  46. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +3 -3
  47. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +16 -2
  48. data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +34 -13
  49. data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +3 -1
  50. data/app/pb_kits/playbook/pb_dropdown/hooks/useHandleOnKeydown.tsx +0 -6
  51. data/app/pb_kits/playbook/pb_dropdown/index.js +336 -30
  52. data/app/pb_kits/playbook/pb_dropdown/keyboard_accessibility.js +39 -12
  53. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +2 -2
  54. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +16 -12
  55. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +79 -13
  56. data/app/pb_kits/playbook/pb_dropdown/subcomponents/MultiSelectTriggerDisplay.tsx +58 -0
  57. data/app/pb_kits/playbook/pb_file_upload/_file_upload.scss +13 -0
  58. data/app/pb_kits/playbook/pb_file_upload/_file_upload.tsx +11 -1
  59. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_error.html.erb +1 -0
  60. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_error.jsx +41 -0
  61. data/app/pb_kits/playbook/pb_file_upload/docs/example.yml +2 -0
  62. data/app/pb_kits/playbook/pb_file_upload/docs/index.js +1 -0
  63. data/app/pb_kits/playbook/pb_file_upload/file_upload.html.erb +1 -0
  64. data/app/pb_kits/playbook/pb_file_upload/file_upload.rb +7 -1
  65. data/app/pb_kits/playbook/pb_file_upload/fileupload.test.js +18 -0
  66. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +1 -0
  67. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -0
  68. data/app/pb_kits/playbook/pb_form_group/_error_state_mixin.scss +2 -2
  69. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +19 -12
  70. data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.tsx +13 -7
  71. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +2 -2
  72. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.html.erb +11 -11
  73. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.jsx +11 -11
  74. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.html.erb +11 -11
  75. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +11 -11
  76. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.html.erb +11 -11
  77. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.jsx +11 -11
  78. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options.html.erb +11 -11
  79. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options.jsx +11 -11
  80. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_default.html.erb +11 -11
  81. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_default.jsx +11 -11
  82. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_parent.html.erb +11 -11
  83. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_parent.jsx +11 -11
  84. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_parent_default.html.erb +11 -11
  85. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_parent_default.jsx +11 -11
  86. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.html.erb +11 -11
  87. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.jsx +11 -11
  88. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +11 -11
  89. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +11 -11
  90. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_react_hook.jsx +11 -11
  91. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb +11 -11
  92. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.html.erb +11 -11
  93. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.jsx +11 -11
  94. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_selected_ids.html.erb +11 -11
  95. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_selected_ids.md +2 -0
  96. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_selected_ids_react.jsx +11 -11
  97. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_selected_ids_react.md +3 -1
  98. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_single.html.erb +22 -22
  99. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_single.jsx +22 -22
  100. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_single_children_only.html.erb +22 -22
  101. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_single_children_only.jsx +22 -22
  102. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx +11 -11
  103. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx +11 -11
  104. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_form.html.erb +11 -11
  105. data/app/pb_kits/playbook/pb_person/_person.tsx +12 -2
  106. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +9 -9
  107. data/app/pb_kits/playbook/pb_section_separator/_section_separator.tsx +2 -2
  108. data/app/pb_kits/playbook/pb_text_input/_text_input.scss +4 -2
  109. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +73 -3
  110. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_preserve_input.jsx +23 -0
  111. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_preserve_input.md +1 -0
  112. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +1 -0
  113. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -0
  114. data/dist/chunks/_typeahead-C-CI5Vgw.js +22 -0
  115. data/dist/chunks/_weekday_stacked-BCiM3zWM.js +45 -0
  116. data/dist/chunks/lazysizes-B7xYodB-.js +1 -0
  117. data/dist/chunks/lib-D5R1BjUn.js +29 -0
  118. data/dist/chunks/{pb_form_validation-BioH7DWv.js → pb_form_validation-BZ2AVAi_.js} +1 -1
  119. data/dist/chunks/vendor.js +1 -1
  120. data/dist/playbook-doc.js +2 -2
  121. data/dist/playbook-rails-react-bindings.js +1 -1
  122. data/dist/playbook-rails.js +1 -1
  123. data/dist/playbook.css +1 -1
  124. data/lib/playbook/kit_base.rb +3 -3
  125. data/lib/playbook/version.rb +2 -2
  126. metadata +38 -8
  127. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default.html.erb +0 -10
  128. data/dist/chunks/_typeahead-D62OcwsT.js +0 -22
  129. data/dist/chunks/_weekday_stacked-Ceh9N0ow.js +0 -45
  130. data/dist/chunks/lazysizes-DHz07jlL.js +0 -1
  131. data/dist/chunks/lib-CeKZrPmu.js +0 -29
@@ -6,7 +6,7 @@
6
6
  <input
7
7
  data-default-value="<%= input_default_value %>"
8
8
  id="dropdown-selected-option"
9
- name="<%= object.name %>"
9
+ name="<%= object.name %><%= '[]' if object.multi_select %>"
10
10
  style="display: none"
11
11
  <%= object.required ? "required" : ""%>
12
12
  />
@@ -14,8 +14,8 @@
14
14
  <%= content.presence %>
15
15
  <%= pb_rails("body", props: { status: "negative", text: object.error }) %>
16
16
  <% else %>
17
- <%= pb_rails("dropdown/dropdown_trigger", props:{autocomplete: object.autocomplete}) %>
18
- <%= pb_rails("dropdown/dropdown_container", props: {searchbar: object.searchbar}) do %>
17
+ <%= pb_rails("dropdown/dropdown_trigger", props:{ autocomplete: object.autocomplete, multi_select:object.multi_select }) %>
18
+ <%= pb_rails("dropdown/dropdown_container", props: { searchbar: object.searchbar }) do %>
19
19
  <% if options_with_blank.present? %>
20
20
  <% options_with_blank.each do |option| %>
21
21
  <%= pb_rails("dropdown/dropdown_option", props: {option: option}) %>
@@ -22,9 +22,17 @@ module Playbook
22
22
  default: false
23
23
  prop :searchbar, type: Playbook::Props::Boolean,
24
24
  default: false
25
+ prop :multi_select, type: Playbook::Props::Boolean,
26
+ default: false
27
+ prop :form_pill_props, type: Playbook::Props::HashProp,
28
+ default: {}
25
29
 
26
30
  def data
27
- Hash(prop(:data)).merge(pb_dropdown: true)
31
+ Hash(prop(:data)).merge(
32
+ pb_dropdown: true,
33
+ pb_dropdown_multi_select: multi_select,
34
+ form_pill_props: form_pill_props.to_json
35
+ )
28
36
  end
29
37
 
30
38
  def classname
@@ -38,7 +46,13 @@ module Playbook
38
46
  end
39
47
 
40
48
  def input_default_value
41
- default_value.present? ? default_value.transform_keys(&:to_s)["id"] : ""
49
+ return "" unless default_value.present?
50
+
51
+ if multi_select
52
+ default_value.map { |v| v.transform_keys(&:to_s)["id"] }.join(",")
53
+ else
54
+ default_value.transform_keys(&:to_s)["id"]
55
+ end
42
56
  end
43
57
 
44
58
  def separators_class
@@ -14,7 +14,7 @@
14
14
  padding_y:"xs",
15
15
  html_options: {tabindex:"0"}
16
16
  }) do %>
17
- <%= pb_rails("flex/flex_item") do %>
17
+ <%= pb_rails("flex/flex_item", props: { fixed_size: object.multi_select ? "85%" : "" }) do %>
18
18
  <%= pb_rails("flex", props: {align: "center"}) do %>
19
19
  <% if object.custom_display.present? %>
20
20
  <%= pb_rails("flex", props: {align: "center"}) do %>
@@ -24,23 +24,44 @@
24
24
  <%= pb_rails("body", props: {text: object.default_display_placeholder, id: "dropdown_trigger_display"}) %>
25
25
  <% end %>
26
26
  <% else %>
27
- <% if object.autocomplete %>
28
- <input
29
- data-dropdown-autocomplete
30
- class="dropdown_input"
31
- type="text"
32
- placeholder="<%= object.placeholder || 'Select…' %>"
33
- autocomplete="off"
34
- />
27
+ <% if object.multi_select %>
28
+ <%= pb_rails("flex", props: { align: "center", wrap: true }) do %>
29
+ <%= pb_rails("flex", props: { id:"dropdown_pills_wrapper", wrap: true }) do %>
30
+ <% end %>
31
+ <% if object.autocomplete %>
32
+ <input
33
+ data-dropdown-autocomplete
34
+ class="dropdown_input"
35
+ type="text"
36
+ placeholder="<%= object.placeholder || 'Select…' %>"
37
+ autocomplete="off"
38
+ />
39
+ <% else %>
40
+ <%= pb_rails("body", props: {text: object.default_display_placeholder, id: "dropdown_trigger_display_multi_select"}) %>
41
+ <% end %>
42
+ <% end %>
35
43
  <% else %>
36
- <%= pb_rails("body", props: {text: object.default_display_placeholder, id: "dropdown_trigger_display"}) %>
44
+ <% if object.autocomplete %>
45
+ <input
46
+ data-dropdown-autocomplete
47
+ class="dropdown_input"
48
+ type="text"
49
+ placeholder="<%= object.placeholder || 'Select…' %>"
50
+ autocomplete="off"
51
+ />
52
+ <% else %>
53
+ <%= pb_rails("body", props: {text: object.default_display_placeholder, id: "dropdown_trigger_display"}) %>
54
+ <% end %>
37
55
  <% end %>
38
56
  <% end %>
39
57
  <% end %>
40
58
  <% end %>
41
- <%= pb_rails("body", props: {display: "flex"}) do %>
42
- <%= pb_rails("icon", props: {icon: "chevron-down", cursor: "pointer", size:"sm", id: "dropdown_open_icon"}) %>
43
- <%= pb_rails("icon", props: {icon: "chevron-up", cursor: "pointer", size:"sm", id: "dropdown_close_icon"}) %>
59
+ <%= pb_rails("flex/flex_item") do %>
60
+ <%= pb_rails("body", props: {display: "flex", align_items:"center" }) do %>
61
+ <%= pb_rails("icon", props: {icon: "times", cursor: "pointer", size:"sm", id: "dropdown_clear_icon", padding_right:"xs" }) %>
62
+ <%= pb_rails("icon", props: {icon: "chevron-down", cursor: "pointer", size:"sm", id: "dropdown_open_icon"}) %>
63
+ <%= pb_rails("icon", props: {icon: "chevron-up", cursor: "pointer", size:"sm", id: "dropdown_close_icon"}) %>
64
+ <% end %>
44
65
  <% end %>
45
66
  <% end %>
46
67
  <% end %>
@@ -11,6 +11,8 @@ module Playbook
11
11
  prop :custom_display
12
12
  prop :autocomplete, type: Playbook::Props::Boolean,
13
13
  default: false
14
+ prop :multi_select, type: Playbook::Props::Boolean,
15
+ default: false
14
16
 
15
17
  def data
16
18
  Hash(prop(:data)).merge(dropdown_trigger: true, dropdown_placeholder: default_display_placeholder)
@@ -25,7 +27,7 @@ module Playbook
25
27
  end
26
28
 
27
29
  def trigger_wrapper_classes
28
- generate_classname("dropdown_trigger_wrapper", ("select_only" unless autocomplete))
30
+ generate_classname("dropdown_trigger_wrapper", ("select_only" unless autocomplete || multi_select))
29
31
  end
30
32
  end
31
33
  end
@@ -22,12 +22,6 @@ const {
22
22
  }
23
23
 
24
24
  switch (e.key) {
25
- case "Backspace":
26
- case "Delete":
27
- if (autocomplete) {
28
- handleBackspace();
29
- }
30
- break;
31
25
  case "ArrowDown": {
32
26
  e.preventDefault();
33
27
  setIsDropDownClosed(false);
@@ -13,6 +13,7 @@ const DROPDOWN_PLACEHOLDER = "[data-dropdown-placeholder]";
13
13
  const DROPDOWN_INPUT = "#dropdown-selected-option";
14
14
  const SEARCH_INPUT_SELECTOR = "[data-dropdown-autocomplete]";
15
15
  const SEARCH_BAR_SELECTOR = "[data-dropdown-search]";
16
+ const CLEAR_ICON_SELECTOR = "#dropdown_clear_icon";
16
17
 
17
18
  export default class PbDropdown extends PbEnhancedElement {
18
19
  static get selector() {
@@ -20,11 +21,18 @@ export default class PbDropdown extends PbEnhancedElement {
20
21
  }
21
22
 
22
23
  get target() {
23
- return this.element.parentNode.querySelector(CONTAINER_SELECTOR);
24
+ return this.element.querySelector(CONTAINER_SELECTOR);
24
25
  }
25
26
 
27
+ selectedOptions = new Set();
28
+ clearBtn = null;
29
+
26
30
  connect() {
27
31
  this.keyboardHandler = new PbDropdownKeyboard(this);
32
+ this.isMultiSelect = this.element.dataset.pbDropdownMultiSelect === "true";
33
+ this.formPillProps = this.element.dataset.formPillProps
34
+ ? JSON.parse(this.element.dataset.formPillProps)
35
+ : {};
28
36
  this.setDefaultValue();
29
37
  this.bindEventListeners();
30
38
  this.bindSearchInput();
@@ -32,6 +40,26 @@ export default class PbDropdown extends PbEnhancedElement {
32
40
  this.handleFormValidation();
33
41
  this.handleFormReset();
34
42
  this.bindSearchBar();
43
+ this.updatePills();
44
+
45
+ this.clearBtn = this.element.querySelector(CLEAR_ICON_SELECTOR);
46
+ if (this.clearBtn) {
47
+ this.clearBtn.style.display = "none";
48
+ this.clearBtn.addEventListener("click", (e) => {
49
+ e.stopPropagation();
50
+ this.clearSelection();
51
+ });
52
+ }
53
+ this.updateClearButton();
54
+ }
55
+
56
+ updateClearButton() {
57
+ if (!this.clearBtn) return;
58
+ const hasSelection = this.isMultiSelect
59
+ ? this.selectedOptions.size > 0
60
+ : Boolean(this.element.querySelector(DROPDOWN_INPUT).value);
61
+
62
+ this.clearBtn.style.display = hasSelection ? "" : "none";
35
63
  }
36
64
 
37
65
  bindEventListeners() {
@@ -52,7 +80,7 @@ export default class PbDropdown extends PbEnhancedElement {
52
80
  bindSearchBar() {
53
81
  this.searchBar = this.element.querySelector(SEARCH_BAR_SELECTOR);
54
82
  if (!this.searchBar) return;
55
-
83
+
56
84
  this.searchBar.addEventListener("input", (e) =>
57
85
  this.handleSearch(e.target.value)
58
86
  );
@@ -73,11 +101,28 @@ export default class PbDropdown extends PbEnhancedElement {
73
101
  );
74
102
  }
75
103
 
104
+ adjustDropdownHeight() {
105
+ if (this.target.classList.contains("open")) {
106
+ const el = this.target;
107
+ el.style.height = "auto";
108
+ requestAnimationFrame(() => {
109
+ const newHeight = el.scrollHeight + "px";
110
+ el.offsetHeight; // force reflow
111
+ el.style.height = newHeight;
112
+ });
113
+ }
114
+ }
115
+
76
116
  handleSearch(term = "") {
77
117
  const lcTerm = term.toLowerCase();
78
118
  this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
79
- const label = JSON.parse(opt.dataset.dropdownOptionLabel).label
80
- .toString()
119
+ //make it so that if the option is selected, it will not show up in the search results
120
+ if (this.isMultiSelect && this.selectedOptions.has(opt.dataset.dropdownOptionLabel)) {
121
+ opt.style.display = "none";
122
+ return;
123
+ }
124
+ const label = JSON.parse(opt.dataset.dropdownOptionLabel)
125
+ .label.toString()
81
126
  .toLowerCase();
82
127
 
83
128
  // hide or show option
@@ -85,15 +130,7 @@ export default class PbDropdown extends PbEnhancedElement {
85
130
  opt.style.display = match ? "" : "none";
86
131
  });
87
132
 
88
- if (this.target.classList.contains("open")) {
89
- const el = this.target;
90
- el.style.height = "auto";
91
- requestAnimationFrame(() => {
92
- const newHeight = el.scrollHeight + "px";
93
- el.offsetHeight; // force reflow
94
- el.style.height = newHeight;
95
- });
96
- }
133
+ this.adjustDropdownHeight();
97
134
  }
98
135
 
99
136
  handleOptionClick(event) {
@@ -102,10 +139,26 @@ export default class PbDropdown extends PbEnhancedElement {
102
139
 
103
140
  if (option) {
104
141
  const value = option.dataset.dropdownOptionLabel;
105
- hiddenInput.value = JSON.parse(value).id;
106
- this.clearFormValidation(hiddenInput);
142
+ if (this.isMultiSelect) {
143
+ const alreadySelected = this.selectedOptions.has(value);
144
+ if (alreadySelected) {
145
+ this.selectedOptions.delete(value);
146
+ } else {
147
+ this.selectedOptions.add(value);
148
+ }
149
+ this.updatePills();
150
+ this.syncHiddenInputs();
151
+ if (this.searchInput && this.isMultiSelect) {
152
+ this.searchInput.value = "";
153
+ this.handleBackspaceClear();
154
+ }
155
+ } else {
156
+ hiddenInput.value = JSON.parse(value).id;
157
+ }
107
158
 
159
+ this.clearFormValidation(hiddenInput);
108
160
  this.onOptionSelected(value, option);
161
+ this.updateClearButton();
109
162
  }
110
163
  }
111
164
 
@@ -137,23 +190,54 @@ export default class PbDropdown extends PbEnhancedElement {
137
190
  }
138
191
  }
139
192
 
193
+ emitSelectionChange() {
194
+ let detail;
195
+
196
+ if (this.isMultiSelect) {
197
+ detail = Array.from(this.selectedOptions).map(JSON.parse);
198
+ } else {
199
+ const hiddenInput = this.element.querySelector(DROPDOWN_INPUT);
200
+ detail = hiddenInput.value
201
+ ? JSON.parse(
202
+ this.element.querySelector(
203
+ OPTION_SELECTOR +
204
+ `[data-dropdown-option-label*='"id":"${hiddenInput.value}"']`
205
+ ).dataset.dropdownOptionLabel
206
+ )
207
+ : null;
208
+ }
209
+ this.element.setAttribute("data-option-selected", JSON.stringify(detail));
210
+ this.element.dispatchEvent(
211
+ new CustomEvent("pb:dropdown:selected", {
212
+ detail,
213
+ bubbles: true,
214
+ })
215
+ );
216
+ }
217
+
140
218
  onOptionSelected(value, selectedOption) {
141
219
  const triggerElement = this.element.querySelector(DROPDOWN_TRIGGER_DISPLAY);
142
220
  const customDisplayElement = this.element.querySelector(
143
221
  "#dropdown_trigger_custom_display"
144
222
  );
223
+
145
224
  if (triggerElement) {
146
- const selectedLabel = JSON.parse(value).label;
147
- triggerElement.textContent = selectedLabel;
225
+ if (!this.isMultiSelect) {
226
+ const selectedLabel = JSON.parse(value).label;
227
+ triggerElement.textContent = selectedLabel;
228
+ this.emitSelectionChange();
229
+ }
148
230
  if (customDisplayElement) {
231
+ triggerElement.textContent = "";
149
232
  customDisplayElement.style.display = "block";
150
233
  customDisplayElement.style.paddingRight = "8px";
151
234
  }
152
235
  }
153
236
 
154
237
  const autocompleteInput = this.element.querySelector(SEARCH_INPUT_SELECTOR);
155
- if (autocompleteInput){
238
+ if (autocompleteInput && !this.isMultiSelect) {
156
239
  autocompleteInput.value = JSON.parse(value).label;
240
+ this.emitSelectionChange();
157
241
  }
158
242
 
159
243
  const customTrigger = this.element.querySelector(CUSTOM_DISPLAY_SELECTOR);
@@ -165,10 +249,24 @@ export default class PbDropdown extends PbEnhancedElement {
165
249
  }
166
250
 
167
251
  const options = this.element.querySelectorAll(OPTION_SELECTOR);
168
- options.forEach((option) => {
169
- option.classList.remove("pb_dropdown_option_selected");
170
- });
171
- selectedOption.classList.add("pb_dropdown_option_selected");
252
+ if (this.isMultiSelect) {
253
+ this.emitSelectionChange();
254
+ Array.from(this.selectedOptions).map((option) => {
255
+ if (
256
+ JSON.parse(option).id ===
257
+ JSON.parse(selectedOption.dataset.dropdownOptionLabel).id
258
+ ) {
259
+ selectedOption.style.display = "none";
260
+ this.adjustDropdownHeight();
261
+ }
262
+ });
263
+ } else {
264
+ options.forEach((option) => {
265
+ option.classList.remove("pb_dropdown_option_selected");
266
+ });
267
+ selectedOption.classList.add("pb_dropdown_option_selected");
268
+ }
269
+ this.updateClearButton();
172
270
  }
173
271
 
174
272
  showElement(elem) {
@@ -242,21 +340,66 @@ export default class PbDropdown extends PbEnhancedElement {
242
340
  errorLabelElement.remove();
243
341
  }
244
342
  }
343
+ if (this.isMultiSelect) {
344
+ if (this.selectedOptions.size > 0) {
345
+ const dropdownWrapperElement = input.closest(".dropdown_wrapper");
346
+ dropdownWrapperElement.classList.remove("error");
347
+ const errorLabelElement = dropdownWrapperElement.querySelector(
348
+ ".pb_body_kit_negative"
349
+ );
350
+ if (errorLabelElement) {
351
+ errorLabelElement.remove();
352
+ }
353
+ }
354
+ }
245
355
  }
246
356
 
247
357
  setDefaultValue() {
248
358
  const hiddenInput = this.element.querySelector(DROPDOWN_INPUT);
249
- const options = this.element.querySelectorAll(OPTION_SELECTOR);
250
-
359
+ const optionEls = Array.from(
360
+ this.element.querySelectorAll(OPTION_SELECTOR)
361
+ );
251
362
  const defaultValue = hiddenInput.dataset.defaultValue || "";
252
- hiddenInput.value = defaultValue;
363
+ if (!defaultValue) return;
364
+
365
+ if (this.isMultiSelect) {
366
+ const ids = defaultValue.split(",");
367
+ ids.forEach((id) => {
368
+ const selectedOption = optionEls.find((opt) => {
369
+ try {
370
+ return JSON.parse(opt.dataset.dropdownOptionLabel).id === id;
371
+ } catch {
372
+ return false;
373
+ }
374
+ });
375
+ if (!selectedOption) {
376
+ console.warn(`Dropdown default ID ${id} not found`);
377
+ return;
378
+ }
253
379
 
254
- if (defaultValue) {
255
- const selectedOption = Array.from(options).find((option) => {
256
- return (
257
- JSON.parse(option.dataset.dropdownOptionLabel).id === defaultValue
258
- );
380
+ const raw = selectedOption.dataset.dropdownOptionLabel;
381
+ this.selectedOptions.add(raw);
382
+
383
+ selectedOption.style.display = "none";
384
+ });
385
+
386
+ this.updatePills();
387
+ this.updateClearButton();
388
+ this.adjustDropdownHeight();
389
+ this.syncHiddenInputs();
390
+ } else {
391
+ hiddenInput.value = defaultValue;
392
+ const selectedOption = optionEls.find((opt) => {
393
+ try {
394
+ return (
395
+ JSON.parse(opt.dataset.dropdownOptionLabel).id === defaultValue
396
+ );
397
+ } catch {
398
+ return false;
399
+ }
259
400
  });
401
+ if (!selectedOption) return;
402
+
260
403
  selectedOption.classList.add("pb_dropdown_option_selected");
261
404
  this.setTriggerElementText(
262
405
  JSON.parse(selectedOption.dataset.dropdownOptionLabel).label
@@ -279,12 +422,32 @@ export default class PbDropdown extends PbEnhancedElement {
279
422
  const options = this.element.querySelectorAll(OPTION_SELECTOR);
280
423
  options.forEach((option) => {
281
424
  option.classList.remove("pb_dropdown_option_selected");
425
+ option.style.display = "";
282
426
  });
283
427
 
284
428
  hiddenInput.value = "";
285
429
 
286
430
  const defaultPlaceholder = this.element.querySelector(DROPDOWN_PLACEHOLDER);
287
431
  this.setTriggerElementText(defaultPlaceholder.dataset.dropdownPlaceholder);
432
+
433
+ if (this.searchInput) {
434
+ this.searchInput.value = "";
435
+ if (this.target.classList.contains("open")) {
436
+ const el = this.target;
437
+ el.style.height = "auto";
438
+ requestAnimationFrame(() => {
439
+ const newHeight = el.scrollHeight + "px";
440
+ el.offsetHeight; // force reflow
441
+ el.style.height = newHeight;
442
+ });
443
+ }
444
+ }
445
+ if (this.isMultiSelect) {
446
+ this.selectedOptions.clear();
447
+ this.updatePills();
448
+ this.updateClearButton();
449
+ this.syncHiddenInputs();
450
+ }
288
451
  }
289
452
 
290
453
  setTriggerElementText(text) {
@@ -293,4 +456,147 @@ export default class PbDropdown extends PbEnhancedElement {
293
456
  triggerElement.textContent = text;
294
457
  }
295
458
  }
459
+
460
+ updatePills() {
461
+ if (!this.isMultiSelect) return;
462
+
463
+ const wrapper = this.element.querySelector("#dropdown_pills_wrapper");
464
+ const placeholder = this.element.querySelector(
465
+ "#dropdown_trigger_display_multi_select"
466
+ );
467
+ if (!wrapper) return;
468
+
469
+ wrapper.innerHTML = "";
470
+ // Show or hide the placeholder based on selected options
471
+ if (placeholder) {
472
+ if (this.selectedOptions.size > 0) {
473
+ placeholder.style.display = "none";
474
+ } else {
475
+ placeholder.style.display = "";
476
+ }
477
+ }
478
+
479
+ Array.from(this.selectedOptions).map((option) => {
480
+ // Create a form pill for each selected option
481
+ const pill = document.createElement("div");
482
+ const color = this.formPillProps.color || "primary";
483
+ pill.classList.add(`pb_form_pill_kit_${color}`, "mr_xs");
484
+ if (this.formPillProps.size === "small") {
485
+ pill.classList.add("small");
486
+ }
487
+ pill.tabIndex = 0;
488
+ pill.dataset.pillId = JSON.parse(option).id;
489
+ const innerDiv = document.createElement("h3");
490
+ innerDiv.className = "pb_title_kit_size_4 pb_form_pill_text";
491
+ innerDiv.textContent = JSON.parse(option).label;
492
+ pill.appendChild(innerDiv);
493
+
494
+ const closeIcon = document.createElement("div");
495
+ closeIcon.className = "pb_form_pill_close";
496
+ closeIcon.innerHTML = `<svg class="pb_custom_icon svg-inline--fa svg_${
497
+ this.formPillProps.size === "small" ? "xs" : "sm"
498
+ } svg_fw" xmlns="http://www.w3.org/2000/svg" width="auto" height="auto" viewBox="0 0 31 25"><path fill="currentColor" d="M23.0762 6.77734L17.4512 12.4023L23.0293 17.9805C23.498 18.4023 23.498 19.1055 23.0293 19.5273C22.6074 19.9961 21.9043 19.9961 21.4824 19.5273L15.8574 13.9492L10.2793 19.5273C9.85742 19.9961 9.1543 19.9961 8.73242 19.5273C8.26367 19.1055 8.26367 18.4023 8.73242 17.9336L14.3105 12.3555L8.73242 6.77734C8.26367 6.35547 8.26367 5.65234 8.73242 5.18359C9.1543 4.76172 9.85742 4.76172 10.3262 5.18359L15.9043 10.8086L21.4824 5.23047C21.9043 4.76172 22.6074 4.76172 23.0762 5.23047C23.498 5.65234 23.498 6.35547 23.0762 6.77734Z"/></svg>`;
499
+ pill.appendChild(closeIcon);
500
+
501
+ closeIcon.addEventListener("click", (e) => {
502
+ e.stopPropagation();
503
+ const id = pill.dataset.pillId;
504
+ this.selectedOptions.delete(option);
505
+
506
+ const optEl = this.element.querySelector(
507
+ `${OPTION_SELECTOR}[data-dropdown-option-label*='"id":${JSON.stringify(
508
+ id
509
+ )}']`
510
+ );
511
+ if (optEl) {
512
+ optEl.style.display = "";
513
+ if (this.target.classList.contains("open")) {
514
+ this.showElement(this.target);
515
+ }
516
+ }
517
+
518
+ this.updatePills();
519
+ this.updateClearButton();
520
+ this.emitSelectionChange();
521
+ this.syncHiddenInputs();
522
+ });
523
+ wrapper.appendChild(pill);
524
+ });
525
+ }
526
+
527
+ clearSelection() {
528
+ if (this.isMultiSelect) {
529
+ this.selectedOptions.clear();
530
+ this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
531
+ opt.style.display = "";
532
+ });
533
+ if (this.target.classList.contains("open")) {
534
+ this.showElement(this.target);
535
+ }
536
+ }
537
+ const customDisplay = this.element.querySelector(
538
+ "#dropdown_trigger_custom_display"
539
+ );
540
+ if (customDisplay) {
541
+ customDisplay.style.display = "none";
542
+ }
543
+ this.resetDropdownValue();
544
+ this.updatePills();
545
+ this.updateClearButton();
546
+ this.syncHiddenInputs();
547
+ this.emitSelectionChange();
548
+ }
549
+
550
+ syncHiddenInputs() {
551
+ if (!this.isMultiSelect) return;
552
+ this.element
553
+ .querySelectorAll('input[data-generated="true"]')
554
+ .forEach((n) => n.remove());
555
+
556
+ const baseInput = this.element.querySelector(DROPDOWN_INPUT);
557
+ if (!baseInput) return;
558
+ // for multi_select, for each selectedOption, create a hidden input
559
+ const name = baseInput.getAttribute("name");
560
+ this.selectedOptions.forEach((raw) => {
561
+ const id = JSON.parse(raw).id;
562
+ const inp = document.createElement("input");
563
+ inp.type = "hidden";
564
+ inp.name = name;
565
+ inp.value = id;
566
+ inp.dataset.generated = "true";
567
+ baseInput.insertAdjacentElement("afterend", inp);
568
+ });
569
+ baseInput.value = "";
570
+ }
571
+
572
+ handleBackspaceClear() {
573
+ if (!this.isMultiSelect) {
574
+ this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
575
+ opt.classList.remove("pb_dropdown_option_selected");
576
+ opt.style.display = "";
577
+ this.adjustDropdownHeight();
578
+ });
579
+
580
+ const hiddenInput = this.element.querySelector(DROPDOWN_INPUT);
581
+ if (hiddenInput) hiddenInput.value = "";
582
+
583
+ const placeholder = this.element.querySelector(DROPDOWN_PLACEHOLDER);
584
+ if (placeholder)
585
+ this.setTriggerElementText(placeholder.dataset.dropdownPlaceholder);
586
+ }
587
+ if (this.isMultiSelect) {
588
+ this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
589
+ const optValue = opt.dataset.dropdownOptionLabel;
590
+ if (
591
+ this.selectedOptions.size > 0 &&
592
+ this.selectedOptions.has(optValue)
593
+ ) {
594
+ opt.style.display = "none";
595
+ } else {
596
+ opt.style.display = "";
597
+ }
598
+ this.adjustDropdownHeight();
599
+ });
600
+ }
601
+ }
296
602
  }