playbook_ui 16.3.0.pre.alpha.play284914770 → 16.3.0.pre.alpha.play285814889

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_button/_button_mixins.scss +6 -1
  3. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.html.erb +1 -1
  4. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.jsx +6 -3
  5. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_default.html.erb +18 -9
  6. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_default.jsx +24 -5
  7. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.html.erb +3 -3
  8. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.jsx +6 -3
  9. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.html.erb +3 -3
  10. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.jsx +6 -3
  11. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +3 -0
  12. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +1 -0
  13. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options_rails.html.erb +16 -0
  14. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options_rails.md +1 -0
  15. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +1 -1
  16. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +5 -4
  17. data/app/pb_kits/playbook/pb_dropdown/index.js +27 -170
  18. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +19 -14
  19. data/app/pb_kits/playbook/pb_multi_level_select/_helper_functions.tsx +1 -1
  20. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +24 -15
  21. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +35 -134
  22. data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_editor.tsx +51 -0
  23. data/app/pb_kits/playbook/pb_rich_text_editor/_trix_editor.tsx +206 -0
  24. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.jsx +56 -0
  25. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.md +1 -0
  26. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +13 -21
  27. data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +0 -10
  28. data/app/pb_kits/playbook/pb_rich_text_editor/inlineFocus.ts +5 -4
  29. data/app/pb_kits/playbook/pb_textarea/_textarea.scss +4 -1
  30. data/dist/chunks/{_pb_line_graph-BGY7jEks.js → _pb_line_graph-BI5wY8Wj.js} +1 -1
  31. data/dist/chunks/_typeahead-8CvXJGlb.js +1 -0
  32. data/dist/chunks/{globalProps-CK2YuA9O.js → globalProps-Bn1WUHLp.js} +1 -1
  33. data/dist/chunks/{lib-DspaUdlc.js → lib-qwWYiGtH.js} +1 -1
  34. data/dist/chunks/vendor.js +4 -4
  35. data/dist/menu.yml +1 -0
  36. data/dist/playbook-rails-react-bindings.js +1 -1
  37. data/dist/playbook-rails.js +1 -1
  38. data/dist/playbook.css +1 -1
  39. data/lib/playbook/version.rb +1 -1
  40. metadata +11 -32
  41. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_custom_event_type.html.erb +0 -224
  42. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_custom_event_type.md +0 -7
  43. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_attributes.html.erb +0 -5
  44. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_attributes.jsx +0 -15
  45. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.html.erb +0 -1
  46. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_focus.html.erb +0 -3
  47. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_focus.jsx +0 -17
  48. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.html.erb +0 -6
  49. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.jsx +0 -16
  50. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.jsx +0 -28
  51. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.md +0 -1
  52. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_preview.html.erb +0 -35
  53. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_preview.jsx +0 -45
  54. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.html.erb +0 -10
  55. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.jsx +0 -22
  56. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.md +0 -3
  57. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_simple.html.erb +0 -1
  58. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_simple.jsx +0 -13
  59. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_sticky.html.erb +0 -1
  60. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_sticky.jsx +0 -15
  61. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_templates.html.erb +0 -115
  62. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_templates.jsx +0 -42
  63. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.html.erb +0 -4
  64. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.jsx +0 -14
  65. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +0 -5
  66. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +0 -63
  67. data/dist/chunks/_typeahead-tG1K5JPP.js +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b00abe84561a81194a57761780d54f66b81d7242fbd9a5375ceb53f68168b31
4
- data.tar.gz: 8915da7876e7d6e0541ac17f82fc7b2dedba1cd78f41b295b2a5162d19b74e8a
3
+ metadata.gz: 02d49483b3956da77f73aacb7718f16ebd2b6b8782c44e80815f22be2ffcde4e
4
+ data.tar.gz: 693802d5534038fe8a607937538cd3b9370627d0613a09353f9c14b4ed6bb721
5
5
  SHA512:
6
- metadata.gz: 5bb0b7509dcbede294e117a06019480ab186eef8306eb8bdf08210b88535739526e0b96fa696cc36e3c05c4cfa03eed520b0e177496940b9dbb6fe6ca6ebf99a
7
- data.tar.gz: e56a8eef5c977851fda3ccaa58058a577e352bb051a39bd933e38267f950fd7a611f5bb090d5243dfbf1c8d6287a7e6909a9f3f93489d83228dd765ed534e18c
6
+ metadata.gz: 9878ce5bb1f4e16cd4dad4389e981eacc4ce7aab095d9c39fa0f3b9411768b0732ff6e96b12d35eaf67d0f34f0fdf85e71b5b063d406c3e44bd9f1d6d2389625
7
+ data.tar.gz: 4bc358a3ec5ede3933e21e49627d59353a776ed6ff602da16d0c2c509df8297486c56c7b10470504f37040d61e04ca8635e597181cab5eb9c9386635ebe3781b
@@ -57,7 +57,7 @@ $pb_button_border_width: 0px;
57
57
  }
58
58
 
59
59
  .loading-icon {
60
- position: absolute;
60
+ position: static;
61
61
  display: none;
62
62
  }
63
63
  .pb_button_content {
@@ -158,10 +158,15 @@ $pb_button_border_width: 0px;
158
158
  // Loading =====================
159
159
  @mixin pb_button_loading($loading: false) {
160
160
  @if $loading == true {
161
+ display: inline-grid;
162
+ place-items: center;
163
+
161
164
  .loading-icon {
165
+ grid-area: 1 / 1;
162
166
  display: block;
163
167
  }
164
168
  .pb_button_content {
169
+ grid-area: 1 / 1;
165
170
  visibility: hidden;
166
171
  }
167
172
  }
@@ -28,7 +28,7 @@
28
28
  <% end %>
29
29
  <%= pb_rails("dialog/dialog_body") do %>
30
30
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
31
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
31
+ <%= pb_rails("textarea", props: {id: "default"}) %>
32
32
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
33
33
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
34
34
  <%= pb_rails("dropdown", props: {options: options, autocomplete: true}) %>
@@ -4,7 +4,7 @@ import Body from '../../pb_body/_body'
4
4
  import Button from '../../pb_button/_button'
5
5
  import Caption from '../../pb_caption/_caption'
6
6
  import Dialog from '../../pb_dialog/_dialog'
7
- import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
7
+ import Textarea from '../../pb_textarea/_textarea'
8
8
  import Typeahead from '../../pb_typeahead/_typeahead'
9
9
 
10
10
  const DialogCompound = () => {
@@ -25,8 +25,11 @@ const DialogCompound = () => {
25
25
  <Body>{'What do you need us to take care of?'}</Body>
26
26
  </Dialog.Header>
27
27
  <Dialog.Body>
28
- <Caption marginBottom="xs">{'Description'}</Caption>
29
- <RichTextEditor />
28
+ <Textarea
29
+ id="default-example-1"
30
+ label="Description"
31
+ rows={4}
32
+ />
30
33
  <br />
31
34
  <Caption>
32
35
  {
@@ -1,11 +1,20 @@
1
1
  <%= pb_rails("button", props: { text: "Open Dialog", data: {"open-dialog": "dialog-1"} }) %>
2
2
 
3
- <%= pb_rails("dialog", props: {
4
- id:"dialog-1",
5
- size: "sm",
6
- title: "Header Title is the Title Prop",
7
- text: "Hello Body Text, Nice to meet ya.",
8
- cancel_button: "Cancel Button",
9
- confirm_button: "Okay",
10
- confirm_button_id: "confirm-button-1"
11
- }) %>
3
+ <%= pb_rails("dialog", props: {
4
+ id:"dialog-1",
5
+ size: "md",
6
+ title: "Header Title is the Title Prop"
7
+ }) do %>
8
+ <%= pb_rails("dialog/dialog_body") do %>
9
+ <%= pb_rails("button", props: { aria: { label: "Loading" }, loading: true, margin_right: "lg", text: "Button Primary" }) %>
10
+ <div style="height: 800px; background-color: lightgray;"></div>
11
+ <%= pb_rails("button", props: { loading: true, text: "Loading..." }) %>
12
+ <% end %>
13
+
14
+ <%= pb_rails("dialog/dialog_footer") do %>
15
+ <%= pb_rails("flex", props: { spacing: "between", padding_x: "md", padding_bottom: "md", padding: "sm" }) do %>
16
+ <%= pb_rails("button", props: { loading: true, text: "Send My Issue" }) %>
17
+ <%= pb_rails("button", props: { text: "Back", variant: "link", data: {"close-dialog": "dialog-1"} }) %>
18
+ <% end %>
19
+ <% end %>
20
+ <% end %>
@@ -12,16 +12,35 @@ const DialogDefault = () => {
12
12
  <>
13
13
  <Button onClick={open}>{'Open Dialog'}</Button>
14
14
  <Dialog
15
- cancelButton="Cancel Button"
16
- confirmButton="Okay"
17
15
  onCancel={close}
18
16
  onClose={close}
19
17
  onConfirm={close}
20
18
  opened={isOpen}
21
- size="sm"
22
- text="Hello Body Text, Nice to meet ya."
19
+ size="md"
23
20
  title="Header Title is the Title Prop"
24
- />
21
+ >
22
+ <Dialog.Body>
23
+ <Button
24
+ aria={{ label: 'Loading' }}
25
+ loading
26
+ text="Button Primary"
27
+ />
28
+ <div style={{height: '800px', backgroundColor: 'lightgray'}} />
29
+ <Button
30
+ loading
31
+ text="Loading..."
32
+ />
33
+ </Dialog.Body>
34
+ <Dialog.Footer>
35
+ <Button
36
+ loading
37
+ text="Send My Issue"
38
+ />
39
+ <Button variant="link">
40
+ {"Back"}
41
+ </Button>
42
+ </Dialog.Footer>
43
+ </Dialog>
25
44
  </>
26
45
  )
27
46
  }
@@ -12,7 +12,7 @@
12
12
  <% end %>
13
13
  <%= pb_rails("dialog/dialog_body") do %>
14
14
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
15
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
15
+ <%= pb_rails("textarea", props: {id: "default-7"}) %>
16
16
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
17
17
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
18
18
 
@@ -31,7 +31,7 @@
31
31
  <% end %>
32
32
  <%= pb_rails("dialog/dialog_body") do %>
33
33
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
34
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
34
+ <%= pb_rails("textarea", props: {id: "default-8"}) %>
35
35
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
36
36
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
37
37
 
@@ -49,7 +49,7 @@
49
49
  <% end %>
50
50
  <%= pb_rails("dialog/dialog_body") do %>
51
51
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
52
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
52
+ <%= pb_rails("textarea", props: {id: "default-9"}) %>
53
53
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
54
54
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
55
55
 
@@ -5,7 +5,7 @@ import Button from '../../pb_button/_button'
5
5
  import Dialog from '../../pb_dialog/_dialog'
6
6
  import Flex from '../../pb_flex/_flex'
7
7
  import Caption from '../../pb_caption/_caption'
8
- import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
8
+ import Textarea from '../../pb_textarea/_textarea'
9
9
  import Typeahead from '../../pb_typeahead/_typeahead'
10
10
 
11
11
  const useDialog = (visible = false) => {
@@ -77,8 +77,11 @@ const DialogFullHeight = () => {
77
77
  <Body>{title}</Body>
78
78
  </Dialog.Header>
79
79
  <Dialog.Body>
80
- <Caption marginBottom="xs">{"Description"}</Caption>
81
- <RichTextEditor />
80
+ <Textarea
81
+ id="default-example-1"
82
+ label="Description"
83
+ rows={4}
84
+ />
82
85
  <br />
83
86
  <Caption>
84
87
  {
@@ -13,7 +13,7 @@
13
13
  <% end %>
14
14
  <%= pb_rails("dialog/dialog_body") do %>
15
15
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
16
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
16
+ <%= pb_rails("textarea", props: {id: "default-2"}) %>
17
17
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
18
18
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
19
19
  <% end %>
@@ -32,7 +32,7 @@
32
32
  <% end %>
33
33
  <%= pb_rails("dialog/dialog_body") do %>
34
34
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
35
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
35
+ <%= pb_rails("textarea", props: {id: "default-3"}) %>
36
36
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
37
37
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
38
38
  <% end %>
@@ -50,7 +50,7 @@
50
50
  <% end %>
51
51
  <%= pb_rails("dialog/dialog_body") do %>
52
52
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
53
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
53
+ <%= pb_rails("textarea", props: {id: "default-4"}) %>
54
54
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
55
55
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
56
56
  <% end %>
@@ -5,7 +5,7 @@ import Button from '../../pb_button/_button'
5
5
  import Dialog from '../../pb_dialog/_dialog'
6
6
  import Flex from '../../pb_flex/_flex'
7
7
  import Caption from '../../pb_caption/_caption'
8
- import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
8
+ import Textarea from "../../pb_textarea/_textarea";
9
9
  import Typeahead from '../../pb_typeahead/_typeahead'
10
10
 
11
11
  const useDialog = (visible = false) => {
@@ -76,8 +76,11 @@ const DialogFullHeightPlacement = () => {
76
76
  <Body>{title}</Body>
77
77
  </Dialog.Header>
78
78
  <Dialog.Body>
79
- <Caption marginBottom="xs">{"Description"}</Caption>
80
- <RichTextEditor />
79
+ <Textarea
80
+ id={`default-example-2-${index}`}
81
+ label="Description"
82
+ rows={4}
83
+ />
81
84
  <br />
82
85
  <Caption>
83
86
  {
@@ -15,6 +15,9 @@
15
15
  .pb_dropdown_quickpick,
16
16
  .pb_dropdown_default_separators_hidden,
17
17
  .pb_dropdown_subtle_separators_hidden {
18
+ label {
19
+ display: block !important;
20
+ }
18
21
  .dropdown_wrapper {
19
22
  .dropdown_trigger_wrapper,
20
23
  .dropdown_trigger_wrapper_focus,
@@ -441,6 +441,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
441
441
  value={{
442
442
  activeStyle,
443
443
  autocomplete,
444
+ blankSelection,
444
445
  clearable,
445
446
  dropdownContainerRef,
446
447
  error,
@@ -0,0 +1,16 @@
1
+ <%
2
+ options = [
3
+ { label: "United States", value: "unitedStates", id: "us" },
4
+ { label: "Canada", value: "canada", id: "ca" },
5
+ { label: "Pakistan", value: "pakistan", id: "pk" },
6
+ ]
7
+ %>
8
+
9
+ <%= pb_rails("caption", props: { margin_bottom: "xs", text: "Any" }) %>
10
+ <%= pb_rails("dropdown", props: { options: options, close_on_click: "any", margin_bottom: "md" }) %>
11
+
12
+ <%= pb_rails("caption", props: { margin_bottom: "xs", text: "Outside" }) %>
13
+ <%= pb_rails("dropdown", props: { options: options, close_on_click: "outside", margin_bottom: "md" }) %>
14
+
15
+ <%= pb_rails("caption", props: { margin_bottom: "xs", text: "Inside" }) %>
16
+ <%= pb_rails("dropdown", props: { options: options, close_on_click: "inside" }) %>
@@ -0,0 +1 @@
1
+ The `close_on_click` prop allows you to control when the Dropdown closes in response to click interactions. The value `any` reflects the default behavior, where the dropdown will close after any click. Set it to `outside` to ensure interactive elements as dropdown options are able to be interacted with or modified. Set it to `inside` for a dropdown that only closes when the input or dropdown menu is clicked.
@@ -25,6 +25,7 @@ examples:
25
25
  - dropdown_separators_hidden: Separators Hidden
26
26
  - dropdown_with_clearable: Clearable
27
27
  - dropdown_with_constrain_height_rails: Constrain Height
28
+ - dropdown_closing_options_rails: Closing Options
28
29
  - dropdown_quickpick_rails: Quick Pick Variant
29
30
  - dropdown_quickpick_range_end_rails: Quick Pick Variant (Range Ends Today)
30
31
  - dropdown_quickpick_default_dates: Quick Pick Variant (Default Dates)
@@ -32,7 +33,6 @@ examples:
32
33
  - dropdown_quickpick_with_date_pickers_rails: Quick Pick with Date Pickers
33
34
  - dropdown_quickpick_with_date_pickers_default_rails: Quick Pick with Date Pickers (Default Value)
34
35
  - dropdown_required_indicator: Required Indicator
35
- - dropdown_custom_event_type: Custom Event Type
36
36
 
37
37
  react:
38
38
  - dropdown_default: Default
@@ -39,6 +39,9 @@ module Playbook
39
39
  default: ""
40
40
  prop :clearable, type: Playbook::Props::Boolean,
41
41
  default: true
42
+ prop :close_on_click, type: Playbook::Props::Enum,
43
+ values: %w[outside inside any],
44
+ default: "any"
42
45
  prop :start_date_id, type: Playbook::Props::String,
43
46
  default: "start_date_id"
44
47
  prop :start_date_name, type: Playbook::Props::String,
@@ -52,8 +55,6 @@ module Playbook
52
55
  default: false
53
56
  prop :required_indicator, type: Playbook::Props::Boolean,
54
57
  default: false
55
- prop :custom_event_type, type: Playbook::Props::String,
56
- default: ""
57
58
 
58
59
  def data
59
60
  Hash(prop(:data)).merge(
@@ -61,12 +62,12 @@ module Playbook
61
62
  pb_dropdown_multi_select: multi_select,
62
63
  pb_dropdown_variant: variant,
63
64
  pb_dropdown_clearable: clearable,
65
+ pb_dropdown_close_on_click: close_on_click,
64
66
  form_pill_props: form_pill_props.to_json,
65
67
  start_date_id: variant == "quickpick" ? start_date_id : nil,
66
68
  end_date_id: variant == "quickpick" ? end_date_id : nil,
67
69
  controls_start_id: variant == "quickpick" && controls_start_id.present? ? controls_start_id : nil,
68
- controls_end_id: variant == "quickpick" && controls_end_id.present? ? controls_end_id : nil,
69
- custom_event_type: custom_event_type.presence
70
+ controls_end_id: variant == "quickpick" && controls_end_id.present? ? controls_end_id : nil
70
71
  ).compact
71
72
  end
72
73
 
@@ -68,6 +68,7 @@ export default class PbDropdown extends PbEnhancedElement {
68
68
 
69
69
  this.keyboardHandler = new PbDropdownKeyboard(this);
70
70
  this.isMultiSelect = this.element.dataset.pbDropdownMultiSelect === "true";
71
+ this.closeOnClick = this.element.dataset.pbDropdownCloseOnClick || "any";
71
72
  this.formPillProps = this.element.dataset.formPillProps
72
73
  ? JSON.parse(this.element.dataset.formPillProps)
73
74
  : {};
@@ -94,25 +95,6 @@ export default class PbDropdown extends PbEnhancedElement {
94
95
  this.clearBtn.addEventListener("click", this.clearBtnHandler);
95
96
  }
96
97
  this.updateClearButton();
97
-
98
- // Listen for clear and select events from external source
99
- this.handleClearEventBound = this.handleClearEvent.bind(this);
100
- document.addEventListener("pb:dropdown:clear", this.handleClearEventBound);
101
- this.handleSelectEventBound = this.handleSelectEvent.bind(this);
102
- document.addEventListener("pb:dropdown:select", this.handleSelectEventBound);
103
-
104
- // Listen for custom_event_type to clear on custom events
105
- const customEventTypeString = this.element.dataset.customEventType;
106
- if (customEventTypeString) {
107
- this.customClearEventTypes = customEventTypeString
108
- .split(",")
109
- .map((e) => e.trim())
110
- .filter(Boolean);
111
- this.handleCustomClearBound = this.handleCustomClearEvent.bind(this);
112
- this.customClearEventTypes.forEach((eventType) => {
113
- document.addEventListener(eventType, this.handleCustomClearBound);
114
- });
115
- }
116
98
  }
117
99
 
118
100
  disconnect() {
@@ -164,19 +146,6 @@ export default class PbDropdown extends PbEnhancedElement {
164
146
  if (this.clearBtn && this.clearBtnHandler) {
165
147
  this.clearBtn.removeEventListener('click', this.clearBtnHandler)
166
148
  }
167
-
168
- // Clean up external clear/select listeners
169
- if (this.handleClearEventBound) {
170
- document.removeEventListener("pb:dropdown:clear", this.handleClearEventBound)
171
- }
172
- if (this.handleSelectEventBound) {
173
- document.removeEventListener("pb:dropdown:select", this.handleSelectEventBound)
174
- }
175
- if (this.customClearEventTypes && this.handleCustomClearBound) {
176
- this.customClearEventTypes.forEach((eventType) => {
177
- document.removeEventListener(eventType, this.handleCustomClearBound)
178
- })
179
- }
180
149
  }
181
150
 
182
151
  updateClearButton() {
@@ -204,6 +173,12 @@ export default class PbDropdown extends PbEnhancedElement {
204
173
  trigger.focus();
205
174
  }
206
175
  }
176
+ if (
177
+ this.closeOnClick === "outside" &&
178
+ this.target?.contains(e.target)
179
+ ) {
180
+ return;
181
+ }
207
182
  this.toggleElement(this.target);
208
183
  }
209
184
  customTrigger.addEventListener("click", this.customTriggerClickHandler);
@@ -367,151 +342,29 @@ export default class PbDropdown extends PbEnhancedElement {
367
342
 
368
343
  handleDocumentClick(event) {
369
344
  if (event.target.closest(SEARCH_BAR_SELECTOR)) return;
370
- if (this.isClickOutside(event) && this.target.classList.contains("open")) {
345
+ const shouldCloseOnOutsideClick =
346
+ this.closeOnClick === "outside" || this.closeOnClick === "any";
347
+ if (
348
+ shouldCloseOnOutsideClick &&
349
+ this.isClickOutside(event) &&
350
+ this.target.classList.contains("open")
351
+ ) {
371
352
  this.hideElement(this.target);
372
353
  this.updateArrowDisplay(false);
373
354
  }
374
355
  }
375
356
 
376
- // ----- External events handling section -----
377
- // Handles pb:dropdown:clear - clear this dropdown when event.detail.dropdownId matches.
378
- handleClearEvent(event) {
379
- const targetId = event.detail?.dropdownId;
380
- if (targetId && this.element.id === targetId) {
381
- this.clearSelection();
382
- }
383
- }
384
-
385
- // Handles custom_event_type events (e.g. turbo:submit-end) - clear when detail.dropdownId matches or is omitted.
386
- handleCustomClearEvent(event) {
387
- const targetId = event.detail?.dropdownId;
388
- if (targetId == null || this.element.id === targetId) {
389
- this.clearSelection();
390
- }
391
- }
392
-
393
- // Handles pb:dropdown:select - set dropdown selection by option id(s).
394
- // Single: detail: { dropdownId, optionId }. Multi: detail: { dropdownId, optionIds: ['id1', 'id2'] }.
395
- handleSelectEvent(event) {
396
- const targetId = event.detail?.dropdownId;
397
- if (!targetId || this.element.id !== targetId) return;
398
-
399
- const optionId = event.detail?.optionId;
400
- const optionIds = event.detail?.optionIds;
401
- if (optionId != null) {
402
- this.setSelectionByOptionId(optionId);
403
- } else if (Array.isArray(optionIds)) {
404
- this.setSelectionByOptionIds(optionIds);
405
- }
406
- }
407
-
408
- // Set single-select dropdown to the option with the given id. No-op if id not found.
409
- setSelectionByOptionId(optionId) {
410
- if (this.isMultiSelect) return;
411
- const hiddenInput = this.baseInput;
412
- const optionEls = Array.from(this.element.querySelectorAll(OPTION_SELECTOR));
413
- const selectedOption = optionEls.find((opt) => {
414
- try {
415
- return JSON.parse(opt.dataset.dropdownOptionLabel).id === optionId;
416
- } catch {
417
- return false;
418
- }
419
- });
420
- if (!selectedOption) return;
421
-
422
- optionEls.forEach((opt) => opt.classList.remove("pb_dropdown_option_selected"));
423
- selectedOption.classList.add("pb_dropdown_option_selected");
424
- hiddenInput.value = optionId;
425
- const optionData = JSON.parse(selectedOption.dataset.dropdownOptionLabel);
426
- const customDisplayElement = this.element.querySelector(
427
- '[data-dropdown-trigger-custom-display]',
428
- );
429
- if (customDisplayElement) {
430
- this.setTriggerElementText("");
431
- customDisplayElement.style.display = "block";
432
- customDisplayElement.style.paddingRight = "8px";
433
- } else {
434
- this.setTriggerElementText(optionData.label);
435
- }
436
- if (this.searchInput) {
437
- this.searchInput.value = optionData.label;
438
- }
439
-
440
- if (optionData.formatted_start_date && optionData.formatted_end_date) {
441
- const startDateId = this.element.dataset.startDateId;
442
- const endDateId = this.element.dataset.endDateId;
443
- const controlsStartId = this.element.dataset.controlsStartId;
444
- const controlsEndId = this.element.dataset.controlsEndId;
445
- if (startDateId) {
446
- const startDateInput = document.getElementById(startDateId);
447
- if (startDateInput) startDateInput.value = optionData.formatted_start_date;
448
- }
449
- if (endDateId) {
450
- const endDateInput = document.getElementById(endDateId);
451
- if (endDateInput) endDateInput.value = optionData.formatted_end_date;
452
- }
453
- const syncDatePickers = () => {
454
- if (controlsStartId) {
455
- const startPicker = document.querySelector(`#${CSS.escape(controlsStartId)}`)?._flatpickr;
456
- if (startPicker) startPicker.setDate(optionData.formatted_start_date, true, "m/d/Y");
457
- }
458
- if (controlsEndId) {
459
- const endPicker = document.querySelector(`#${CSS.escape(controlsEndId)}`)?._flatpickr;
460
- if (endPicker) endPicker.setDate(optionData.formatted_end_date, true, "m/d/Y");
461
- }
462
- };
463
- syncDatePickers();
464
- setTimeout(syncDatePickers, 100);
465
- setTimeout(syncDatePickers, 300);
466
- }
467
-
468
- this.updateClearButton();
469
- this.emitSelectionChange();
470
- }
471
-
472
- // Set multi-select dropdown to the options with the given ids. Invalid ids are skipped.
473
- setSelectionByOptionIds(optionIds) {
474
- if (!this.isMultiSelect || !optionIds.length) return;
475
- const optionEls = Array.from(this.element.querySelectorAll(OPTION_SELECTOR));
476
- this.selectedOptions.clear();
477
- optionEls.forEach((opt) => {
478
- opt.classList.remove("pb_dropdown_option_selected");
479
- opt.style.display = "";
480
- });
481
-
482
- optionIds.forEach((id) => {
483
- const opt = optionEls.find((o) => {
484
- try {
485
- return JSON.parse(o.dataset.dropdownOptionLabel).id === id;
486
- } catch {
487
- return false;
488
- }
489
- });
490
- if (opt) {
491
- const raw = opt.dataset.dropdownOptionLabel;
492
- this.selectedOptions.add(raw);
493
- opt.style.display = "none";
494
- }
495
- });
496
-
497
- this.updatePills();
498
- this.updateClearButton();
499
- this.adjustDropdownHeight();
500
- this.syncHiddenInputs();
501
- this.emitSelectionChange();
502
- }
503
- // ----- End External events handling section -----
504
-
505
357
  isClickOutside(event) {
506
358
  const label = event.target.closest(LABEL_SELECTOR);
507
359
  if (label && this.element.contains(label)) return false;
508
360
  const customTrigger = this.customTrigger;
509
361
  if (customTrigger) {
510
- return !customTrigger.contains(event.target);
362
+ const clickInTrigger = customTrigger.contains(event.target);
363
+ const clickInContainer = this.target?.contains(event.target);
364
+ return !clickInTrigger && !clickInContainer;
511
365
  } else {
512
366
  const triggerElement = this.trigger;
513
- const containerElement =
514
- this.element.parentNode.querySelector(CONTAINER_SELECTOR);
367
+ const containerElement = this.element.querySelector(CONTAINER_SELECTOR);
515
368
 
516
369
  const isOutsideTrigger = triggerElement
517
370
  ? !triggerElement.contains(event.target)
@@ -650,11 +503,15 @@ export default class PbDropdown extends PbEnhancedElement {
650
503
  }
651
504
 
652
505
  const customTrigger = this.customTrigger;
653
- if (customTrigger) {
654
- if (this.target.classList.contains("open")) {
655
- this.hideElement(this.target);
656
- this.updateArrowDisplay(false);
657
- }
506
+ const shouldCloseOnOptionSelect =
507
+ this.closeOnClick === "any" || this.closeOnClick === "inside";
508
+ if (
509
+ customTrigger &&
510
+ shouldCloseOnOptionSelect &&
511
+ this.target.classList.contains("open")
512
+ ) {
513
+ this.hideElement(this.target);
514
+ this.updateArrowDisplay(false);
658
515
  }
659
516
 
660
517
  const options = this.element.querySelectorAll(OPTION_SELECTOR);
@@ -44,6 +44,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
44
44
 
45
45
  const {
46
46
  autocomplete,
47
+ blankSelection,
47
48
  clearable,
48
49
  error,
49
50
  errorId,
@@ -57,6 +58,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
57
58
  isInputFocused,
58
59
  label: contextLabel,
59
60
  multiSelect,
61
+ optionsWithBlankSelection,
60
62
  selected,
61
63
  selectId,
62
64
  setIsInputFocused,
@@ -240,20 +242,22 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
240
242
  onClick: (e: Event) => {e.stopPropagation();handleWrapperClick()}
241
243
  }}
242
244
  key={`${isDropDownClosed ? "chevron-down" : "chevron-up"}`}
243
- >
244
- {
245
- clearable !== false && selectedArray.length > 0 && (
246
- <div onClick={(e)=>{e.stopPropagation();handleBackspace()}}>
247
- <Icon
248
- cursor="pointer"
249
- dark={dark}
250
- icon="times"
251
- paddingRight="xs"
252
- size="sm"
253
- />
254
- </div>
255
- )
256
- }
245
+ >
246
+ {(!blankSelection || selected?.value !== optionsWithBlankSelection?.[0]?.value) && (
247
+ <>
248
+ {clearable !== false && selectedArray.length > 0 && (
249
+ <div onClick={(e)=>{e.stopPropagation();handleBackspace()}}>
250
+ <Icon
251
+ cursor="pointer"
252
+ dark={dark}
253
+ icon="times"
254
+ paddingRight="xs"
255
+ size="sm"
256
+ />
257
+ </div>
258
+ )}
259
+ </>
260
+ )}
257
261
  <Icon
258
262
  cursor="pointer"
259
263
  dark={dark}
@@ -261,6 +265,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
261
265
  size="sm"
262
266
  />
263
267
  </Body>
268
+
264
269
  </FlexItem>
265
270
  </Flex>
266
271
  </>