playbook_ui 14.16.0.pre.rc.6 → 14.17.0.pre.alpha.PBNTR920emojipickerpoc7130

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Utilities/types.ts +1 -1
  3. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +104 -2
  4. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +168 -85
  5. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +2 -2
  6. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +10 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +20 -7
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_fullscreen.jsx +90 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_fullscreen.md +3 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination.jsx +0 -1
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows.html.erb +39 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_no_subrows.html.erb +33 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_no_subrows_rails.md +1 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_rails.md +6 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +4 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  17. data/app/pb_kits/playbook/pb_advanced_table/index.js +1 -1
  18. data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +4 -2
  19. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +19 -9
  20. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +38 -1
  21. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +49 -37
  22. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +39 -0
  23. data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +1 -1
  24. data/app/pb_kits/playbook/pb_button/_button.scss +5 -5
  25. data/app/pb_kits/playbook/pb_circle_chart/_circle_chart.tsx +1 -1
  26. data/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap +14 -7
  27. data/app/pb_kits/playbook/pb_contact/contact.test.js +7 -7
  28. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +34 -34
  29. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +2 -2
  30. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +16 -0
  31. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.html.erb +0 -11
  32. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.jsx +0 -7
  33. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_inline_styles.scss +28 -24
  34. data/app/pb_kits/playbook/pb_date_range_inline/date_range_inline.test.js +2 -2
  35. data/app/pb_kits/playbook/pb_date_range_stacked/date_range_stacked.test.js +1 -1
  36. data/app/pb_kits/playbook/pb_draggable/_draggable.scss +115 -5
  37. data/app/pb_kits/playbook/pb_draggable/_draggable.tsx +6 -4
  38. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +91 -24
  39. data/app/pb_kits/playbook/pb_draggable/context/types.ts +35 -28
  40. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones.jsx +184 -0
  41. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones.md +5 -0
  42. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones_colors.jsx +97 -0
  43. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_drop_zones_colors.md +1 -0
  44. data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +3 -3
  45. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableContainer.tsx +11 -2
  46. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableItem.tsx +65 -6
  47. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.html.erb +3 -0
  48. data/app/pb_kits/playbook/pb_drawer/docs/example.yml +1 -0
  49. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete.jsx +6 -6
  50. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete_and_custom_display.jsx +6 -6
  51. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.jsx +6 -6
  52. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.html.erb +8 -8
  53. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +3 -3
  54. data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.tsx +3 -4
  55. data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +2 -2
  56. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.scss +2 -2
  57. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +67 -0
  58. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +67 -0
  59. data/app/pb_kits/playbook/pb_form/pb_form_validation.js +1 -1
  60. data/app/pb_kits/playbook/pb_form_group/_form_group.scss +22 -0
  61. data/app/pb_kits/playbook/pb_gauge/_gauge.tsx +1 -1
  62. data/app/pb_kits/playbook/pb_icon/_icon.scss +8 -1
  63. data/app/pb_kits/playbook/pb_icon/docs/_icon_color.html.erb +10 -4
  64. data/app/pb_kits/playbook/pb_icon/docs/_icon_color.jsx +49 -24
  65. data/app/pb_kits/playbook/pb_icon/icon.test.js +9 -9
  66. data/app/pb_kits/playbook/pb_icon_button/_icon_button.tsx +4 -1
  67. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_click.jsx +13 -0
  68. data/app/pb_kits/playbook/pb_icon_button/docs/example.yml +1 -0
  69. data/app/pb_kits/playbook/pb_icon_button/docs/index.js +1 -0
  70. data/app/pb_kits/playbook/pb_icon_circle/icon_circle.test.js +1 -1
  71. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.test.js +1 -1
  72. data/app/pb_kits/playbook/pb_icon_value/icon_value.test.js +1 -1
  73. data/app/pb_kits/playbook/pb_label_value/label_value.test.js +1 -1
  74. data/app/pb_kits/playbook/pb_layout/_layout.scss +58 -0
  75. data/app/pb_kits/playbook/pb_layout/_layout.tsx +20 -7
  76. data/app/pb_kits/playbook/pb_layout/docs/_layout_bracket.jsx +190 -0
  77. data/app/pb_kits/playbook/pb_layout/docs/_layout_bracket.md +5 -0
  78. data/app/pb_kits/playbook/pb_layout/docs/example.yml +1 -0
  79. data/app/pb_kits/playbook/pb_layout/docs/index.js +1 -0
  80. data/app/pb_kits/playbook/pb_layout/layout.test.js +4 -0
  81. data/app/pb_kits/playbook/pb_layout/subcomponents/_game.tsx +90 -0
  82. data/app/pb_kits/playbook/pb_layout/subcomponents/_round.tsx +57 -0
  83. data/app/pb_kits/playbook/pb_lightbox/Carousel/Slide.tsx +1 -1
  84. data/app/pb_kits/playbook/pb_lightbox/Carousel/Slides.tsx +1 -1
  85. data/app/pb_kits/playbook/pb_lightbox/Carousel/Thumbnails.tsx +1 -1
  86. data/app/pb_kits/playbook/pb_lightbox/hooks/useVisibility.js +1 -1
  87. data/app/pb_kits/playbook/pb_line_graph/_line_graph.tsx +1 -1
  88. data/app/pb_kits/playbook/pb_link/link.test.jsx +2 -2
  89. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +23 -0
  90. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +28 -1
  91. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.html.erb +72 -0
  92. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.jsx +97 -0
  93. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +71 -0
  94. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +91 -0
  95. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +4 -0
  96. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +3 -1
  97. data/app/pb_kits/playbook/pb_multi_level_select/index.js +105 -0
  98. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +10 -0
  99. data/app/pb_kits/playbook/pb_nav/_nav.scss +5 -0
  100. data/app/pb_kits/playbook/pb_nav/_nav_item.test.js +5 -3
  101. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +29 -11
  102. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.html.erb +1 -1
  103. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +4 -4
  104. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +2 -0
  105. data/app/pb_kits/playbook/pb_section_separator/docs/_section_separator_vertical.md +1 -0
  106. data/app/pb_kits/playbook/pb_stat_change/stat_change.test.js +8 -4
  107. data/app/pb_kits/playbook/pb_table/_table.tsx +4 -0
  108. data/app/pb_kits/playbook/pb_table/docs/_table_with_header_style_borderless.jsx +50 -0
  109. data/app/pb_kits/playbook/pb_table/docs/_table_with_header_style_borderless_react.md +1 -0
  110. data/app/pb_kits/playbook/pb_table/docs/_table_with_header_style_floating.jsx +59 -0
  111. data/app/pb_kits/playbook/pb_table/docs/_table_with_header_style_floating_react.md +1 -0
  112. data/app/pb_kits/playbook/pb_table/docs/example.yml +2 -0
  113. data/app/pb_kits/playbook/pb_table/docs/index.js +2 -0
  114. data/app/pb_kits/playbook/pb_table/styles/_headers.scss +76 -0
  115. data/app/pb_kits/playbook/pb_table/styles/_striped.scss +3 -3
  116. data/app/pb_kits/playbook/pb_table/subcomponents/_table_head.tsx +11 -1
  117. data/app/pb_kits/playbook/pb_table/subcomponents/_table_header.tsx +11 -1
  118. data/app/pb_kits/playbook/pb_table/subcomponents/_table_row.tsx +5 -0
  119. data/app/pb_kits/playbook/pb_table/table.test.js +17 -0
  120. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_emoji_picker.jsx +371 -0
  121. data/app/pb_kits/playbook/pb_text_input/docs/example.yml +1 -0
  122. data/app/pb_kits/playbook/pb_text_input/docs/index.js +1 -0
  123. data/app/pb_kits/playbook/pb_tooltip/index.js +183 -56
  124. data/app/pb_kits/playbook/pb_tooltip/tooltip.html.erb +2 -5
  125. data/app/pb_kits/playbook/pb_treemap_chart/_treemap_chart.tsx +1 -1
  126. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +4 -0
  127. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -0
  128. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_disabled.html.erb +19 -0
  129. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_disabled.jsx +23 -0
  130. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_highlight.jsx +4 -4
  131. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  132. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -0
  133. data/app/pb_kits/playbook/pb_typeahead/index.ts +2 -2
  134. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +2 -5
  135. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -0
  136. data/app/pb_kits/playbook/pb_user/_user.tsx +78 -13
  137. data/app/pb_kits/playbook/pb_user/docs/_user_font_options.html.erb +22 -0
  138. data/app/pb_kits/playbook/pb_user/docs/_user_font_options.jsx +40 -0
  139. data/app/pb_kits/playbook/pb_user/docs/_user_font_options_rails.md +5 -0
  140. data/app/pb_kits/playbook/pb_user/docs/_user_font_options_react.md +5 -0
  141. data/app/pb_kits/playbook/pb_user/docs/example.yml +2 -0
  142. data/app/pb_kits/playbook/pb_user/docs/index.js +1 -0
  143. data/app/pb_kits/playbook/pb_user/user.html.erb +28 -12
  144. data/app/pb_kits/playbook/pb_user/user.rb +17 -1
  145. data/app/pb_kits/playbook/pb_user/user.test.js +182 -1
  146. data/app/pb_kits/playbook/pb_user_badge/user_badge.html.erb +1 -6
  147. data/app/pb_kits/playbook/tokens/_colors.scss +1 -4
  148. data/app/pb_kits/playbook/utilities/globalProps.ts +1 -1
  149. data/app/pb_kits/playbook/utilities/object.test.js +287 -1
  150. data/app/pb_kits/playbook/utilities/object.ts +171 -3
  151. data/app/pb_kits/playbook/utilities/text.ts +1 -1
  152. data/dist/chunks/_typeahead-7W5Ha5Td.js +22 -0
  153. data/dist/chunks/_weekday_stacked-DSKatW3m.js +45 -0
  154. data/dist/chunks/lib-BGzBzFZX.js +29 -0
  155. data/dist/chunks/{pb_form_validation-BvDxpfs-.js → pb_form_validation-BvNy9Bd6.js} +1 -1
  156. data/dist/chunks/vendor.js +1 -1
  157. data/dist/playbook-doc.js +1 -1
  158. data/dist/playbook-rails-react-bindings.js +1 -1
  159. data/dist/playbook-rails.js +1 -1
  160. data/dist/playbook.css +1 -1
  161. data/lib/playbook/forms/builder/multi_level_select_field.rb +2 -0
  162. data/lib/playbook/kit_base.rb +4 -4
  163. data/lib/playbook/version.rb +2 -2
  164. metadata +40 -9
  165. data/app/pb_kits/playbook/pb_tooltip/floating_ui.js +0 -282
  166. data/dist/chunks/_typeahead-CRAPc8k-.js +0 -22
  167. data/dist/chunks/_weekday_stacked-T0kFfioG.js +0 -45
  168. data/dist/chunks/lib-BeKPJYlk.js +0 -29
  169. /data/app/pb_kits/playbook/pb_advanced_table/docs/{_advanced_table_selectable_rows_no_subrows.md → _advanced_table_selectable_rows_no_subrows_react.md} +0 -0
  170. /data/app/pb_kits/playbook/pb_advanced_table/docs/{_advanced_table_selectable_rows.md → _advanced_table_selectable_rows_react.md} +0 -0
@@ -0,0 +1,371 @@
1
+ import React, { useState, useEffect } from "react"
2
+ import TextInput from '../../pb_text_input/_text_input'
3
+ import Button from "../../pb_button/_button"
4
+
5
+ // Emojis organized by category with icons for navigation and search names
6
+ const emojiCategories = [
7
+ {
8
+ id: 'recent',
9
+ name: "Recent",
10
+ icon: '🕒',
11
+ emojis: [
12
+ { emoji: '😀', name: 'happy' },
13
+ { emoji: '👍', name: 'thumbs up' },
14
+ { emoji: '🎉', name: 'popper' },
15
+ { emoji: '❤️', name: 'heart' },
16
+ { emoji: '🔥', name: 'fire' },
17
+ { emoji: '✨', name: 'sparkles' }
18
+ ]
19
+ },
20
+ {
21
+ id: 'smileys',
22
+ name: "Smileys",
23
+ icon: '😊',
24
+ emojis: [
25
+ { emoji: '😀', name: 'happy' },
26
+ { emoji: '😂', name: 'lmao' },
27
+ { emoji: '🙂', name: 'smile' },
28
+ { emoji: '😊', name: 'smiley' },
29
+ { emoji: '😍', name: 'smile-heart' },
30
+ { emoji: '🤩', name: 'star-struck' }
31
+ ]
32
+ },
33
+ {
34
+ id: 'people',
35
+ name: "People",
36
+ icon: '👥',
37
+ emojis: [
38
+ { emoji: '👍', name: 'thumbs up' },
39
+ { emoji: '👎', name: 'thumbs down' },
40
+ { emoji: '👌', name: 'OK' },
41
+ { emoji: '👋', name: 'wave' },
42
+ { emoji: '✌️', name: 'victory' },
43
+ { emoji: '🙏', name: 'pray' }
44
+ ]
45
+ },
46
+ {
47
+ id: 'animals',
48
+ name: "Animals",
49
+ icon: '🐼',
50
+ emojis: [
51
+ { emoji: '🐶', name: 'dog' },
52
+ { emoji: '🐱', name: 'cat' },
53
+ { emoji: '🐼', name: 'panda' },
54
+ { emoji: '🦁', name: 'lion' },
55
+ { emoji: '🐢', name: 'turtle' },
56
+ { emoji: '🦋', name: 'butterfly' }
57
+ ]
58
+ },
59
+ {
60
+ id: 'food',
61
+ name: "Food",
62
+ icon: '🍔',
63
+ emojis: [
64
+ { emoji: '🍎', name: 'apple' },
65
+ { emoji: '🍕', name: 'pizza' },
66
+ { emoji: '🍦', name: 'ice cream' },
67
+ { emoji: '🍩', name: 'doughnut' },
68
+ { emoji: '🍷', name: 'wine' },
69
+ { emoji: '🍺', name: 'beer' }
70
+ ]
71
+ },
72
+ {
73
+ id: 'activities',
74
+ name: "Activities",
75
+ icon: '⚽',
76
+ emojis: [
77
+ { emoji: '⚽', name: 'soccer ball' },
78
+ { emoji: '🏀', name: 'basketball' },
79
+ { emoji: '🎮', name: 'video game' },
80
+ { emoji: '🎬', name: 'clapper board' },
81
+ { emoji: '🎨', name: 'artist palette' },
82
+ { emoji: '🎭', name: 'performing arts' }
83
+ ]
84
+ },
85
+ {
86
+ id: 'travel',
87
+ name: "Travel",
88
+ icon: '✈️',
89
+ emojis: [
90
+ { emoji: '🚗', name: 'automobile' },
91
+ { emoji: '✈️', name: 'airplane' },
92
+ { emoji: '🚲', name: 'bicycle' },
93
+ { emoji: '⛵', name: 'sailboat' },
94
+ { emoji: '🏖️', name: 'beach' },
95
+ { emoji: '🗽', name: 'Statue of Liberty' }
96
+ ]
97
+ },
98
+ {
99
+ id: 'symbols',
100
+ name: "Symbols",
101
+ icon: '🔣',
102
+ emojis: [
103
+ { emoji: '❤️', name: 'heart' },
104
+ { emoji: '🔥', name: 'fire' },
105
+ { emoji: '✨', name: 'sparkles' },
106
+ { emoji: '💯', name: 'hundred' },
107
+ { emoji: '⭐', name: 'star' },
108
+ { emoji: '💪', name: 'flex' }
109
+ ]
110
+ }
111
+ ]
112
+
113
+ const TextInputEmojiPicker = (props) => {
114
+ const [inputText, setInputText] = useState('')
115
+ const [showPicker, setShowPicker] = useState(false)
116
+ const [activeCategory, setActiveCategory] = useState('smileys')
117
+ const [searchQuery, setSearchQuery] = useState('')
118
+ const [filteredEmojis, setFilteredEmojis] = useState([])
119
+ const [recentEmojis, setRecentEmojis] = useState([
120
+ { emoji: '😀', name: 'happy' },
121
+ { emoji: '👍', name: 'thumbs up' },
122
+ { emoji: '🎉', name: 'popper' },
123
+ { emoji: '❤️', name: 'heart' },
124
+ { emoji: '🔥', name: 'fire' },
125
+ { emoji: '✨', name: 'sparkles' }
126
+ ])
127
+
128
+ // Handle emoji selection
129
+ const handleEmojiClick = (emojiObj) => {
130
+ setInputText(prevInput => prevInput + emojiObj.emoji)
131
+
132
+ // Add to recent emojis
133
+ if (!recentEmojis.some(item => item.emoji === emojiObj.emoji)) {
134
+ const updatedRecent = [emojiObj, ...recentEmojis.slice(0, 5)]
135
+ setRecentEmojis(updatedRecent)
136
+
137
+ // Update the recent category
138
+ const recentCategory = emojiCategories.find(cat => cat.id === 'recent')
139
+ if (recentCategory) {
140
+ recentCategory.emojis = updatedRecent
141
+ }
142
+ }
143
+
144
+ // Uncomment to close picker after selection
145
+ // setShowPicker(false)
146
+ }
147
+
148
+ // Handle search
149
+ useEffect(() => {
150
+ if (searchQuery.trim() === '') {
151
+ setFilteredEmojis([])
152
+ return
153
+ }
154
+
155
+ // Collect all emojis and filter based on search
156
+ const allEmojis = emojiCategories.flatMap(category =>
157
+ category.emojis.map(emojiObj => ({
158
+ ...emojiObj,
159
+ categoryName: category.name
160
+ }))
161
+ )
162
+
163
+ // Search by emoji name or category
164
+ const filtered = allEmojis.filter(item =>
165
+ item.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
166
+ item.categoryName.toLowerCase().includes(searchQuery.toLowerCase())
167
+ )
168
+
169
+ setFilteredEmojis(filtered)
170
+ }, [searchQuery])
171
+
172
+ return (
173
+ <div className="emoji-container">
174
+ <div className="input-wrapper">
175
+ <TextInput
176
+ id="emoji-text-input"
177
+ name="emojiPicker"
178
+ onChange={(e) => setInputText(e.target.value)}
179
+ placeholder="Enter some emojis here"
180
+ value={inputText}
181
+ {...props}
182
+ />
183
+ <Button
184
+ className="emoji-toggle-button"
185
+ marginRight="lg"
186
+ onClick={() => setShowPicker(!showPicker)}
187
+ variant="link"
188
+ >
189
+ 😊
190
+ </Button>
191
+ </div>
192
+
193
+ {showPicker && (
194
+ <div className="picker-wrapper">
195
+ {/* Search Bar */}
196
+ <TextInput
197
+ onChange={(e) => setSearchQuery(e.target.value)}
198
+ placeholder="Search emojis..."
199
+ type="text"
200
+ value={searchQuery}
201
+ {...props}
202
+ />
203
+
204
+ {/* Category Navigation */}
205
+ <div className="category-nav">
206
+ {emojiCategories.map(category => (
207
+ <button
208
+ className={`category-button ${activeCategory === category.id ? 'active' : ''}`}
209
+ key={category.id}
210
+ onClick={() => {
211
+ setActiveCategory(category.id)
212
+ setSearchQuery('')
213
+ document.getElementById(`category-${category.id}`)?.scrollIntoView({ behavior: 'smooth' })
214
+ }}
215
+ title={category.name}
216
+ >
217
+ {category.icon}
218
+ </button>
219
+ ))}
220
+ </div>
221
+
222
+ {/* Emoji Content */}
223
+ <div className="emoji-content">
224
+ {searchQuery.trim() !== '' ? (
225
+ // Search Results
226
+ <div className="search-results">
227
+ <div className="category-title">Search Results</div>
228
+ {filteredEmojis.length > 0 ? (
229
+ <div className="emoji-grid">
230
+ {filteredEmojis.map((item, index) => (
231
+ <Button
232
+ className="emoji-button"
233
+ key={`search-${index}`}
234
+ onClick={() => handleEmojiClick(item)}
235
+ title={item.name}
236
+ variant="link"
237
+ >
238
+ {item.emoji}
239
+ </Button>
240
+ ))}
241
+ </div>
242
+ ) : (
243
+ <div className="no-results">No emojis found</div>
244
+ )}
245
+ </div>
246
+ ) : (
247
+ // All Categories
248
+ <div className="all-categories">
249
+ {emojiCategories.map(category => (
250
+ <div
251
+ className="category-section"
252
+ id={`category-${category.id}`}
253
+ key={category.id}
254
+ >
255
+ <div className="category-title">
256
+ {category.name}
257
+ </div>
258
+ <div className="emoji-grid">
259
+ {category.emojis.map((emojiObj, index) => (
260
+ <Button
261
+ className="emoji-button"
262
+ key={`${category.id}-${index}`}
263
+ onClick={() => handleEmojiClick(emojiObj)}
264
+ title={emojiObj.name}
265
+ variant="link"
266
+ >
267
+ {emojiObj.emoji}
268
+ </Button>
269
+ ))}
270
+ </div>
271
+ </div>
272
+ ))}
273
+ </div>
274
+ )}
275
+ </div>
276
+ </div>
277
+ )}
278
+
279
+ <style jsx>{`
280
+ .emoji-container {
281
+ font-family: Arial, sans-serif;
282
+ max-width: 500px;
283
+ position: relative;
284
+ }
285
+
286
+ .input-wrapper {
287
+ display: flex;
288
+ margin-bottom: 10px;
289
+ position: relative;
290
+ }
291
+
292
+ .picker-wrapper {
293
+ position: absolute;
294
+ z-index: 100;
295
+ margin-top: 5px;
296
+ background: white;
297
+ border: 1px solid #ddd;
298
+ border-radius: 8px;
299
+ padding: 12px;
300
+ box-shadow: 0 2px 15px rgba(0,0,0,0.1);
301
+ width: 320px;
302
+ }
303
+
304
+ .search-container {
305
+ margin-bottom: 12px;
306
+ }
307
+
308
+ .category-nav {
309
+ display: flex;
310
+ overflow-x: auto;
311
+ padding-bottom: 8px;
312
+ margin-bottom: 12px;
313
+ border-bottom: 1px solid #eee;
314
+ }
315
+
316
+ .category-button {
317
+ background: none;
318
+ border: none;
319
+ font-size: 18px;
320
+ padding: 8px;
321
+ cursor: pointer;
322
+ margin-right: 4px;
323
+ border-radius: 4px;
324
+ min-width: 36px;
325
+ }
326
+
327
+ .category-button.active {
328
+ background-color: #f0f0f0;
329
+ }
330
+
331
+ .emoji-content {
332
+ max-height: 300px;
333
+ overflow-y: auto;
334
+ scroll-behavior: smooth;
335
+ }
336
+
337
+ .all-categories {
338
+ display: flex;
339
+ flex-direction: column;
340
+ gap: 16px;
341
+ }
342
+
343
+ .category-section {
344
+ padding-top: 8px;
345
+ scroll-margin-top: 8px;
346
+ }
347
+
348
+ .category-title {
349
+ font-weight: bold;
350
+ margin-bottom: 8px;
351
+ color: #555;
352
+ font-size: 14px;
353
+ }
354
+
355
+ .emoji-grid {
356
+ display: grid;
357
+ grid-template-columns: repeat(6, 1fr);
358
+ gap: 2px;
359
+ }
360
+
361
+ .no-results {
362
+ padding: 20px;
363
+ text-align: center;
364
+ color: #999;
365
+ }
366
+ `}</style>
367
+ </div>
368
+ );
369
+ };
370
+
371
+ export default TextInputEmojiPicker
@@ -18,6 +18,7 @@ examples:
18
18
  - text_input_inline: Inline
19
19
  - text_input_no_label: No Label
20
20
  - text_input_mask: Mask
21
+ - text_input_emoji_picker: Emoji Picker
21
22
 
22
23
  swift:
23
24
  - text_input_default_swift: Default
@@ -6,3 +6,4 @@ export { default as TextInputAddOn } from './_text_input_add_on.jsx'
6
6
  export { default as TextInputInline } from './_text_input_inline.jsx'
7
7
  export { default as TextInputNoLabel } from './_text_input_no_label.jsx'
8
8
  export { default as TextInputMask } from './_text_input_mask.jsx'
9
+ export { default as TextInputEmojiPicker } from './_text_input_emoji_picker.jsx'
@@ -1,22 +1,26 @@
1
1
  import PbEnhancedElement from '../pb_enhanced_element'
2
- import {
3
- createPopperLite as createPopper,
4
- flip,
5
- offset,
6
- preventOverflow,
7
- } from '@popperjs/core'
8
-
9
- const TOOLTIP_OFFSET = [0, 20]
2
+ import { computePosition, offset, flip, shift, arrow, autoUpdate } from '@floating-ui/dom'
3
+
4
+ const TOOLTIP_OFFSET = 20
10
5
  const TOOLTIP_TIMEOUT = 250
6
+ const SAFE_ZONE_MARGIN = 1
11
7
 
12
8
  export default class PbTooltip extends PbEnhancedElement {
13
9
  static get selector() {
14
- return '[data-pb-tooltip-kit="true"][data-pb-tooltip-interaction="false"]:not([data-pb-tooltip-delay-open]):not([data-pb-tooltip-delay-close])'
10
+ return '[data-pb-tooltip-kit]'
15
11
  }
16
12
 
17
13
  connect() {
14
+ if (this.tooltipInteraction) {
15
+ document.addEventListener('mousemove', (e) => {
16
+ this.lastMouseX = e.clientX
17
+ this.lastMouseY = e.clientY
18
+ })
19
+ }
20
+
18
21
  this.triggerElements.forEach((trigger) => {
19
22
  const method = this.triggerMethod
23
+ const interactionEnabled = this.tooltipInteraction
20
24
 
21
25
  if (method === 'click') {
22
26
  trigger.addEventListener('click', () => {
@@ -24,29 +28,66 @@ export default class PbTooltip extends PbEnhancedElement {
24
28
  })
25
29
  } else {
26
30
  trigger.addEventListener('mouseenter', () => {
31
+ clearSafeZoneListener(this)
32
+ clearTimeout(this.mouseleaveTimeout)
33
+ this.currentTrigger = trigger
34
+ const delayOpen = this.delayOpen ? parseInt(this.delayOpen) : TOOLTIP_TIMEOUT
27
35
  this.mouseenterTimeout = setTimeout(() => {
28
36
  this.showTooltip(trigger)
29
- this.checkCloseTooltip(trigger)
30
- }, TOOLTIP_TIMEOUT)
31
-
32
- trigger.addEventListener('mouseleave', () => {
33
- clearTimeout(this.mouseenterTimeout)
34
- setTimeout(() => {
35
- this.hideTooltip()
36
- }, 0)
37
- }, { once: true })
37
+ if (interactionEnabled) {
38
+ this.checkCloseTooltip(trigger)
39
+ }
40
+ }, delayOpen)
38
41
  })
39
42
 
40
- this.tooltip.addEventListener('mouseenter', () => {
43
+ trigger.addEventListener('mouseleave', () => {
41
44
  clearTimeout(this.mouseenterTimeout)
45
+ if (this.delayClose) {
46
+ const delayClose = parseInt(this.delayClose)
47
+ this.mouseleaveTimeout = setTimeout(() => {
48
+ if (interactionEnabled) {
49
+ this.attachSafeZoneListener()
50
+ } else {
51
+ this.hideTooltip()
52
+ }
53
+ }, delayClose)
54
+ } else {
55
+ if (interactionEnabled) {
56
+ this.attachSafeZoneListener()
57
+ } else {
58
+ this.hideTooltip()
59
+ }
60
+ }
42
61
  })
43
- this.tooltip.addEventListener('mouseleave', () => {
44
- this.hideTooltip()
45
- })
62
+
63
+ if (interactionEnabled) {
64
+ this.tooltip.addEventListener('mouseenter', () => {
65
+ clearSafeZoneListener(this)
66
+ })
67
+
68
+ this.tooltip.addEventListener('mouseleave', () => {
69
+ this.attachSafeZoneListener()
70
+ })
71
+ }
46
72
  }
47
73
  })
48
74
  }
49
75
 
76
+ attachSafeZoneListener() {
77
+ clearSafeZoneListener(this)
78
+ this.safeZoneHandler = (e) => {
79
+ if (!this.currentTrigger) return
80
+ const triggerRect = this.currentTrigger.getBoundingClientRect()
81
+ const tooltipRect = this.tooltip.getBoundingClientRect()
82
+ const safeRect = getSafeZone(triggerRect, tooltipRect, this.position, SAFE_ZONE_MARGIN)
83
+ if (!isPointInsideRect(e.clientX, e.clientY, safeRect)) {
84
+ this.hideTooltip()
85
+ clearSafeZoneListener(this)
86
+ }
87
+ }
88
+ document.addEventListener('mousemove', this.safeZoneHandler)
89
+ }
90
+
50
91
  checkCloseTooltip(trigger) {
51
92
  document.querySelector('body').addEventListener('click', ({ target }) => {
52
93
  const isTooltip = target.closest(`#${this.tooltipId}`) === this.tooltip
@@ -62,27 +103,46 @@ export default class PbTooltip extends PbEnhancedElement {
62
103
  showTooltip(trigger) {
63
104
  if (this.shouldShowTooltip === 'false') return
64
105
 
65
- this.popper = createPopper(trigger, this.tooltip, {
66
- placement: this.position,
67
- strategy: 'fixed',
68
- modifiers: [
69
- {
70
- name: 'offset',
71
- options: {
72
- offset: TOOLTIP_OFFSET,
73
- },
74
- },
75
- {
76
- name: 'arrow',
77
- options: {
78
- element: document.querySelector(`#${this.tooltipId}-arrow`),
79
- },
80
- },
81
- offset,
82
- preventOverflow,
83
- flip,
84
- ],
106
+ clearSafeZoneListener(this)
107
+
108
+ this.tooltip.style.opacity = '1'
109
+ this.tooltip.style.visibility = 'visible'
110
+ this.tooltip.style.pointerEvents = 'auto'
111
+
112
+ if (this.cleanup) {
113
+ this.cleanup()
114
+ }
115
+
116
+ const arrowElement = document.querySelector(`#${this.tooltipId}-arrow`)
117
+
118
+ this.cleanup = autoUpdate(trigger, this.tooltip, () => {
119
+ computePosition(trigger, this.tooltip, {
120
+ placement: this.position,
121
+ strategy: 'fixed',
122
+ middleware: [
123
+ offset({ mainAxis: TOOLTIP_OFFSET, crossAxis: 0 }),
124
+ flip(),
125
+ shift(),
126
+ arrow({ element: arrowElement })
127
+ ],
128
+ }).then(({ x, y, placement, middlewareData }) => {
129
+ Object.assign(this.tooltip.style, {
130
+ left: `${x}px`,
131
+ top: `${y}px`,
132
+ position: 'fixed'
133
+ })
134
+ this.tooltip.setAttribute('data-popper-placement', placement)
135
+ if (arrowElement && middlewareData.arrow) {
136
+ const { x: arrowX, y: arrowY } = middlewareData.arrow
137
+ Object.assign(arrowElement.style, {
138
+ left: arrowX != null ? `${arrowX}px` : '',
139
+ top: arrowY != null ? `${arrowY}px` : '',
140
+ position: 'absolute'
141
+ })
142
+ }
143
+ })
85
144
  })
145
+
86
146
  this.tooltip.classList.add('show')
87
147
 
88
148
  if (this.triggerMethod === 'click') {
@@ -94,39 +154,50 @@ export default class PbTooltip extends PbEnhancedElement {
94
154
  }
95
155
 
96
156
  hideTooltip() {
157
+ if (!this.tooltip) return
158
+
97
159
  this.tooltip.classList.add('fade_out')
98
160
  setTimeout(() => {
99
- if (!this.popper) return
100
- this.popper.destroy()
161
+ if (this.cleanup) {
162
+ this.cleanup()
163
+ this.cleanup = null
164
+ }
101
165
  this.tooltip.classList.remove('show')
102
166
  this.tooltip.classList.remove('fade_out')
167
+ this.tooltip.style.opacity = '0'
168
+ this.tooltip.style.visibility = 'hidden'
169
+ this.tooltip.style.pointerEvents = 'none'
170
+ this.tooltip.style.position = ''
171
+ this.tooltip.style.top = ''
172
+ this.tooltip.style.left = ''
173
+ this.tooltip.style.transform = ''
103
174
  }, TOOLTIP_TIMEOUT)
104
175
  }
105
176
 
106
177
  get triggerElements() {
107
178
  let triggerEl
108
-
109
179
  if (this.triggerElementId) {
110
180
  triggerEl = document.querySelector(`#${this.triggerElementId}`)
111
- } else {
181
+ } else if (this.triggerElementSelector) {
112
182
  const selectorIsId = this.triggerElementSelector.indexOf('#') > -1
113
183
  triggerEl = selectorIsId
114
- ? document.querySelector(`${this.triggerElementSelector}`)
115
- : document.querySelectorAll(`${this.triggerElementSelector}`)
184
+ ? document.querySelector(this.triggerElementSelector)
185
+ : document.querySelectorAll(this.triggerElementSelector)
186
+ } else {
187
+ triggerEl = this.element
116
188
  }
117
-
118
189
  if (!triggerEl) {
119
- console.error('Tooltip Kit: an invalid or unavailable DOM reference was provided!')
190
+ console.error('Tooltip Kit: No valid trigger element found!')
120
191
  return []
121
192
  }
122
-
123
- if (!triggerEl.length) triggerEl = [triggerEl]
124
- return (this._triggerElements = this._triggerElements || triggerEl)
193
+ if (triggerEl.length === undefined) {
194
+ triggerEl = [triggerEl]
195
+ }
196
+ return triggerEl
125
197
  }
126
198
 
127
199
  get tooltip() {
128
- return (this._tooltip =
129
- this._tooltip || this.element.querySelector(`#${this.tooltipId}`))
200
+ return (this._tooltip = this._tooltip || this.element.querySelector(`#${this.tooltipId}`))
130
201
  }
131
202
 
132
203
  get position() {
@@ -152,4 +223,60 @@ export default class PbTooltip extends PbEnhancedElement {
152
223
  get triggerMethod() {
153
224
  return this.element.dataset.pbTooltipTriggerMethod || 'hover'
154
225
  }
155
- }
226
+
227
+ get tooltipInteraction() {
228
+ return this.element.dataset.pbTooltipInteraction === 'true'
229
+ }
230
+
231
+ get delayOpen() {
232
+ return this.element.dataset.pbTooltipDelayOpen
233
+ }
234
+
235
+ get delayClose() {
236
+ return this.element.dataset.pbTooltipDelayClose
237
+ }
238
+ }
239
+
240
+ function clearSafeZoneListener(context) {
241
+ if (context.safeZoneHandler) {
242
+ document.removeEventListener('mousemove', context.safeZoneHandler)
243
+ context.safeZoneHandler = null
244
+ }
245
+ }
246
+
247
+ function getSafeZone(triggerRect, tooltipRect, placement, margin) {
248
+ let safeRect = {}
249
+ if (placement.startsWith('top')) {
250
+ safeRect.left = triggerRect.left - margin
251
+ safeRect.right = triggerRect.right + margin
252
+ safeRect.top = tooltipRect.bottom - margin
253
+ safeRect.bottom = triggerRect.top + margin
254
+ } else if (placement.startsWith('bottom')) {
255
+ safeRect.left = triggerRect.left - margin
256
+ safeRect.right = triggerRect.right + margin
257
+ safeRect.top = triggerRect.bottom - margin
258
+ safeRect.bottom = tooltipRect.top + margin
259
+ } else if (placement.startsWith('left')) {
260
+ safeRect.top = triggerRect.top - margin
261
+ safeRect.bottom = triggerRect.bottom + margin
262
+ safeRect.left = tooltipRect.right - margin
263
+ safeRect.right = triggerRect.left + margin
264
+ } else if (placement.startsWith('right')) {
265
+ safeRect.top = triggerRect.top - margin
266
+ safeRect.bottom = triggerRect.bottom + margin
267
+ safeRect.left = triggerRect.right - margin
268
+ safeRect.right = tooltipRect.left + margin
269
+ } else {
270
+ safeRect = {
271
+ left: triggerRect.left - margin,
272
+ right: triggerRect.right + margin,
273
+ top: triggerRect.top - margin,
274
+ bottom: triggerRect.bottom + margin,
275
+ }
276
+ }
277
+ return safeRect
278
+ }
279
+
280
+ function isPointInsideRect(x, y, rect) {
281
+ return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
282
+ }