playbook_ui 14.8.0.pre.alpha.PLAY1598floatinguiupgrade4563 → 14.8.0.pre.alpha.PLAY1598floatinguiupgrade4617

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -0
  3. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +2 -2
  4. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +0 -4
  5. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +84 -7
  6. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +1 -0
  7. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.html.erb +0 -1
  8. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +0 -4
  9. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +2 -2
  10. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +1 -1
  11. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +63 -12
  12. data/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_default.html.erb +2 -1
  13. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.html.erb +47 -0
  14. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.jsx +59 -0
  15. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +3 -0
  16. data/app/pb_kits/playbook/pb_typeahead/index.ts +29 -3
  17. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +5 -2
  18. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
  19. data/app/pb_kits/playbook/tokens/_height.scss +19 -0
  20. data/app/pb_kits/playbook/tokens/exports/_height.module.scss +37 -0
  21. data/app/pb_kits/playbook/utilities/_height.scss +33 -0
  22. data/app/pb_kits/playbook/utilities/_max_width.scss +4 -0
  23. data/app/pb_kits/playbook/utilities/_min_width.scss +1 -1
  24. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +0 -1
  25. data/app/pb_kits/playbook/utilities/globalProps.ts +24 -0
  26. data/dist/chunks/{_typeahead-DUfnbpvK.js → _typeahead-q6dSnFmE.js} +2 -2
  27. data/dist/chunks/{_weekday_stacked-CFiZk2PV.js → _weekday_stacked-BF7UZO3j.js} +1 -1
  28. data/dist/chunks/{lib-BC6ESsxG.js → lib-SyD3buPZ.js} +1 -1
  29. data/dist/chunks/{pb_form_validation-B_Z9rEbg.js → pb_form_validation-Dt8UJgrJ.js} +1 -1
  30. data/dist/chunks/vendor.js +1 -1
  31. data/dist/playbook-doc.js +1 -1
  32. data/dist/playbook-rails-react-bindings.js +1 -1
  33. data/dist/playbook-rails.js +1 -1
  34. data/dist/playbook.css +1 -1
  35. data/lib/playbook/classnames.rb +3 -0
  36. data/lib/playbook/forms/builder/typeahead_field.rb +13 -0
  37. data/lib/playbook/height.rb +29 -0
  38. data/lib/playbook/kit_base.rb +16 -1
  39. data/lib/playbook/max_height.rb +29 -0
  40. data/lib/playbook/min_height.rb +29 -0
  41. data/lib/playbook/version.rb +1 -1
  42. metadata +12 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9353b3b402295905df0220cec1a5c043cdfaf017635cfb8e53887a581f1932ca
4
- data.tar.gz: d68b282ac69c6ea7d57e48b7aaee56682c0247fff6ca297c54e1a8c70cf2a7d3
3
+ metadata.gz: 3ed8f704052a7acdd9d76714b8702cc95ebcc2cd273bba9de5d0dec1053f1d7c
4
+ data.tar.gz: 46073a3b2fccec41e50abfd388306382c330ebe6a7cd4e8ae98217fd56fb4476
5
5
  SHA512:
6
- metadata.gz: d32c0d53ed43c2f064913f1a73499f8652bb1c13a388646721590fd9cd8c0f57893c912b9d0d1530e3bf28b779567a260e69a66046847186b1e4a4a9a6e0613f
7
- data.tar.gz: 1432f779bd804e750f0cee0e9b4fe1adcf01ba489c9a1da7f3495684320cb8cdb2f7d2755316c69f4548404fc1106eea396d4c0a1e2835a3cab970816bb0e904
6
+ metadata.gz: 5a945e9e445453e8ce8dd27d2466e9524be36652ee2b1246d644a5b26fbd2863fefbb1cea67212e84b81bd3aad6fe76dde306b00c6cd55bcc1f2ab31807f7700
7
+ data.tar.gz: fe0ea2f55ccc17a0bfdf50233919636b2b3794932ac026e51ac16f8a1ee13a19d2a44887208406f5e341d24db0383cf63c7a9c0a495091edd150507a9b2c79e2
@@ -126,3 +126,4 @@
126
126
  @import 'utilities/overflow';
127
127
  @import 'utilities/truncate';
128
128
  @import 'utilities/vertical_align';
129
+ @import 'utilities/height';
@@ -6,12 +6,12 @@
6
6
  ) do %>
7
7
  <%= content.presence || object.input %>
8
8
  <% if object.indeterminate %>
9
- <span class="pb_checkbox_indeterminate">
9
+ <span data-pb-checkbox-icon-span="true" class="pb_checkbox_indeterminate">
10
10
  <%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon", fixed_width: true}) %>
11
11
  <%= pb_rails("icon", props: { icon: "check", classname: "check_icon hidden", fixed_width: true}) %>
12
12
  </span>
13
13
  <% else %>
14
- <span class="pb_checkbox_checkmark">
14
+ <span data-pb-checkbox-icon-span="true" class="pb_checkbox_checkmark">
15
15
  <%= pb_rails("icon", props: { icon: "check", classname: "check_icon", fixed_width: true}) %>
16
16
  <%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon hidden", fixed_width: true}) %>
17
17
  </span>
@@ -18,10 +18,6 @@ module Playbook
18
18
  prop :form_spacing, type: Playbook::Props::Boolean,
19
19
  default: false
20
20
 
21
- def checked_html
22
- checked ? "checked='true'" : nil
23
- end
24
-
25
21
  def classname
26
22
  generate_classname("pb_checkbox_kit", checked_class) + indeterminate_class + error_class
27
23
  end
@@ -1,7 +1,84 @@
1
- <%= pb_rails("checkbox" , props: {
2
- text: "Select ",
3
- value: "checkbox-value",
4
- name: "main",
5
- indeterminate: true,
6
- id: "test-indeterminate-js"
7
- }) %>
1
+ <% checkboxes = [
2
+ { name: 'Coffee', id: 'coffee', checked: false },
3
+ { name: 'Ice Cream', id: 'ice-cream', checked: false },
4
+ { name: 'Chocolate', id: 'chocolate', checked: true }
5
+ ] %>
6
+
7
+ <%= pb_rails("table", props: { container: false, size: "md" }) do %>
8
+ <thead>
9
+ <tr>
10
+ <th>
11
+ <%= pb_rails("checkbox", props: {
12
+ checked: true,
13
+ text: "Uncheck All",
14
+ value: "checkbox-value",
15
+ name: "main-checkbox",
16
+ indeterminate: true,
17
+ id: "indeterminate-checkbox"
18
+ }) %>
19
+ </th>
20
+ </tr>
21
+ </thead>
22
+
23
+ <tbody>
24
+ <% checkboxes.each do |checkbox| %>
25
+ <tr>
26
+ <td>
27
+ <%= pb_rails("checkbox", props: {
28
+ checked: checkbox[:checked],
29
+ text: checkbox[:name],
30
+ value: checkbox[:id],
31
+ name: "#{checkbox[:id]}-indeterminate-checkbox",
32
+ id: "#{checkbox[:id]}-indeterminate-checkbox",
33
+ }) %>
34
+ </td>
35
+ </tr>
36
+ <% end %>
37
+ </tbody>
38
+ <% end %>
39
+
40
+ <script>
41
+ document.addEventListener('DOMContentLoaded', function() {
42
+ const mainCheckboxWrapper = document.getElementById('indeterminate-checkbox');
43
+ const mainCheckbox = document.getElementsByName("main-checkbox")[0];
44
+ const childCheckboxes = document.querySelectorAll('input[type="checkbox"][id$="indeterminate-checkbox"]');
45
+
46
+ const updateMainCheckbox = () => {
47
+ // Count the number of checked child checkboxes
48
+ const checkedCount = Array.from(childCheckboxes).filter(cb => cb.checked).length;
49
+ // Determine if the main checkbox should be in an indeterminate state
50
+ const indeterminate = checkedCount > 0 && checkedCount < childCheckboxes.length;
51
+
52
+ // Set the main checkbox states
53
+ mainCheckbox.indeterminate = indeterminate;
54
+ mainCheckbox.checked = checkedCount > 0;
55
+
56
+ // Determine the main checkbox label based on the number of checked checkboxes
57
+ const text = checkedCount === 0 ? 'Check All' : 'Uncheck All';
58
+
59
+ // Determine the icon class to add and remove based on the number of checked checkboxes
60
+ const iconClassToAdd = checkedCount === 0 ? 'pb_checkbox_checkmark' : 'pb_checkbox_indeterminate';
61
+ const iconClassToRemove = checkedCount === 0 ? 'pb_checkbox_indeterminate' : 'pb_checkbox_checkmark';
62
+
63
+ // Update main checkbox label
64
+ mainCheckboxWrapper.getElementsByClassName('pb_body_kit')[0].textContent = text;
65
+
66
+ // Add and remove the icon class to the main checkbox wrapper
67
+ mainCheckboxWrapper.querySelector('[data-pb-checkbox-icon-span]').classList.add(iconClassToAdd);
68
+ mainCheckboxWrapper.querySelector('[data-pb-checkbox-icon-span]').classList.remove(iconClassToRemove);
69
+
70
+ // Toggle the visibility of the checkbox icon based on the indeterminate state
71
+ mainCheckboxWrapper.getElementsByClassName("indeterminate_icon")[0].classList.toggle('hidden', !indeterminate);
72
+ mainCheckboxWrapper.getElementsByClassName("check_icon")[0].classList.toggle('hidden', indeterminate);
73
+ };
74
+
75
+ mainCheckbox.addEventListener('change', function() {
76
+ childCheckboxes.forEach(cb => cb.checked = this.checked);
77
+ updateMainCheckbox();
78
+ });
79
+
80
+ childCheckboxes.forEach(cb => {
81
+ cb.addEventListener('change', updateMainCheckbox);
82
+ });
83
+ });
84
+ </script>
@@ -53,6 +53,7 @@
53
53
  }
54
54
 
55
55
  .pb_dropdown_container {
56
+ position: absolute;
56
57
  background-color: $white;
57
58
  overflow: hidden;
58
59
  box-shadow: $shadow_deep;
@@ -3,7 +3,6 @@
3
3
  class: object.classname,
4
4
  data: object.data,
5
5
  id: object.id,
6
- style: object.container_style,
7
6
  **combined_html_options) do %>
8
7
  <%= pb_rails("list", props: {ordered: false, borderless: false}) do %>
9
8
  <% if content.present? %>
@@ -7,10 +7,6 @@ module Playbook
7
7
  generate_classname("pb_dropdown_container", "close", separator: " ")
8
8
  end
9
9
 
10
- def container_style
11
- "position: absolute"
12
- end
13
-
14
10
  def data
15
11
  Hash(prop(:data)).merge(dropdown_container: true)
16
12
  end
@@ -23,7 +23,7 @@
23
23
  %>
24
24
 
25
25
  <%= pb_form_with(scope: :example, url: "", method: :get) do |form| %>
26
- <%= form.typeahead :example_user, props: { data: { typeahead_example1: true, user: {} }, placeholder: "Search for a user" } %>
26
+ <%= form.typeahead :example_typeahead, props: { data: { typeahead_example1: true, user: {} }, label: true, placeholder: "Search for a user" } %>
27
27
  <%= form.text_field :example_text_field, props: { label: true } %>
28
28
  <%= form.phone_number_field :example_phone_number_field, props: { label: "Example phone field" } %>
29
29
  <%= form.email_field :example_email_field, props: { label: true } %>
@@ -92,7 +92,7 @@
92
92
  const selectedUserData = JSON.parse(selectedUserJSON)
93
93
 
94
94
  // set the input field's value
95
- event.target.querySelector('input[name=example_user]').value = selectedUserData.login
95
+ event.target.querySelector('input[name=example_typeahead]').value = selectedUserData.login
96
96
 
97
97
  // log the selected option's dataset
98
98
  console.log('The selected user data:')
@@ -1,5 +1,5 @@
1
1
  <%= pb_form_with(scope: :example, url: "", method: :get, loading: true) do |form| %>
2
- <%= form.text_field :example_text_field, props: { label: true } %>
2
+ <%= form.text_field :example_text_field_loading, props: { label: true } %>
3
3
 
4
4
  <%= form.actions do |action| %>
5
5
  <%= action.submit %>
@@ -22,23 +22,74 @@
22
22
  %>
23
23
 
24
24
  <%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
25
- <%= form.text_field :example_text_field, props: { label: true, required: true } %>
26
- <%= form.phone_number_field :example_phone_number_field, props: { label: "Example phone field" } %>
27
- <%= form.email_field :example_email_field, props: { label: true, required: true } %>
28
- <%= form.number_field :example_number_field, props: { label: true, required: true } %>
29
- <%= form.search_field :example_project_number, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
30
- <%= form.password_field :example_password_field, props: { label: true, required: true } %>
31
- <%= form.url_field :example_url_field, props: { label: true, required: true } %>
32
- <%= form.text_area :example_text_area, props: { label: true, required: true } %>
33
- <%= form.dropdown_field :example_dropdown, props: { label: true, options: example_dropdown_options, required: true } %>
34
- <%= form.select :example_select, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true } %>
35
- <%= form.collection_select :example_collection_select, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
25
+ <%= form.typeahead :example_typeahead_validation, props: { data: { typeahead_example2: true, user: {} }, label: true, placeholder: "Search for a user", required: true, validation: { message: "Please select a user." } } %>
26
+ <%= form.text_field :example_text_field_validation, props: { label: true, required: true } %>
27
+ <%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field" } %>
28
+ <%= form.email_field :example_email_field_validation, props: { label: true, required: true } %>
29
+ <%= form.number_field :example_number_field_validation, props: { label: true, required: true } %>
30
+ <%= form.search_field :example_project_number_validation, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
31
+ <%= form.password_field :example_password_field_validation, props: { label: true, required: true } %>
32
+ <%= form.url_field :example_url_field_validation, props: { label: true, required: true } %>
33
+ <%= form.text_area :example_text_area_validation, props: { label: true, required: true } %>
34
+ <%= form.dropdown_field :example_dropdown_validation, props: { label: true, options: example_dropdown_options, required: true } %>
35
+ <%= form.select :example_select_validation, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true } %>
36
+ <%= form.collection_select :example_collection_select_validation, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
36
37
  <%= form.check_box :example_checkbox, props: { text: "Example Checkbox", label: true, required: true } %>
37
38
  <%= form.date_picker :example_date_picker_2, props: { label: true, required: true } %>
38
- <%= form.star_rating_field :example_star_rating, props: { variant: "interactive", label: true, required: true } %>
39
+ <%= form.star_rating_field :example_star_rating_validation, props: { variant: "interactive", label: true, required: true } %>
39
40
 
40
41
  <%= form.actions do |action| %>
41
42
  <%= action.submit %>
42
43
  <%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
43
44
  <% end %>
44
45
  <% end %>
46
+
47
+ <!-- form.typeahead user results example template -->
48
+ <template data-typeahead-example-result-option>
49
+ <%= pb_rails("user", props: {
50
+ name: tag(:slot, name: "name"),
51
+ orientation: "horizontal",
52
+ align: "left",
53
+ avatar_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII=",
54
+ avatar: true
55
+ }) %>
56
+ </template>
57
+
58
+ <!-- form.typeahead JS example implementation -->
59
+ <%= javascript_tag defer: "defer" do %>
60
+ document.addEventListener("pb-typeahead-kit-search", function(event) {
61
+ if (!event.target.dataset || !event.target.dataset.typeaheadExample2) return
62
+
63
+ fetch(`https://api.github.com/search/users?q=${encodeURIComponent(event.detail.searchingFor)}`)
64
+ .then(response => response.json())
65
+ .then((result) => {
66
+ const resultOptionTemplate = document.querySelector("[data-typeahead-example-result-option]")
67
+
68
+ event.detail.setResults((result.items || []).map((user) => {
69
+ const wrapper = resultOptionTemplate.content.cloneNode(true)
70
+ wrapper.children[0].dataset.user = JSON.stringify(user)
71
+ wrapper.querySelector('slot[name="name"]').replaceWith(user.login)
72
+ wrapper.querySelector('img').dataset.src = user.avatar_url
73
+ return wrapper
74
+ }))
75
+ })
76
+ })
77
+
78
+
79
+ document.addEventListener("pb-typeahead-kit-result-option-selected", function(event) {
80
+ if (!event.target.dataset.typeaheadExample2) return
81
+
82
+ const selectedUserJSON = event.detail.selected.firstElementChild.dataset.user
83
+ const selectedUserData = JSON.parse(selectedUserJSON)
84
+
85
+ // set the input field's value
86
+ event.target.querySelector('input[name=example_typeahead_validation]').value = selectedUserData.login
87
+
88
+ // log the selected option's dataset
89
+ console.log('The selected user data:')
90
+ console.dir(selectedUserData)
91
+
92
+ // do even more with the data later - TBD
93
+ event.target.dataset.user = selectedUserJSON
94
+ })
95
+ <% end %>
@@ -6,6 +6,7 @@
6
6
  value: "selected_with_icon",
7
7
  checked: true,
8
8
  icon: true,
9
+
9
10
  }) do %>
10
11
  Selected, with icon
11
12
  <% end %>
@@ -36,4 +37,4 @@
36
37
  Disabled
37
38
  <% end %>
38
39
 
39
- </div>
40
+ </div>
@@ -41,3 +41,50 @@
41
41
  }) %>
42
42
  <% end %>
43
43
  <% end %>
44
+
45
+ <br /><br /><br />
46
+
47
+ <%= pb_rails("timeline", props: {orientation: "vertical", show_date: true}) do %>
48
+ <%= pb_rails("timeline/item") do |item| %>
49
+
50
+ <% item.label do %>
51
+ <%= pb_rails("timeline/label") do %>
52
+ <%= pb_rails("title", props: { text: "Any Kit", size: 4 }) %>
53
+ <% end %>
54
+ <% end %>
55
+
56
+ <% item.step do %>
57
+ <%= pb_rails("timeline/step", props: { icon: 'user', icon_color: 'royal' }) %>
58
+ <% end %>
59
+
60
+ <% item.detail do %>
61
+ <%= pb_rails("title_detail", props: {
62
+ title: "Jackson Heights",
63
+ detail: "37-27 74th Street"
64
+ }) %>
65
+ <% end %>
66
+ <% end %>
67
+
68
+ <%= pb_rails("timeline/item", props: {icon: "map-marker-alt", icon_color: "purple", date: Date.today+1, line_style: "dotted" }) do |item| %>
69
+ <%= pb_rails("title_detail", props: {
70
+ title: "Society Hill",
71
+ detail: "72 E St Astoria"
72
+ }) %>
73
+ <% end %>
74
+
75
+ <%= pb_rails("timeline/item") do |item| %>
76
+
77
+ <% item.step do %>
78
+ <%= pb_rails("timeline/step") do %>
79
+ <%= pb_rails("pill", props: { text: "3" , variant: "success" }) %>
80
+ <% end %>
81
+ <% end %>
82
+
83
+ <% item.detail do %>
84
+ <%= pb_rails("title_detail", props: {
85
+ title: "Greenpoint",
86
+ detail: "81 Gate St Brooklyn"
87
+ }) %>
88
+ <% end %>
89
+ <% end %>
90
+ <% end %>
@@ -62,6 +62,65 @@ const TimelineWithChildren = (props) => (
62
62
  </Timeline.Detail>
63
63
  </Timeline.Item>
64
64
  </Timeline>
65
+
66
+ <br />
67
+ <br />
68
+ <br />
69
+
70
+ <Timeline orientation="vertical"
71
+ showDate
72
+ {...props}
73
+ >
74
+ <Timeline.Item lineStyle="solid"
75
+ {...props}
76
+ >
77
+ <Timeline.Label>
78
+ <Title size={4}
79
+ text='Any Kit'
80
+ />
81
+ </Timeline.Label>
82
+ <Timeline.Step icon="user"
83
+ iconColor="royal"
84
+ />
85
+ <Timeline.Detail>
86
+ <TitleDetail detail="37-27 74th Street"
87
+ title="Jackson Heights"
88
+ {...props}
89
+ />
90
+ </Timeline.Detail>
91
+ </Timeline.Item>
92
+
93
+ <Timeline.Item lineStyle="dotted"
94
+ {...props}
95
+ >
96
+ <Timeline.Label date={new Date(new Date().setDate(new Date().getDate() + 1))} />
97
+ <Timeline.Step icon="map-marker-alt"
98
+ iconColor="purple"
99
+ />
100
+ <Timeline.Detail>
101
+ <TitleDetail detail="72 E St Astoria"
102
+ title="Society Hill"
103
+ {...props}
104
+ />
105
+ </Timeline.Detail>
106
+ </Timeline.Item>
107
+
108
+ <Timeline.Item lineStyle="solid"
109
+ {...props}
110
+ >
111
+ <Timeline.Step>
112
+ <Pill text="3"
113
+ variant="success"
114
+ />
115
+ </Timeline.Step>
116
+ <Timeline.Detail>
117
+ <TitleDetail detail="81 Gate St Brooklyn"
118
+ title="Greenpoint"
119
+ {...props}
120
+ />
121
+ </Timeline.Detail>
122
+ </Timeline.Item>
123
+ </Timeline>
65
124
  </div>
66
125
  )
67
126
 
@@ -2,6 +2,7 @@
2
2
  @import "../tokens/border_radius";
3
3
  @import "../tokens/spacing";
4
4
  @import "../tokens/shadows";
5
+ @import "../tokens/positioning";
5
6
 
6
7
  [class^=pb_typeahead_kit] {
7
8
  .typeahead-kit-select__option {
@@ -99,6 +100,7 @@
99
100
  .typeahead-kit-select__menu {
100
101
  background-color: $bg_dark;
101
102
  color: $white;
103
+ z-index: $z_1;
102
104
  }
103
105
  .typeahead-kit-select__option:hover {
104
106
  background-color: $active_dark;
@@ -182,6 +184,7 @@
182
184
  }
183
185
 
184
186
  .typeahead-kit-select__menu {
187
+ z-index: $z_1;
185
188
  .typeahead-kit-select__menu-list {
186
189
  padding: 0;
187
190
  }
@@ -4,11 +4,12 @@ import { debounce } from 'lodash'
4
4
  export default class PbTypeahead extends PbEnhancedElement {
5
5
  _searchInput: HTMLInputElement
6
6
  _resultsElement: HTMLElement
7
- _debouncedSearch: Function
7
+ _debouncedSearch: () => void
8
8
  _resultsLoadingIndicator: HTMLElement
9
9
  _resultOptionTemplate: HTMLElement
10
10
  _resultsOptionCache: Map<string, Array<DocumentFragment>>
11
11
  _searchContext: string
12
+ _validSelection: boolean
12
13
 
13
14
  static get selector() {
14
15
  return '[data-pb-typeahead-kit]'
@@ -86,6 +87,9 @@ export default class PbTypeahead extends PbEnhancedElement {
86
87
  const resultOption = (event.target as Element).closest('[data-result-option-item]')
87
88
  if (!resultOption) return
88
89
 
90
+ this._validSelection = true
91
+ this.removeValidationError()
92
+
89
93
  this.resultsCacheClear()
90
94
  this.searchInputClear()
91
95
  this.clearResults()
@@ -93,6 +97,28 @@ export default class PbTypeahead extends PbEnhancedElement {
93
97
  this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-result-option-selected', { bubbles: true, detail: { selected: resultOption, typeahead: this } }))
94
98
  }
95
99
 
100
+ removeValidationError() {
101
+ const inputWrapper = this.searchInput.closest('.text_input_wrapper')
102
+ if (inputWrapper) {
103
+ const errorMessage = inputWrapper.querySelector('.pb_body_kit_negative') as HTMLElement
104
+ if (errorMessage) {
105
+ errorMessage.style.display = 'none'
106
+ }
107
+ this.searchInput.classList.remove('error')
108
+ }
109
+ }
110
+
111
+ showValidationError() {
112
+ const inputWrapper = this.searchInput.closest('.text_input_wrapper')
113
+ if (inputWrapper) {
114
+ const errorMessage = inputWrapper.querySelector('.pb_body_kit_negative') as HTMLElement
115
+ if (errorMessage) {
116
+ errorMessage.style.display = 'block'
117
+ }
118
+ this.searchInput.classList.add('error')
119
+ }
120
+ }
121
+
96
122
  clearResults() {
97
123
  this.resultsElement.innerHTML = ''
98
124
  }
@@ -201,8 +227,8 @@ export default class PbTypeahead extends PbEnhancedElement {
201
227
  }
202
228
 
203
229
  toggleResultsLoadingIndicator(visible: boolean) {
204
- var visibilityProperty = '0'
230
+ let visibilityProperty = '0'
205
231
  if (visible) visibilityProperty = '1'
206
232
  this.resultsLoadingIndicator.style.opacity = visibilityProperty
207
233
  }
208
- }
234
+ }
@@ -17,11 +17,14 @@
17
17
  <%= pb_rails("text_input", props: {
18
18
  type: "search",
19
19
  input_options: object.input_options,
20
- label: object.label,
21
20
  name: object.name,
22
21
  value: object.value,
23
22
  placeholder: object.placeholder,
24
23
  margin_bottom: "none",
24
+ required: object.required,
25
+ validation: object.validation,
26
+ label: object.label,
27
+ id: object.input_options[:id],
25
28
  }) %>
26
29
  <%= pb_rails("list", props: { ordered: false, borderless: false, xpadding: true, role: "status", aria: { live: "polite" }, data: { pb_typeahead_kit_results: true } }) do %>
27
30
  <% end %>
@@ -33,4 +36,4 @@
33
36
  <% end %>
34
37
  </template>
35
38
  <% end %>
36
- <% end %>
39
+ <% end %>
@@ -40,6 +40,10 @@ module Playbook
40
40
  prop :pill_color, type: Playbook::Props::Enum,
41
41
  values: %w[primary neutral success warning error info data_1 data_2 data_3 data_4 data_5 data_6 data_7 data_8 windows siding roofing doors gutters solar insulation accessories],
42
42
  default: "primary"
43
+ prop :required, type: Playbook::Props::Boolean,
44
+ default: false
45
+ prop :validation, type: Playbook::Props::HashProp,
46
+ default: {}
43
47
 
44
48
  def classname
45
49
  default_margin_bottom = margin_bottom.present? ? "" : " mb_sm"
@@ -0,0 +1,19 @@
1
+
2
+ $height_auto: auto !default;
3
+ $height_xs: 320px !default;
4
+ $height_sm: 480px !default;
5
+ $height_md: 768px !default;
6
+ $height_lg: 1024px !default;
7
+ $height_xl: 1280px !default;
8
+ $height_2xl: 1440px !default;
9
+ $height_3xl: 1920px !default;
10
+ $heights: (
11
+ height_auto: $height_auto,
12
+ height_xs: $height_xs,
13
+ height_sm: $height_sm,
14
+ height_md: $height_md,
15
+ height_lg: $height_lg,
16
+ height_xl: $height_xl,
17
+ height_xxl: $height_2xl,
18
+ height_xxxl: $height_3xl
19
+ );
@@ -0,0 +1,37 @@
1
+ @import "../height";
2
+
3
+ :export {
4
+ @mixin export_height($height_list) {
5
+ @each $name, $value in $height_list {
6
+ .#{$name} {
7
+ height: $value;
8
+ }
9
+ }
10
+ }
11
+
12
+ @include export_height($heights);
13
+ }
14
+
15
+ :export {
16
+ @mixin export_max_height($height_list) {
17
+ @each $name, $value in $height_list {
18
+ .max_#{$name} {
19
+ max-height: $value;
20
+ }
21
+ }
22
+ }
23
+
24
+ @include export_max_height($heights);
25
+ }
26
+
27
+ :export {
28
+ @mixin export_min_height($height_list) {
29
+ @each $name, $value in $height_list {
30
+ .min_#{$name} {
31
+ min-height: $value;
32
+ }
33
+ }
34
+ }
35
+
36
+ @include export_min_height($heights);
37
+ }
@@ -0,0 +1,33 @@
1
+ @import "../tokens/exports/height.module";
2
+
3
+ @mixin export_height($height_list) {
4
+ @each $name, $value in $height_list {
5
+ .#{$name} {
6
+ height: $value;
7
+ }
8
+ }
9
+ }
10
+
11
+ @mixin export_max_height($height_list) {
12
+ @each $name, $value in $height_list {
13
+ .max_#{$name} {
14
+ max-height: $value;
15
+ }
16
+ }
17
+ }
18
+
19
+ @mixin export_min_height($height_list) {
20
+ @each $name, $value in $height_list {
21
+ .min_#{$name} {
22
+ min-height: $value;
23
+ }
24
+ }
25
+ }
26
+
27
+ @include export_height($heights);
28
+ @include export_max_height($heights);
29
+ @include export_min_height($heights);
30
+
31
+ .height-resize {
32
+ resize: vertical;
33
+ }
@@ -27,3 +27,7 @@
27
27
  .max_width_xxl {
28
28
  max-width: 1320px;
29
29
  }
30
+
31
+ .width-resize {
32
+ resize: horizontal;
33
+ }
@@ -40,6 +40,6 @@
40
40
  min-width: $container_100;
41
41
  }
42
42
 
43
- .minwidth-resize {
43
+ .width-resize {
44
44
  resize: horizontal;
45
45
  }
@@ -7,7 +7,6 @@ export default [
7
7
  "right",
8
8
  "top",
9
9
  "hover",
10
- "groupHover",
11
10
  "zIndex",
12
11
  "verticalAlign",
13
12
  "truncate",