playbook_ui 18.1.0 → 18.2.0.pre.alpha.PLAY3330requiredtypeaheadvalidationbug20501
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 +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +1 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +9 -5
- data/app/pb_kits/playbook/pb_advanced_table/Components/TablePagination.tsx +28 -6
- data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +6 -1
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +26 -9
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +2 -0
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +137 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination_section_separator.jsx +90 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination_section_separator.md +5 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination_with_props.md +2 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +8 -3
- data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +19 -6
- data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +1 -1
- data/app/pb_kits/playbook/pb_background/_background.tsx +3 -1
- data/app/pb_kits/playbook/pb_background/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_caption/_caption.tsx +3 -1
- data/app/pb_kits/playbook/pb_caption/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_container/_container.tsx +3 -1
- data/app/pb_kits/playbook/pb_container/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_currency/_currency.tsx +3 -1
- data/app/pb_kits/playbook/pb_currency/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_dashboard_value/_dashboard_value.tsx +3 -1
- data/app/pb_kits/playbook/pb_dashboard_value/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +4 -0
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +51 -49
- data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +56 -56
- data/app/pb_kits/playbook/pb_date_picker/sass_partials/_calendar_input_icon.scss +1 -1
- data/app/pb_kits/playbook/pb_detail/_detail.tsx +3 -1
- data/app/pb_kits/playbook/pb_detail/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +16 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +190 -29
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_custom_content.html.erb +58 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_custom_content.md +10 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_initial_options.jsx +45 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_initial_options.md +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_loading.jsx +43 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_loading.md +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_multi_select.jsx +44 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_multi_select.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_multi_select_rails.html.erb +44 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_multi_select_rails.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_rich_results.jsx +64 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_rich_results.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_search.html.erb +43 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_async_search.md +55 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_controlled_selection.jsx +42 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_controlled_selection.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_external_search.jsx +58 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_external_search.md +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_no_options_text.html.erb +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_no_options_text.jsx +35 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_no_options_text_rails.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_no_options_text_react.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom.md +7 -2
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom_rails.md +7 -2
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.html.erb +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.jsx +42 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.md +24 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_highlight.jsx +101 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_highlight.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_playground.json +174 -3
- data/app/pb_kits/playbook/pb_dropdown/docs/_playground.overrides.json +154 -2
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +14 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +9 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +4 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +14 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +1344 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown_container.html.erb +1 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +1 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown_index.test.js +1153 -0
- data/app/pb_kits/playbook/pb_dropdown/hooks/useAsyncOptions.ts +179 -0
- data/app/pb_kits/playbook/pb_dropdown/hooks/useHandleOnKeydown.tsx +21 -19
- data/app/pb_kits/playbook/pb_dropdown/index.js +518 -79
- data/app/pb_kits/playbook/pb_dropdown/keyboard_accessibility.js +5 -3
- data/app/pb_kits/playbook/pb_dropdown/kit.schema.json +78 -1
- data/app/pb_kits/playbook/pb_dropdown/quickpick/index.ts +60 -18
- data/app/pb_kits/playbook/pb_dropdown/quickpick_helper.rb +92 -3
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +17 -3
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +21 -7
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +5 -2
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/MultiSelectTriggerDisplay.tsx +9 -3
- data/app/pb_kits/playbook/pb_dropdown/utilities/index.ts +8 -1
- data/app/pb_kits/playbook/pb_dropdown/utilities/quickpickDateSync.ts +137 -0
- data/app/pb_kits/playbook/pb_filter/Filter/InteractiveFilter.tsx +1 -1
- data/app/pb_kits/playbook/pb_form/docs/_description_react.md +3 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_default_values.jsx +898 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_default_values_react.md +3 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_defaults.jsx +879 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_defaults_react.md +29 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_loading.jsx +75 -0
- data/app/pb_kits/playbook/pb_form/docs/{_form_loading.md → _form_loading_rails.md} +1 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_loading_react.md +1 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_model_values.jsx +147 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_model_values_react.md +16 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_multi_column.jsx +164 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_multi_column_react.md +3 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_validation.html.erb +27 -4
- data/app/pb_kits/playbook/pb_form/docs/_form_validation.jsx +1057 -0
- data/app/pb_kits/playbook/pb_form/docs/{_form_validation.md → _form_validation_rails.md} +4 -4
- data/app/pb_kits/playbook/pb_form/docs/_form_validation_react.md +26 -0
- data/app/pb_kits/playbook/pb_form/docs/example.yml +7 -0
- data/app/pb_kits/playbook/pb_form/docs/snippets/validation_checkbox.txt +6 -1
- data/app/pb_kits/playbook/pb_form/docs/snippets/validation_dropdown.txt +6 -1
- data/app/pb_kits/playbook/pb_form/docs/snippets/validation_multi_level_select.txt +1 -0
- data/app/pb_kits/playbook/pb_form/docs/snippets/validation_multi_select.txt +1 -0
- data/app/pb_kits/playbook/pb_form/docs/snippets/validation_phone_number.txt +1 -0
- data/app/pb_kits/playbook/pb_form/docs/snippets/validation_star_rating.txt +6 -1
- data/app/pb_kits/playbook/pb_form/docs/snippets/validation_textarea.txt +5 -1
- data/app/pb_kits/playbook/pb_form/docs/snippets/validation_time_zone_select.txt +1 -0
- data/app/pb_kits/playbook/pb_form/pb_form_validation.js +52 -34
- data/app/pb_kits/playbook/pb_full_screen/_full_screen.scss +6 -1
- data/app/pb_kits/playbook/pb_full_screen/full_screen.test.jsx +19 -7
- data/app/pb_kits/playbook/pb_highlight/_highlight.tsx +3 -1
- data/app/pb_kits/playbook/pb_highlight/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_icon_stat_value/_icon_stat_value.tsx +3 -1
- data/app/pb_kits/playbook/pb_icon_stat_value/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_icon_value/_icon_value.tsx +3 -1
- data/app/pb_kits/playbook/pb_icon_value/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_label_value/_label_value.tsx +3 -1
- data/app/pb_kits/playbook/pb_label_value/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_legend/_legend.tsx +3 -1
- data/app/pb_kits/playbook/pb_legend/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_lightbox/Header/_lightbox_header.tsx +1 -1
- data/app/pb_kits/playbook/pb_lightbox/lightbox.test.jsx +1 -1
- data/app/pb_kits/playbook/pb_map/docs/_map_default.jsx +1 -1
- data/app/pb_kits/playbook/pb_map/docs/_map_default.md +3 -3
- data/app/pb_kits/playbook/pb_map/docs/_map_with_custom_button.jsx +1 -1
- data/app/pb_kits/playbook/pb_map/docs/_map_with_plugin.jsx +1 -1
- data/app/pb_kits/playbook/pb_map/docs/_playground.json +3 -3
- data/app/pb_kits/playbook/pb_map/docs/_playground.overrides.json +3 -3
- data/app/pb_kits/playbook/pb_message/_message.tsx +3 -1
- data/app/pb_kits/playbook/pb_message/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/index.js +19 -4
- data/app/pb_kits/playbook/pb_multi_level_select/kit.schema.json +7 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.html.erb +4 -1
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +7 -3
- data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +3 -1
- data/app/pb_kits/playbook/pb_multiple_users/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +4 -2
- data/app/pb_kits/playbook/pb_multiple_users_stacked/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +10 -7
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_playground.json +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/index.ts +7 -2
- data/app/pb_kits/playbook/pb_phone_number_input/kit.schema.json +8 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +5 -1
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +12 -0
- data/app/pb_kits/playbook/pb_progress_step/_progress_step_item.tsx +3 -1
- data/app/pb_kits/playbook/pb_rich_text_editor/TipTap/MoreExtensionsDropdown.tsx +10 -3
- data/app/pb_kits/playbook/pb_rich_text_editor/TipTap/Toolbar.tsx +16 -1
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_playground.json +2 -3
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_playground.overrides.json +2 -3
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_attributes.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_default.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_default.md +2 -3
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_focus.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_height.jsx +3 -13
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_inline.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.jsx +2 -3
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_markdown_support.jsx +1 -2
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_min_height.jsx +3 -13
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_preview.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_simple.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_sticky.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_templates.jsx +1 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_more_extensions.jsx +3 -8
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_more_extensions.md +1 -1
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.md +2 -2
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.test.js +2 -1
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_advanced.test.js +7 -5
- data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_rails.js +15 -4
- data/app/pb_kits/playbook/pb_select/_select.scss +2 -2
- data/app/pb_kits/playbook/pb_star_rating/index.js +8 -4
- data/app/pb_kits/playbook/pb_star_rating/kit.schema.json +7 -0
- data/app/pb_kits/playbook/pb_star_rating/star_rating.rb +6 -1
- data/app/pb_kits/playbook/pb_stat_value/_stat_value.tsx +3 -1
- data/app/pb_kits/playbook/pb_stat_value/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_table/_table.tsx +70 -197
- data/app/pb_kits/playbook/pb_table/index.ts +53 -122
- data/app/pb_kits/playbook/pb_table/styles/_sticky_columns.scss +26 -3
- data/app/pb_kits/playbook/pb_table/styles/_vertical_border.scss +27 -8
- data/app/pb_kits/playbook/pb_table/utilities/stickyColumns.test.ts +266 -0
- data/app/pb_kits/playbook/pb_table/utilities/stickyColumns.ts +145 -0
- data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +1 -1
- data/app/pb_kits/playbook/pb_text_input/add_on.html.erb +2 -2
- data/app/pb_kits/playbook/pb_timeline/_item.tsx +3 -1
- data/app/pb_kits/playbook/pb_title_count/_title_count.tsx +3 -1
- data/app/pb_kits/playbook/pb_title_count/kit.schema.json +1 -0
- data/app/pb_kits/playbook/pb_title_detail/_title_detail.tsx +3 -1
- data/app/pb_kits/playbook/pb_title_detail/kit.schema.json +1 -0
- data/app/pb_kits/playbook/utilities/test/globalProps/onClick.test.js +327 -0
- data/dist/ai/all-schemas.json +277 -55
- data/dist/ai/forms.json +26 -20
- data/dist/ai/index.json +2 -2
- data/dist/ai/kits/background.schema.json +1 -0
- data/dist/ai/kits/button.schema.json +1 -1
- data/dist/ai/kits/caption.schema.json +1 -0
- data/dist/ai/kits/checkbox.schema.json +7 -5
- data/dist/ai/kits/container.schema.json +1 -0
- data/dist/ai/kits/currency.schema.json +1 -0
- data/dist/ai/kits/dashboard_value.schema.json +1 -0
- data/dist/ai/kits/date_picker.schema.json +4 -3
- data/dist/ai/kits/detail.schema.json +1 -0
- data/dist/ai/kits/dropdown.schema.json +160 -10
- data/dist/ai/kits/highlight.schema.json +1 -0
- data/dist/ai/kits/icon_stat_value.schema.json +1 -0
- data/dist/ai/kits/icon_value.schema.json +1 -0
- data/dist/ai/kits/label_value.schema.json +1 -0
- data/dist/ai/kits/legend.schema.json +1 -0
- data/dist/ai/kits/message.schema.json +1 -0
- data/dist/ai/kits/multi_level_select.schema.json +15 -6
- data/dist/ai/kits/multiple_users.schema.json +1 -0
- data/dist/ai/kits/multiple_users_stacked.schema.json +1 -0
- data/dist/ai/kits/phone_number_input.schema.json +18 -7
- data/dist/ai/kits/select.schema.json +25 -6
- data/dist/ai/kits/star_rating.schema.json +14 -5
- data/dist/ai/kits/stat_value.schema.json +1 -0
- data/dist/ai/kits/text_input.schema.json +4 -3
- data/dist/ai/kits/textarea.schema.json +4 -3
- data/dist/ai/kits/time_picker.schema.json +4 -3
- data/dist/ai/kits/title_count.schema.json +1 -0
- data/dist/ai/kits/title_detail.schema.json +1 -0
- data/dist/ai/kits/typeahead.schema.json +4 -3
- data/dist/ai/playgrounds/dropdown.json +1 -1
- data/dist/ai/playgrounds/index.json +1 -1
- data/dist/ai/playgrounds/map.json +1 -1
- data/dist/ai/playgrounds/rich_text_editor.json +1 -1
- data/dist/chunks/_pagination-BStnaG2D.js +1 -0
- data/dist/chunks/{_pb_line_graph-CUJ7NJIr.js → _pb_line_graph-DAoYQIoh.js} +1 -1
- data/dist/chunks/_tooltip-WptzLsCF.js +5 -0
- data/dist/chunks/_typeahead-DNfFwTns.js +1 -0
- data/dist/chunks/{globalProps-DDcpyjvz.js → globalProps-wXibGgWr.js} +1 -1
- data/dist/chunks/lib-BDUQ3wt0.js +29 -0
- data/dist/chunks/quickpickDateSync-DLDx4zce.js +1 -0
- data/dist/chunks/{time_picker_helper-B_FN8E9Y.js → time_picker_helper-Dc38LjqW.js} +1 -1
- data/dist/chunks/vendor.js +2 -2
- data/dist/menu.yml +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/forms/builder/checkbox_field.rb +1 -0
- data/lib/playbook/forms/builder/collection_select_field.rb +5 -1
- data/lib/playbook/forms/builder/select_field.rb +5 -1
- data/lib/playbook/version.rb +2 -2
- metadata +63 -16
- data/dist/chunks/_detail-CDUjsRrc.js +0 -5
- data/dist/chunks/_table-C9KHGQhb.js +0 -1
- data/dist/chunks/_typeahead-CfxZLhd3.js +0 -1
- data/dist/chunks/lib-D57mygNT.js +0 -29
- /data/app/pb_kits/playbook/pb_form/docs/{_description.md → _description_rails.md} +0 -0
- /data/app/pb_kits/playbook/pb_form/docs/{_form_default_values.md → _form_default_values_rails.md} +0 -0
- /data/app/pb_kits/playbook/pb_form/docs/{_form_defaults.md → _form_defaults_rails.md} +0 -0
- /data/app/pb_kits/playbook/pb_form/docs/{_form_model_values.md → _form_model_values_rails.md} +0 -0
- /data/app/pb_kits/playbook/pb_form/docs/{_form_multi_column.md → _form_multi_column_rails.md} +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Dropdown from '../_dropdown'
|
|
3
|
+
|
|
4
|
+
let searchTimer
|
|
5
|
+
let searchController
|
|
6
|
+
|
|
7
|
+
const loadUsers = (term) => new Promise((resolve, reject) => {
|
|
8
|
+
clearTimeout(searchTimer)
|
|
9
|
+
searchController?.abort()
|
|
10
|
+
const controller = searchController = new AbortController()
|
|
11
|
+
searchTimer = setTimeout(() => {
|
|
12
|
+
const params = new URLSearchParams({ q: term, limit: '10', select: 'firstName,lastName' })
|
|
13
|
+
fetch(`https://dummyjson.com/users/search?${params}`, { signal: controller.signal, credentials: 'omit' })
|
|
14
|
+
.then((response) => {
|
|
15
|
+
if (!response.ok) throw new Error('Search failed')
|
|
16
|
+
return response.json()
|
|
17
|
+
})
|
|
18
|
+
.then(({ users }) => resolve(users.map((user) => ({
|
|
19
|
+
...user,
|
|
20
|
+
label: `${user.firstName} ${user.lastName}`,
|
|
21
|
+
value: user.id,
|
|
22
|
+
}))))
|
|
23
|
+
.catch((error) => {
|
|
24
|
+
if (error.name !== 'AbortError') reject(error)
|
|
25
|
+
})
|
|
26
|
+
}, 250)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const DropdownAsyncMultiSelect = () => (
|
|
30
|
+
<Dropdown
|
|
31
|
+
activeStyle={{
|
|
32
|
+
backgroundColor: "bg_light",
|
|
33
|
+
fontColor: "text_lt_default",
|
|
34
|
+
}}
|
|
35
|
+
async
|
|
36
|
+
autocomplete
|
|
37
|
+
label="Users"
|
|
38
|
+
loadOptions={loadUsers}
|
|
39
|
+
multiSelect
|
|
40
|
+
placeholder="Try Emily, then Michael"
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
export default DropdownAsyncMultiSelect
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
`multiSelect` works with `async` and `autocomplete`, or with `Dropdown.Container searchbar`.
|
|
2
|
+
|
|
3
|
+
This example searches DummyJSON's public sample users and waits 250ms after the last keystroke before fetching. Try `Emily`, select a match, then search for `Michael`.
|
|
4
|
+
|
|
5
|
+
Each selection is added as a Form Pill and the query is cleared. The chosen row stays hidden, including when a later search returns that person again. When `defaultOptions` is set, the menu returns to that list; otherwise it shows the empty message until the next search. Removing a pill or clearing the Dropdown removes that selection. Results are matched by `value`, then `id`, then `label`, so two results with the same label stay distinct. The pills hold the selection, and the input stays empty for the next search.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<%= pb_rails("dropdown", props: {
|
|
2
|
+
id: "async-multi-user-dropdown",
|
|
3
|
+
label: "Users",
|
|
4
|
+
name: "async_multi_user",
|
|
5
|
+
placeholder: "Try Emily, then Michael",
|
|
6
|
+
autocomplete: true,
|
|
7
|
+
async: true,
|
|
8
|
+
multi_select: true,
|
|
9
|
+
active_style: { background_color: "bg_light", font_color: "text_lt_default" },
|
|
10
|
+
search_term_minimum_length: 3,
|
|
11
|
+
search_debounce_timeout: 250,
|
|
12
|
+
options: [],
|
|
13
|
+
}) %>
|
|
14
|
+
|
|
15
|
+
<script>
|
|
16
|
+
(() => {
|
|
17
|
+
const dropdown = document.getElementById("async-multi-user-dropdown");
|
|
18
|
+
if (!dropdown || dropdown.dataset.asyncExampleBound) return;
|
|
19
|
+
dropdown.dataset.asyncExampleBound = "true";
|
|
20
|
+
|
|
21
|
+
dropdown.addEventListener("pb:dropdown:search", (event) => {
|
|
22
|
+
const { searchingFor, setResults, setError } = event.detail;
|
|
23
|
+
const params = new URLSearchParams({
|
|
24
|
+
q: searchingFor,
|
|
25
|
+
limit: "10",
|
|
26
|
+
select: "firstName,lastName",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
fetch(`https://dummyjson.com/users/search?${params}`, { credentials: "omit" })
|
|
30
|
+
.then((response) => {
|
|
31
|
+
if (!response.ok) throw new Error("User search failed");
|
|
32
|
+
return response.json();
|
|
33
|
+
})
|
|
34
|
+
.then(({ users }) => {
|
|
35
|
+
setResults(users.map((user) => ({
|
|
36
|
+
id: user.id,
|
|
37
|
+
label: `${user.firstName} ${user.lastName}`,
|
|
38
|
+
value: user.id,
|
|
39
|
+
})));
|
|
40
|
+
})
|
|
41
|
+
.catch(() => setError());
|
|
42
|
+
});
|
|
43
|
+
})();
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
`multi_select: true` works with `async: true` and either `autocomplete: true` or `searchbar: true`.
|
|
2
|
+
|
|
3
|
+
Search still waits for `search_term_minimum_length` characters and `search_debounce_timeout` before emitting `pb:dropdown:search`. This example searches [DummyJSON’s public sample users API](https://dummyjson.com/docs/users). Try `Emily`, select a match, then search for `Michael`.
|
|
4
|
+
|
|
5
|
+
Each selection is added as a Form Pill, the query is cleared, and the loaded results are dropped. Opening the menu again shows the empty message until the next search. `pb:dropdown:selected` emits the full array of selected options. A previously selected id stays hidden when that person appears in a later result list. Removing a pill or clearing the Dropdown removes that selection and emits the remaining array, or `[]` when nothing remains. Removing the last pill with an empty query also drops a later search, so the menu shows the empty message until the next search. The pills hold the selection, and the input stays empty for the next search.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import Dropdown from '../_dropdown'
|
|
3
|
+
import Body from '../../pb_body/_body'
|
|
4
|
+
import Detail from '../../pb_detail/_detail'
|
|
5
|
+
|
|
6
|
+
let searchTimer
|
|
7
|
+
let searchController
|
|
8
|
+
|
|
9
|
+
const loadUsers = (term) => new Promise((resolve, reject) => {
|
|
10
|
+
clearTimeout(searchTimer)
|
|
11
|
+
searchController?.abort()
|
|
12
|
+
const controller = searchController = new AbortController()
|
|
13
|
+
searchTimer = setTimeout(() => {
|
|
14
|
+
const params = new URLSearchParams({ q: term, limit: '10', select: 'firstName,lastName,company' })
|
|
15
|
+
fetch(`https://dummyjson.com/users/search?${params}`, { signal: controller.signal, credentials: 'omit' })
|
|
16
|
+
.then((response) => {
|
|
17
|
+
if (!response.ok) throw new Error('Search failed')
|
|
18
|
+
return response.json()
|
|
19
|
+
})
|
|
20
|
+
.then(({ users }) => resolve(users.map((user) => ({
|
|
21
|
+
...user,
|
|
22
|
+
label: `${user.firstName} ${user.lastName}`,
|
|
23
|
+
value: user.id,
|
|
24
|
+
}))))
|
|
25
|
+
.catch((error) => {
|
|
26
|
+
if (error.name !== 'AbortError') reject(error)
|
|
27
|
+
})
|
|
28
|
+
}, 250)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const companyDetail = (user) => [user.company?.title, user.company?.department].filter(Boolean).join(' · ')
|
|
32
|
+
|
|
33
|
+
const renderUser = (user) => (
|
|
34
|
+
<div>
|
|
35
|
+
<Body text={user.label} />
|
|
36
|
+
<Detail text={companyDetail(user)} />
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const DropdownAsyncRichResults = () => {
|
|
41
|
+
const [selected, setSelected] = useState(null)
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Dropdown
|
|
45
|
+
activeStyle={{
|
|
46
|
+
backgroundColor: "bg_light",
|
|
47
|
+
fontColor: "text_lt_default",
|
|
48
|
+
}}
|
|
49
|
+
async
|
|
50
|
+
autocomplete
|
|
51
|
+
label="User"
|
|
52
|
+
loadOptions={loadUsers}
|
|
53
|
+
onSelect={setSelected}
|
|
54
|
+
placeholder="Try Emily or Michael"
|
|
55
|
+
renderOption={renderUser}
|
|
56
|
+
value={selected}
|
|
57
|
+
>
|
|
58
|
+
<Dropdown.Trigger customDisplay={selected && <Detail text={companyDetail(selected)} />} />
|
|
59
|
+
<Dropdown.Container />
|
|
60
|
+
</Dropdown>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default DropdownAsyncRichResults
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Use `renderOption` to render rich content for each async result. `activeStyle={{ backgroundColor: "bg_light", fontColor: "text_lt_default" }}` is recommended so the selected row uses a light background and default text color. The callback receives the complete option object and returns React content. This example searches DummyJSON's public sample users and displays each user's name, job title, and department.
|
|
2
|
+
|
|
3
|
+
Dropdown retains the option wrapper, keyboard navigation, disabled behavior, and selection handling. Map each result to include a plain-text `label` for the autocomplete input; selecting an option returns its original data, not its rendered content. Avoid interactive controls inside the renderer because the entire row selects the option. Explicit `Dropdown.Option` children take precedence, and returning `null` or `undefined` falls back to the standard label.
|
|
4
|
+
|
|
5
|
+
To customize the selected value, pass `customDisplay` to `Dropdown.Trigger`, as with synchronous options. A `Dropdown.Container` without option children displays the async results.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<%= pb_rails("dropdown", props: {
|
|
2
|
+
id: "async-user-dropdown",
|
|
3
|
+
label: "User",
|
|
4
|
+
name: "async_user",
|
|
5
|
+
placeholder: "Try Emily or Michael",
|
|
6
|
+
autocomplete: true,
|
|
7
|
+
async: true,
|
|
8
|
+
active_style: { background_color: "bg_light", font_color: "text_lt_default" },
|
|
9
|
+
search_term_minimum_length: 3,
|
|
10
|
+
search_debounce_timeout: 250,
|
|
11
|
+
options: [],
|
|
12
|
+
}) %>
|
|
13
|
+
|
|
14
|
+
<script>
|
|
15
|
+
(() => {
|
|
16
|
+
const dropdown = document.getElementById("async-user-dropdown");
|
|
17
|
+
if (!dropdown || dropdown.dataset.asyncExampleBound) return;
|
|
18
|
+
dropdown.dataset.asyncExampleBound = "true";
|
|
19
|
+
|
|
20
|
+
dropdown.addEventListener("pb:dropdown:search", (event) => {
|
|
21
|
+
const { searchingFor, setResults, setError } = event.detail;
|
|
22
|
+
const params = new URLSearchParams({
|
|
23
|
+
q: searchingFor,
|
|
24
|
+
limit: "10",
|
|
25
|
+
select: "firstName,lastName",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
fetch(`https://dummyjson.com/users/search?${params}`, { credentials: "omit" })
|
|
29
|
+
.then((response) => {
|
|
30
|
+
if (!response.ok) throw new Error("User search failed");
|
|
31
|
+
return response.json();
|
|
32
|
+
})
|
|
33
|
+
.then(({ users }) => {
|
|
34
|
+
setResults(users.map((user) => ({
|
|
35
|
+
id: user.id,
|
|
36
|
+
label: `${user.firstName} ${user.lastName}`,
|
|
37
|
+
value: user.id,
|
|
38
|
+
})));
|
|
39
|
+
})
|
|
40
|
+
.catch(() => setError());
|
|
41
|
+
});
|
|
42
|
+
})();
|
|
43
|
+
</script>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Rails Dropdown supports event-driven remote search with `async: true` and either `autocomplete: true` or `searchbar: true`. `active_style: { background_color: "bg_light", font_color: "text_lt_default" }` is recommended so the selected row uses a light background and default text color. Local Dropdown filtering remains unchanged when async is disabled.
|
|
2
|
+
|
|
3
|
+
Type at least `search_term_minimum_length` characters (default: `3`). After `search_debounce_timeout` milliseconds without another edit (default: `250`), the kit opens the menu, displays a loading message, and emits a bubbling `pb:dropdown:search` event from the Dropdown root. Scope listeners using the root's `id` or `data` attributes.
|
|
4
|
+
|
|
5
|
+
The event detail contains:
|
|
6
|
+
|
|
7
|
+
- `searchingFor`: the current query.
|
|
8
|
+
- `setResults(options)`: complete the search with an array of standard Dropdown option objects (`id`, `label`, `value`) or rich `{ option, content }` entries. Use an empty array for no matches.
|
|
9
|
+
- `setError()`: end loading and display a failure message.
|
|
10
|
+
|
|
11
|
+
The application owns fetching and mapping results. For example, call `fetch(yourUrl).then(...)`, check `response.ok`, map the response to options, pass them to `setResults`, and call `setError` on failure. This example searches [DummyJSON’s public sample users API](https://dummyjson.com/docs/users) over HTTPS without credentials or an API key. Try `Emily` or `Michael`. The API receives the search text and returns up to 10 results; only name fields are requested. HTTP or network failures display the kit’s error state.
|
|
12
|
+
|
|
13
|
+
Results are displayed without additional local label filtering and without clearing the typed query. When a search returns nothing, earlier results whose labels contain the query stay visible, including text typed after a space. After a single-select autocomplete selection, the input shows that option's label, and opening the menu again shows only retained results whose labels contain that text. With `multi_select: true`, each pick becomes a Form Pill, the query clears, and that row stays hidden. See Async Multi Select. The current results stay in place until the debounced search starts. Callbacks are request-scoped: subsequent edits, selection, clear/reset, dismissal with Escape/Tab or an outside click, and disconnect invalidate pending requests. Dismissing during the debounce delay cancels the search before it starts. Dismissing an in-flight request drops its late response and restores the last results. Opening the menu again searches for the query still in the input. Only the first completion is accepted. Requests that do not complete within 15 seconds display a failure message. The kit ignores stale responses; it does not abort application-owned network requests.
|
|
14
|
+
|
|
15
|
+
For rich results, `content` is an Element or DocumentFragment cloned into the option wrapper; `option` supplies the label and selection data. See Async Search with Custom Content for a Rails template example.
|
|
16
|
+
|
|
17
|
+
Async Quick Pick is not supported; Quick Pick uses static date presets.
|
|
18
|
+
|
|
19
|
+
#### Selection data and defaults
|
|
20
|
+
|
|
21
|
+
`pb:dropdown:selected` bubbles from the Dropdown root after selection updates. Its existing detail shape is unchanged: the complete option object for single-select, an array for multi-select, and `null` / `[]` when cleared. Custom JSON-serializable fields, nested objects, booleans, and arrays are preserved; rendered rich content is not included. For example, read `event.detail.department_id` to update a dependent field. Numeric IDs retain their type in the event payload; form inputs submit strings.
|
|
22
|
+
|
|
23
|
+
Selected data is retained independently of remote result rows. A later search returning different options does not discard the selection or its metadata. In multi-select, previously selected IDs remain hidden when they reappear, even if the server returns updated labels or other fields. The selected payload stays as it was when selected until the selection is explicitly changed.
|
|
24
|
+
|
|
25
|
+
To initialize an async Dropdown before any results load, supply a complete `default_value` option object (an array for multi-select), including `id` and `label`. For example: `async: true, autocomplete: true, options: [], default_value: { id: 42, label: "Ada", department_id: 7 }`. When using `dropdown_field`, pass that explicit default: the builder cannot resolve an initial model ID from an empty options array.
|
|
26
|
+
|
|
27
|
+
#### Input and reset events
|
|
28
|
+
|
|
29
|
+
With `async: true`, `pb:dropdown:input` bubbles from the Dropdown root immediately on every edit, including text below the search minimum. Its detail is `{ value, reason }`:
|
|
30
|
+
|
|
31
|
+
- `reason: "input"`: the autocomplete or search-bar text was edited. `value` is the current text, without debouncing.
|
|
32
|
+
- `reason: "clear"`: the clear control or public `pb:dropdown:clear` command cleared the Dropdown. `value` is `""`.
|
|
33
|
+
- `reason: "reset"`: a native form reset cleared the Dropdown. `value` is `""`.
|
|
34
|
+
|
|
35
|
+
Selection changes continue to use `pb:dropdown:selected`; choosing an option does not emit an input event. For free-text forms such as a name search, maintain a separate field from both events:
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
dropdown.addEventListener("pb:dropdown:input", ({ detail }) => {
|
|
39
|
+
nameField.value = detail.value;
|
|
40
|
+
// Application-owned IDs can be invalidated on any edit.
|
|
41
|
+
userIdField.value = "";
|
|
42
|
+
});
|
|
43
|
+
dropdown.addEventListener("pb:dropdown:selected", ({ detail }) => {
|
|
44
|
+
nameField.value = detail?.label || "";
|
|
45
|
+
userIdField.value = detail?.id ?? "";
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Editing nonempty text retains the Dropdown's selected value until it is changed or cleared. Emptying a single-select autocomplete clears its selected ID and emits `pb:dropdown:selected` with `null`. Emptying a search-bar query does not discard the selected option; emptying a multi-select query does not discard selected pills.
|
|
50
|
+
|
|
51
|
+
Explicit clear and native form reset clear query text, selection, remote results, loading/error messages, and pending callbacks, and close the menu. They emit the normal cleared selection payload (`null` or `[]`) and the input notification. Native reset completes after the browser resets form controls; a canceled reset leaves kit state intact. Reset clears the selection rather than restoring `default_value`, matching the kit's existing reset convention.
|
|
52
|
+
|
|
53
|
+
Clicking inside an async Dropdown opens the menu. When no results are available yet, it shows the empty message until the debounced search starts and replaces it with loading, results, or an error. An async search-bar menu stays open so its input remains accessible.
|
|
54
|
+
|
|
55
|
+
The clear control is also available for an unselected async query unless `clearable: false`. No async input notifications, remote-result cleanup, or changed reset timing are applied to synchronous Dropdowns.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import Button from '../../pb_button/_button'
|
|
3
|
+
import Dropdown from '../_dropdown'
|
|
4
|
+
import Flex from '../../pb_flex/_flex'
|
|
5
|
+
|
|
6
|
+
const options = [
|
|
7
|
+
{ id: 'engineering', label: 'Engineering', value: 'engineering' },
|
|
8
|
+
{ id: 'operations', label: 'Operations', value: 'operations' },
|
|
9
|
+
{ id: 'sales', label: 'Sales', value: 'sales' },
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
const DropdownControlledSelection = (props) => {
|
|
13
|
+
const [selected, setSelected] = useState([])
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
<Dropdown
|
|
18
|
+
label="Departments"
|
|
19
|
+
marginBottom="sm"
|
|
20
|
+
multiSelect
|
|
21
|
+
onSelect={setSelected}
|
|
22
|
+
options={options}
|
|
23
|
+
value={selected}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
<Flex gap="xs">
|
|
27
|
+
<Button
|
|
28
|
+
onClick={() => setSelected(options)}
|
|
29
|
+
text="Select All"
|
|
30
|
+
variant="secondary"
|
|
31
|
+
/>
|
|
32
|
+
<Button
|
|
33
|
+
onClick={() => setSelected([])}
|
|
34
|
+
text="Clear"
|
|
35
|
+
variant="secondary"
|
|
36
|
+
/>
|
|
37
|
+
</Flex>
|
|
38
|
+
</>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default DropdownControlledSelection
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Pass `value` to control selection from React state. Use an option object or `null` for single selection, and an array of option objects for multi-selection. Dropdown emits a proposed selection through `onSelect` and the existing `onChange({ target: { name, value } })` callback; the parent must update `value` to accept it. Clear and pill removal follow the same rule. Updating `value` itself does not emit a selection callback.
|
|
2
|
+
|
|
3
|
+
Use `value` when something other than the Dropdown changes the selection after mount, such as the buttons in this example, a form library reset, or data loaded after the form renders. `defaultValue` is only read on mount.
|
|
4
|
+
|
|
5
|
+
Selected objects retain their custom and nested data, even when absent from the current options or remote results. `value` takes precedence over `defaultValue`. Omit `value` to retain the existing uncontrolled behavior; use `null` or `[]`, rather than `undefined`, to clear a controlled selection.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import Dropdown from '../_dropdown'
|
|
3
|
+
|
|
4
|
+
const DropdownExternalSearch = () => {
|
|
5
|
+
const [query, setQuery] = useState('')
|
|
6
|
+
const [options, setOptions] = useState([])
|
|
7
|
+
const [loading, setLoading] = useState(false)
|
|
8
|
+
const [selected, setSelected] = useState(null)
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const controller = new AbortController()
|
|
12
|
+
setOptions([])
|
|
13
|
+
setLoading(false)
|
|
14
|
+
if (query.length < 3) return () => controller.abort()
|
|
15
|
+
setLoading(true)
|
|
16
|
+
const timer = setTimeout(() => {
|
|
17
|
+
const params = new URLSearchParams({ q: query, limit: '10', select: 'firstName,lastName' })
|
|
18
|
+
fetch(`https://dummyjson.com/users/search?${params}`, { signal: controller.signal, credentials: 'omit' })
|
|
19
|
+
.then((response) => {
|
|
20
|
+
if (!response.ok) throw new Error('Search failed')
|
|
21
|
+
return response.json()
|
|
22
|
+
})
|
|
23
|
+
.then(({ users }) => {
|
|
24
|
+
if (!controller.signal.aborted) setOptions(users.map((user) => ({
|
|
25
|
+
...user,
|
|
26
|
+
label: `${user.firstName} ${user.lastName}`,
|
|
27
|
+
value: user.id,
|
|
28
|
+
})))
|
|
29
|
+
})
|
|
30
|
+
.catch(() => { if (!controller.signal.aborted) setOptions([]) })
|
|
31
|
+
.finally(() => { if (!controller.signal.aborted) setLoading(false) })
|
|
32
|
+
}, 250)
|
|
33
|
+
return () => { clearTimeout(timer); controller.abort() }
|
|
34
|
+
}, [query])
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<form>
|
|
38
|
+
<Dropdown
|
|
39
|
+
activeStyle={{
|
|
40
|
+
backgroundColor: "bg_light",
|
|
41
|
+
fontColor: "text_lt_default",
|
|
42
|
+
}}
|
|
43
|
+
async
|
|
44
|
+
autocomplete
|
|
45
|
+
label="User"
|
|
46
|
+
loading={loading}
|
|
47
|
+
onInputChange={(text, { reason }) => setQuery(reason === 'input' ? text : '')}
|
|
48
|
+
onSelect={setSelected}
|
|
49
|
+
options={options}
|
|
50
|
+
placeholder="Try Emily or Michael"
|
|
51
|
+
value={selected}
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
</form>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default DropdownExternalSearch
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
`onInputChange(text, { reason })` reports typing immediately with reason `input`, and an empty query with reason `clear` on explicit clear. `activeStyle={{ backgroundColor: "bg_light", fontColor: "text_lt_default" }}` is recommended so the selected row uses a light background and default text color. Selecting an option and prop-driven selection changes do not emit input events. This example stores the selection with `onSelect` and keeps the fetched options, so opening the menu again can show labels that contain the selected label. Clear the application query from `onSelect` only when that list should be dropped.
|
|
2
|
+
|
|
3
|
+
Async mode or opting into `onInputChange` keeps the selected option while typing a nonempty query. Emptying a single-select autocomplete proposes an empty selection through the existing selection callbacks. Emptying a search-bar query or multi-select query preserves the selection. Controlled `value` remains owned by the parent.
|
|
4
|
+
|
|
5
|
+
For externally managed fetching, pass `async`, `options`, and optionally `loading`, without `loadOptions`. Dropdown displays these results without local label filtering. After a single-select autocomplete selection, opening the menu again shows only results whose labels contain the selected label now in the input. The application owns debounce, minimum query length, errors, cancellation, and stale-response protection. When the menu is open, a new result list focuses the first match, so Enter selects it; ArrowDown and ArrowUp still move through the list. This example uses an abortable DummyJSON request. When `loadOptions` is supplied, Dropdown owns the result list instead and focuses the first match the same way.
|
|
6
|
+
|
|
7
|
+
Form libraries reset selection by updating controlled `value`. The existing `ref.clearSelected()` clears an uncontrolled selection and emits the `clear` input reason. React Dropdown does not add native form-reset handling.
|
|
8
|
+
|
|
9
|
+
Without the new props, existing input behavior stays unchanged. In async mode or with `onInputChange`, the clear icon is also available for a nonempty query before an option has been selected.
|
|
@@ -0,0 +1,9 @@
|
|
|
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("dropdown", props: { autocomplete: true, label: "Country", no_options_text: "No matching countries", options: options }) %>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Dropdown from '../../pb_dropdown/_dropdown'
|
|
3
|
+
|
|
4
|
+
const DropdownNoOptionsText = (props) => {
|
|
5
|
+
|
|
6
|
+
const options = [
|
|
7
|
+
{
|
|
8
|
+
label: "United States",
|
|
9
|
+
value: "unitedStates",
|
|
10
|
+
id: "us"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
label: "Canada",
|
|
14
|
+
value: "canada",
|
|
15
|
+
id: "ca"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: "Pakistan",
|
|
19
|
+
value: "pakistan",
|
|
20
|
+
id: "pk"
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Dropdown
|
|
26
|
+
autocomplete
|
|
27
|
+
label="Country"
|
|
28
|
+
noOptionsText="No matching countries"
|
|
29
|
+
options={options}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default DropdownNoOptionsText
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Use `no_options_text` to replace the message shown when there are no options to display. Type a country that is not listed to see it.
|
|
2
|
+
|
|
3
|
+
It applies when filtering matches no options, when an `async` search returns no results, when an async Dropdown is opened before searching, and when the Dropdown renders without options. Without it, Dropdown shows "no option" for filtered options and "No results found" for async searches.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Use `noOptionsText` to replace the message shown when there are no options to display. Type a country that is not listed to see it.
|
|
2
|
+
|
|
3
|
+
It applies when filtering matches no options and when an `async` search returns no results or the Dropdown is opened before searching. Without it, Dropdown shows "no option" for filtered options and "No results found" for async searches.
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
The `customQuickPickDates` prop allows for defining custom quick pick date options for the dropdown. The prop accepts an object with two properties: `dates` and `override`.
|
|
2
2
|
|
|
3
3
|
**The `dates` property** accepts an array of objects. Each object has `label` and `value` properties. The `label` is what will be displayed in the dropdown menu. The `value` property defines the date range that will be selected, and can be:
|
|
4
|
-
- An array of two date strings representing a specific range (e.g., `["06/01/2022", "06/07/2022"]`)
|
|
4
|
+
- An array of two date strings (or `Date` objects) representing a specific range (e.g., `["06/01/2022", "06/07/2022"]`).
|
|
5
|
+
- **Date-only** strings get start-of-day / end-of-day when synced to `enableTime` DatePickers.
|
|
6
|
+
- **Timed** strings use the same formats as Rails QuickPick customs:
|
|
7
|
+
- US `m/d/Y` + time (optional `:SS`, optional `AM`/`PM`) — wall-clock; emit offset-less `YYYY-MM-DDTHH:MM:SS`
|
|
8
|
+
- Offset-less `YYYY-MM-DDTHH:MM:SS` — wall-clock; emit offset-less
|
|
9
|
+
- ISO-8601 with `Z` or numeric offset — absolute instant; emit keeps offset/`Z` so `new Date(iso)` preserves the instant
|
|
10
|
+
- `Date` objects keep their authored time (including intentional midnight / `23:59:00`).
|
|
5
11
|
- An object with `timePeriod` and `amount` properties for dynamic date calculations:
|
|
6
12
|
- The `timePeriod` property accepts `"days"`, `"weeks"`, `"months"`, `"quarters"`, or `"years"`, representing past time periods calculated from today.
|
|
7
13
|
- The `amount` property accepts any number.
|
|
8
14
|
|
|
9
15
|
**The `override` property** is a boolean that controls whether custom dates replace or append to the default quick pick options. Default is `true` (replaces defaults). Set to `false` to append your custom dates to the default quick pick options.
|
|
10
|
-
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
The `custom_quick_pick_dates` prop allows for defining custom quick pick date options for the dropdown. The prop accepts an object with two properties: `dates` and `override`.
|
|
2
2
|
|
|
3
3
|
**The `dates` property** accepts an array of objects. Each object has `label` and `value` properties. The `label` is what will be displayed in the dropdown menu. The `value` property defines the date range that will be selected, and can be:
|
|
4
|
-
- An array of two date strings representing a specific range (e.g., `["06/01/2022", "06/07/2022"]`)
|
|
4
|
+
- An array of two date strings representing a specific range (e.g., `["06/01/2022", "06/07/2022"]`).
|
|
5
|
+
- **Date-only** strings stay date-only on the option payload (SOD/EOD applied at sync when `enable_time`).
|
|
6
|
+
- **Timed** strings use the same formats as React QuickPick customs:
|
|
7
|
+
- US `m/d/Y` + time (optional `:SS`, optional `AM`/`PM`) — wall-clock (e.g. `"06/01/2022 9:30 AM"`); emit offset-less `YYYY-MM-DDTHH:MM:SS`
|
|
8
|
+
- Offset-less `YYYY-MM-DDTHH:MM:SS` — wall-clock (e.g. `"2026-03-08T02:30:00"`); emit offset-less
|
|
9
|
+
- ISO-8601 with `Z` or numeric offset — absolute instant; emit keeps offset/`Z` (e.g. `…+00:00` / `…-05:00`) so JS `new Date(iso)` preserves the same instant
|
|
10
|
+
- Wall-clock and absolute emit differ on purpose: offset-less keeps the authored clock face; offset-bearing strings keep the absolute instant across Rails ↔ React. Unsupported timed free text raises.
|
|
5
11
|
- An object with `time_period` and `amount` properties for dynamic date calculations:
|
|
6
12
|
- The `time_period` property accepts `"days"`, `"weeks"`, `"months"`, `"quarters"`, or `"years"`, representing past time periods calculated from today.
|
|
7
13
|
- The `amount` property accepts any number.
|
|
8
14
|
|
|
9
15
|
**The `override` property** is a boolean that controls whether custom dates replace or append to the default quick pick options. Default is `true` (replaces defaults). Set to `false` to append your custom dates to the default quick pick options.
|
|
10
|
-
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<%= pb_rails("dropdown", props: {
|
|
2
|
+
id: "dropdown-quickpick-enable-time",
|
|
3
|
+
label: "Date Range",
|
|
4
|
+
name: "date_range_enable_time",
|
|
5
|
+
margin_bottom: "sm",
|
|
6
|
+
variant: "quickpick",
|
|
7
|
+
controls_start_id: "start-date-picker-enable-time",
|
|
8
|
+
controls_end_id: "end-date-picker-enable-time",
|
|
9
|
+
start_date_id: "quickpick_enable_time_start_date",
|
|
10
|
+
start_date_name: "start_date_enable_time",
|
|
11
|
+
end_date_id: "quickpick_enable_time_end_date",
|
|
12
|
+
end_date_name: "end_date_enable_time"
|
|
13
|
+
}) %>
|
|
14
|
+
|
|
15
|
+
<%= pb_rails("date_picker", props: {
|
|
16
|
+
picker_id: "start-date-picker-enable-time",
|
|
17
|
+
label: "Start Date",
|
|
18
|
+
name: "start_date_picker_enable_time",
|
|
19
|
+
placeholder: "Select Start Date",
|
|
20
|
+
enable_time: true,
|
|
21
|
+
close_on_select: false,
|
|
22
|
+
sync_start_with: "dropdown-quickpick-enable-time"
|
|
23
|
+
}) %>
|
|
24
|
+
|
|
25
|
+
<%= pb_rails("date_picker", props: {
|
|
26
|
+
picker_id: "end-date-picker-enable-time",
|
|
27
|
+
label: "End Date",
|
|
28
|
+
name: "end_date_picker_enable_time",
|
|
29
|
+
placeholder: "Select End Date",
|
|
30
|
+
enable_time: true,
|
|
31
|
+
close_on_select: false,
|
|
32
|
+
sync_end_with: "dropdown-quickpick-enable-time"
|
|
33
|
+
}) %>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Dropdown from "../../pb_dropdown/_dropdown";
|
|
3
|
+
import DatePicker from "../../pb_date_picker/_date_picker";
|
|
4
|
+
|
|
5
|
+
const DropdownQuickpickEnableTime = (props) => {
|
|
6
|
+
return (
|
|
7
|
+
<>
|
|
8
|
+
<Dropdown
|
|
9
|
+
{...props}
|
|
10
|
+
controlsEndId="end-date-picker-enable-time"
|
|
11
|
+
controlsStartId="start-date-picker-enable-time"
|
|
12
|
+
id="dropdown-quickpick-enable-time"
|
|
13
|
+
label="Range"
|
|
14
|
+
marginBottom="sm"
|
|
15
|
+
placeholder="Select a Date Range"
|
|
16
|
+
variant="quickpick"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<DatePicker
|
|
20
|
+
{...props}
|
|
21
|
+
enableTime
|
|
22
|
+
initializeOnce
|
|
23
|
+
label="Start Date"
|
|
24
|
+
pickerId="start-date-picker-enable-time"
|
|
25
|
+
placeholder="Select a Start Date"
|
|
26
|
+
syncStartWith="dropdown-quickpick-enable-time"
|
|
27
|
+
/>
|
|
28
|
+
|
|
29
|
+
<DatePicker
|
|
30
|
+
{...props}
|
|
31
|
+
enableTime
|
|
32
|
+
initializeOnce
|
|
33
|
+
label="End Date"
|
|
34
|
+
pickerId="end-date-picker-enable-time"
|
|
35
|
+
placeholder="Select an End Date"
|
|
36
|
+
syncEndWith="dropdown-quickpick-enable-time"
|
|
37
|
+
/>
|
|
38
|
+
</>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default DropdownQuickpickEnableTime;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
When linked DatePickers use `enable_time` / `enableTime`, QuickPick presets that do not include an explicit time apply **start of day** (12:00 AM) to the start picker and **end of day** (11:59 PM) to the end picker. That matches DatePicker minute precision and inclusive datetime filters for ranges like Today and Yesterday.
|
|
2
|
+
|
|
3
|
+
**Supported timed custom formats (Rails and React — same contract):**
|
|
4
|
+
|
|
5
|
+
| Format | Example | Semantics |
|
|
6
|
+
|--------|---------|-----------|
|
|
7
|
+
| US `m/d/Y` + time (optional `:SS`, optional `AM`/`PM`) | `"06/01/2022 9:30 AM"` | Wall-clock — literal hour/minute/second labels |
|
|
8
|
+
| Offset-less ISO datetime (`T` separator) | `"2026-03-08T02:30:00"` | Wall-clock — same as above (not server-TZ) |
|
|
9
|
+
| ISO-8601 with `Z` or numeric offset | `"2026-03-08T07:30:00Z"`, `"2026-03-08T02:30:00-05:00"` | Absolute instant |
|
|
10
|
+
|
|
11
|
+
**Emit (Rails and React — same contract):**
|
|
12
|
+
|
|
13
|
+
| Input kind | Emit |
|
|
14
|
+
|------------|------|
|
|
15
|
+
| Wall-clock (US `m/d/Y` + time, or offset-less `YYYY-MM-DDTHH:MM:SS`) | Offset-less `YYYY-MM-DDTHH:MM:SS` — authored clock face |
|
|
16
|
+
| Absolute (`Z` or numeric offset) | Keeps offset / `Z` (e.g. `…Z` or `…±HH:MM`) so `parseQuickPickDateTime` / `new Date(iso)` preserve the same instant |
|
|
17
|
+
|
|
18
|
+
React also accepts `Date` objects (times kept as authored, including intentional midnight / `23:59:00`).
|
|
19
|
+
|
|
20
|
+
Date-only custom strings (e.g. `"06/01/2022"`) still get SOD/EOD at sync when `enableTime` is on. Other free-text / undocumented timed strings are not supported.
|
|
21
|
+
|
|
22
|
+
On Rails, wall-clock customs go to the option payload as offset-less datetimes; absolute customs keep their offset so linked `enableTime` DatePickers sync to the correct instant (not a misread local wall-clock).
|
|
23
|
+
|
|
24
|
+
Alternate configuration: QuickPick + Time can also be handled by a Date-only QuickPick (Dropdown QuickPick + two DatePickers without time selection) paired with two TimePickers for start + end time selection in the same form.
|