@ably/ui 7.10.0 → 8.0.0-dev.43132fe

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 (72) hide show
  1. package/core/Meganav/component.css +5 -1
  2. package/core/Meganav/component.js +2 -1
  3. package/core/Meganav/component.js.LICENSE.txt +7 -0
  4. package/core/Meganav.jsx +7905 -2809
  5. package/core/MeganavContentPlatform.jsx +27 -20
  6. package/core/MeganavContentWhyAbly.jsx +1 -1
  7. package/core/MeganavControl/component.js +1 -1
  8. package/core/MeganavControl.jsx +5 -3
  9. package/core/MeganavItemsDesktop.jsx +5 -3
  10. package/core/MeganavItemsMobile.jsx +188 -11
  11. package/core/MeganavItemsSignedIn.jsx +259 -7
  12. package/core/MeganavSearch/component.js +1 -0
  13. package/core/MeganavSearch.jsx +504 -0
  14. package/core/MeganavSearchAutocomplete/component.js +2 -0
  15. package/core/MeganavSearchAutocomplete/component.js.LICENSE.txt +7 -0
  16. package/core/MeganavSearchAutocomplete.jsx +117 -0
  17. package/core/MeganavSearchPanel/component.js +1 -0
  18. package/core/MeganavSearchPanel.jsx +455 -0
  19. package/core/MeganavSearchSuggestions/component.js +1 -0
  20. package/core/MeganavSearchSuggestions.jsx +366 -0
  21. package/core/fonts/.DS_Store +0 -0
  22. package/core/fonts/source-code-pro.css +3 -0
  23. package/core/images/.DS_Store +0 -0
  24. package/core/scripts.js +1 -1
  25. package/core/styles.css +18 -36
  26. package/package.json +2 -1
  27. package/preview/vendor/bundle/ruby/3.0.0/bundler/gems/ably-ui-abffd210ec0f/preview/log/.keep +0 -0
  28. package/preview/vendor/bundle/ruby/3.0.0/bundler/gems/ably-ui-abffd210ec0f/preview/tmp/.keep +0 -0
  29. package/preview/vendor/bundle/ruby/3.0.0/bundler/gems/ably-ui-abffd210ec0f/preview/tmp/pids/.keep +0 -0
  30. package/src/.DS_Store +0 -0
  31. package/src/core/.DS_Store +0 -0
  32. package/src/core/Code/component.css +1 -3
  33. package/src/core/Meganav/component.css +5 -1
  34. package/src/core/Meganav/component.html.erb +10 -5
  35. package/src/core/Meganav/component.js +11 -1
  36. package/src/core/Meganav/component.jsx +15 -7
  37. package/src/core/MeganavContentPlatform/component.html.erb +24 -18
  38. package/src/core/MeganavContentPlatform/component.jsx +25 -20
  39. package/src/core/MeganavControl/component.html.erb +2 -2
  40. package/src/core/MeganavControl/component.js +37 -12
  41. package/src/core/MeganavControl/component.jsx +3 -2
  42. package/src/core/MeganavControl/component.rb +3 -1
  43. package/src/core/MeganavItemsMobile/component.html.erb +30 -2
  44. package/src/core/MeganavItemsMobile/component.jsx +33 -2
  45. package/src/core/MeganavItemsSignedIn/component.html.erb +6 -2
  46. package/src/core/MeganavItemsSignedIn/component.jsx +7 -2
  47. package/src/core/MeganavSearch/component.html.erb +15 -0
  48. package/src/core/MeganavSearch/component.js +0 -0
  49. package/src/core/MeganavSearch/component.jsx +33 -0
  50. package/src/core/MeganavSearch/component.rb +13 -0
  51. package/src/core/MeganavSearchAutocomplete/component.html.erb +6 -0
  52. package/src/core/MeganavSearchAutocomplete/component.js +177 -0
  53. package/src/core/MeganavSearchAutocomplete/component.jsx +14 -0
  54. package/src/core/MeganavSearchAutocomplete/component.rb +6 -0
  55. package/src/core/MeganavSearchPanel/component.html.erb +22 -0
  56. package/src/core/MeganavSearchPanel/component.js +0 -0
  57. package/src/core/MeganavSearchPanel/component.jsx +39 -0
  58. package/src/core/MeganavSearchPanel/component.rb +13 -0
  59. package/src/core/MeganavSearchSuggestions/component.html.erb +22 -0
  60. package/src/core/MeganavSearchSuggestions/component.js +123 -0
  61. package/src/core/MeganavSearchSuggestions/component.jsx +49 -0
  62. package/src/core/MeganavSearchSuggestions/component.rb +18 -0
  63. package/src/core/fonts/.DS_Store +0 -0
  64. package/src/core/fonts/source-code-pro.css +3 -0
  65. package/src/core/images/.DS_Store +0 -0
  66. package/src/core/remote-blogs-posts.js +1 -1
  67. package/src/core/remote-session-data.js +1 -1
  68. package/src/core/styles/properties.css +6 -3
  69. package/src/core/styles/text.css +12 -16
  70. package/src/core/styles.components.css +0 -15
  71. package/src/reset/.DS_Store +0 -0
  72. package/tailwind.config.js +14 -4
@@ -17,6 +17,8 @@ import MeganavControl from "../MeganavControl/component";
17
17
  import MeganavControlMobileDropdown from "../MeganavControlMobileDropdown/component";
18
18
  import MobilePanelOpenClick from "../MeganavControlMobilePanelOpen/component";
19
19
  import MobilePanelCloseClick from "../MeganavControlMobilePanelClose/component";
20
+ import MeganavSearchAutocomplete from "../MeganavSearchAutocomplete/component";
21
+ import MeganavSearchSuggestions from "../MeganavSearchSuggestions/component";
20
22
 
21
23
  // Close menu when user clicks outside of viewport
22
24
  const windowOnBlur = (closeAll) => {
@@ -111,10 +113,14 @@ const documentScroll = (themeName) => {
111
113
  };
112
114
  };
113
115
 
114
- export default function Meganav({ themeName } = { themeName: null }) {
116
+ export default function Meganav(
117
+ { themeName, addSearchApiKey } = { themeName: null }
118
+ ) {
115
119
  const controls = MeganavControl();
116
120
  const panelOpenControls = MobilePanelOpenClick();
117
121
  const panelCloseControls = MobilePanelCloseClick();
122
+ const search = MeganavSearchAutocomplete(addSearchApiKey);
123
+ const searchSuggestions = MeganavSearchSuggestions();
118
124
 
119
125
  const mobileDropdownControl = MeganavControlMobileDropdown({
120
126
  clearPanels: () =>
@@ -124,9 +130,11 @@ export default function Meganav({ themeName } = { themeName: null }) {
124
130
  const closeAll = () =>
125
131
  [
126
132
  mobileDropdownControl,
133
+ searchSuggestions,
127
134
  ...panelOpenControls,
128
135
  ...panelCloseControls,
129
136
  ...controls,
137
+ ...search,
130
138
  ].forEach((i) => i.clear());
131
139
 
132
140
  const teardowns = [
@@ -134,9 +142,11 @@ export default function Meganav({ themeName } = { themeName: null }) {
134
142
  documentClick(closeAll),
135
143
  windowOnBlur(closeAll),
136
144
  mobileDropdownControl,
145
+ searchSuggestions,
137
146
  ...controls,
138
147
  ...panelOpenControls,
139
148
  ...panelCloseControls,
149
+ ...search,
140
150
  ].map((i) => i.teardown);
141
151
 
142
152
  return () => teardowns.forEach((teardown) => teardown());
@@ -18,23 +18,27 @@ import MeganavContentPlatform from "../MeganavContentPlatform/component.jsx";
18
18
  import MeganavContentUseCases from "../MeganavContentUseCases/component.jsx";
19
19
  import MeganavContentWhyAbly from "../MeganavContentWhyAbly/component.jsx";
20
20
  import MeganavContentDevelopers from "../MeganavContentDevelopers/component.jsx";
21
+ import MeganavSearch from "../MeganavSearch/component.jsx";
21
22
 
22
23
  const SignIn = ({ sessionState, theme, loginLink, absUrl }) => {
23
24
  return sessionState.signedIn ? (
24
25
  <MeganavItemsSignedIn absUrl={absUrl} sessionState={sessionState} theme={theme} />
25
26
  ) : (
26
27
  <ul className="hidden md:flex items-center">
27
- <li>
28
+ <li className="ui-meganav-item">
28
29
  <a href={absUrl("/contact")} className={`ui-meganav-link ${theme.textColor}`} data-id="meganav-link">
29
30
  Contact us
30
31
  </a>
31
32
  </li>
32
- <li>
33
- <a href={absUrl(loginLink)} className={`ui-meganav-link ${theme.textColor}`} data-id="meganav-link">
33
+ <li className="ui-meganav-item">
34
+ <a href={absUrl(loginLink)} className={`ui-meganav-link mr-0 ${theme.textColor}`} data-id="meganav-link">
34
35
  Login
35
36
  </a>
36
37
  </li>
37
- <li className="ml-16">
38
+ <li className="ui-meganav-item">
39
+ <MeganavSearch absUrl={absUrl} />
40
+ </li>
41
+ <li className="ui-meganav-item">
38
42
  <a href={absUrl("/sign-up")} data-id="meganav-sign-up-btn" className={`ui-btn p-btn-small ${theme.buttonBackgroundColor} ${theme.buttonTextColor}`}>
39
43
  Sign up free
40
44
  </a>
@@ -60,7 +64,7 @@ const panels = {
60
64
  MeganavContentDevelopers: MeganavContentDevelopers,
61
65
  };
62
66
 
63
- export default function Meganav({ paths, themeName = "white", notice, loginLink = "/login", urlBase }) {
67
+ export default function Meganav({ paths, themeName = "white", notice, loginLink = "/login", urlBase, addSearchApiKey }) {
64
68
  const [sessionState, setSessionState] = useState(null);
65
69
 
66
70
  useEffect(() => {
@@ -70,7 +74,7 @@ export default function Meganav({ paths, themeName = "white", notice, loginLink
70
74
  }, []);
71
75
 
72
76
  useEffect(() => {
73
- const teardown = MeganavScripts({ themeName });
77
+ const teardown = MeganavScripts({ themeName, addSearchApiKey });
74
78
  return () => teardown();
75
79
  }, [sessionState]);
76
80
 
@@ -81,7 +85,10 @@ export default function Meganav({ paths, themeName = "white", notice, loginLink
81
85
  <nav className={`ui-meganav-wrapper ${theme.backgroundColor} ${theme.barShadow}`} data-id="meganav" aria-label="Main">
82
86
  {notice && <Notice {...notice.props} config={notice.config} />}
83
87
  <div className="ui-meganav ui-grid-px">
84
- <Logo dataId="meganav-logo" href={urlBase} />
88
+ <div className="mr-24">
89
+ <Logo dataId="meganav-logo" href={urlBase} />
90
+ </div>
91
+
85
92
  <MeganavItemsDesktop panels={panels} paths={paths} theme={theme} absUrl={absUrl} />
86
93
 
87
94
  {/* Because we load the session state through fetch, we display a placeholder until fetch returns */}
@@ -112,4 +119,5 @@ Meganav.propTypes = {
112
119
  }),
113
120
  loginLink: T.string,
114
121
  urlBase: T.string,
122
+ addSearchApiKey: T.string,
115
123
  };
@@ -14,41 +14,47 @@
14
14
  </div>
15
15
 
16
16
  <div>
17
- <h3 class="ui-meganav-overline" id="meganav-platform-panel-list-our-features">Our features</h3>
18
- <ul aria-labelledby="meganav-platform-panel-list-our-features">
17
+ <h3 class="ui-meganav-overline" id="meganav-platform-panel-list-examples">Examples</h3>
18
+ <ul aria-labelledby="meganav-platform-panel-list-examples">
19
19
  <li>
20
- <%= link_to abs_url("/pub-sub-messaging"), class: "ui-meganav-media group" do %>
21
- <p class="ui-meganav-media-heading">Publish/subscribe messaging</p>
22
- <p class="ui-meganav-media-copy">Feature-rich pub/sub messaging to power any realtime requirement.</p>
20
+ <%= link_to abs_url("/examples/avatar-stack"), class: "ui-meganav-media group" do %>
21
+ <p class="ui-meganav-media-heading">Avatar Stack</p>
22
+ <p class="ui-meganav-media-copy">See who is connected in a digital space.</p>
23
23
  <% end %>
24
24
  </li>
25
25
  <li>
26
- <%= link_to abs_url("/push-notifications"), class: "ui-meganav-media group" do %>
27
- <p class="ui-meganav-media-heading">Push notifications</p>
28
- <p class="ui-meganav-media-copy">Deliver native notifications at scale with our unified API.</p>
26
+ <%= link_to abs_url("/examples/emoji-reactions"), class: "ui-meganav-media group" do %>
27
+ <p class="ui-meganav-media-heading">Emoji Reactions</p>
28
+ <p class="ui-meganav-media-copy">React with an emoji to a message.</p>
29
29
  <% end %>
30
30
  </li>
31
31
  <li>
32
- <%= link_to abs_url("/integrations"), class: "ui-meganav-media group" do %>
33
- <p class="ui-meganav-media-heading">Third-party integrations</p>
34
- <p class="ui-meganav-media-copy">Integrate and extend Ably with cloud services like AWS Kinesis.</p>
32
+ <%= link_to abs_url("/examples/activity-feed"), class: "ui-meganav-media group" do %>
33
+ <p class="ui-meganav-media-heading">Activity Feed</p>
34
+ <p class="ui-meganav-media-copy">Display a list of user actions in realtime.</p>
35
35
  <% end %>
36
36
  </li>
37
37
  <li>
38
- <%= link_to abs_url("/protocols"), class: "ui-meganav-media group" do %>
39
- <p class="ui-meganav-media-heading">Multi-protocol messaging</p>
40
- <p class="ui-meganav-media-copy">We support pub/sub over WebSockets, MQTT, SSE, and more.</p>
38
+ <%= link_to abs_url("/examples/live-charts"), class: "ui-meganav-media group" do %>
39
+ <p class="ui-meganav-media-heading">Live Charts</p>
40
+ <p class="ui-meganav-media-copy">Visualise live metrics and data in a chart.</p>
41
41
  <% end %>
42
42
  </li>
43
43
  <li>
44
- <%= link_to abs_url("/hub"), class: "ui-meganav-media group" do %>
45
- <p class="ui-meganav-media-heading">Streaming data sources</p>
46
- <p class="ui-meganav-media-copy">Augment your apps with realtime updates like weather or transit.</p>
44
+ <%= link_to abs_url("/examples/live-cursors"), class: "ui-meganav-media group" do %>
45
+ <p class="ui-meganav-media-heading">Live Cursors</p>
46
+ <p class="ui-meganav-media-copy">Track all cursors in realtime.</p>
47
+ <% end %>
48
+ </li>
49
+ <li>
50
+ <%= link_to abs_url("/examples/typing-indicator"), class: "ui-meganav-media group" do %>
51
+ <p class="ui-meganav-media-heading">Typing Indicator</p>
52
+ <p class="ui-meganav-media-copy">See when a user is typing a message.</p>
47
53
  <% end %>
48
54
  </li>
49
55
  </ul>
50
56
 
51
- <%= render(AblyUi::Core::FeaturedLink.new(url: abs_url("/platform"))) do %>Explore all platform features<% end %>
57
+ <%= render(AblyUi::Core::FeaturedLink.new(url: abs_url("/examples/live-examples"))) do %>Explore all live examples<% end %>
52
58
  </div>
53
59
 
54
60
  <div>
@@ -19,43 +19,48 @@ const MeganavContentPlatform = ({ paths, absUrl }) => (
19
19
  </div>
20
20
 
21
21
  <div>
22
- <h3 className="ui-meganav-overline" id="meganav-platform-panel-list-our-features">
23
- Our features
22
+ <h3 className="ui-meganav-overline" id="meganav-platform-panel-list-examples">
23
+ Examples
24
24
  </h3>
25
- <ul aria-labelledby="meganav-platform-panel-list-our-features">
25
+ <ul aria-labelledby="meganav-platform-panel-list-examples">
26
26
  <li>
27
- <a href={absUrl("/pub-sub-messaging")} className="group ui-meganav-media">
28
- <p className="ui-meganav-media-heading">Publish/subscribe messaging</p>
29
- <p className="ui-meganav-media-copy">Feature-rich pub/sub messaging to power any realtime requirement.</p>
27
+ <a href={absUrl("/examples/avatar-stack")} className="group ui-meganav-media">
28
+ <p className="ui-meganav-media-heading">Avatar Stack</p>
29
+ <p className="ui-meganav-media-copy">See who is connected in a digital space.</p>
30
30
  </a>
31
31
  </li>
32
32
  <li>
33
- <a href={absUrl("/push-notifications")} className="ui-meganav-media group">
34
- <p className="ui-meganav-media-heading">Push notifications</p>
35
- <p className="ui-meganav-media-copy">Deliver native notifications at scale with our unified API.</p>
33
+ <a href={absUrl("/examples/emoji-reactions")} className="ui-meganav-media group">
34
+ <p className="ui-meganav-media-heading">Emoji Reactions</p>
35
+ <p className="ui-meganav-media-copy">React with an emoji to a message.</p>
36
36
  </a>
37
37
  </li>
38
38
  <li>
39
- <a href={absUrl("/integrations")} className="ui-meganav-media group">
40
- <p className="ui-meganav-media-heading">Third-party integrations</p>
41
- <p className="ui-meganav-media-copy">Integrate and extend Ably with cloud services like AWS Kinesis.</p>
39
+ <a href={absUrl("/examples/activity-feed")} className="ui-meganav-media group">
40
+ <p className="ui-meganav-media-heading">Activity Feed</p>
41
+ <p className="ui-meganav-media-copy">Display a list of user actions in realtime.</p>
42
42
  </a>
43
43
  </li>
44
44
  <li>
45
- <a href={absUrl("/protocols")} className="ui-meganav-media group">
46
- <p className="ui-meganav-media-heading">Multi-protocol messaging</p>
47
- <p className="ui-meganav-media-copy">We support pub/sub over WebSockets, MQTT, SSE, and more.</p>
45
+ <a href={absUrl("/examples/live-charts")} className="ui-meganav-media group">
46
+ <p className="ui-meganav-media-heading">Live Charts</p>
47
+ <p className="ui-meganav-media-copy">Visualise live metrics and data in a chart.</p>
48
48
  </a>
49
49
  </li>
50
50
  <li>
51
- <a href={absUrl("/hub")} className="ui-meganav-media group">
52
- <p className="ui-meganav-media-heading">Streaming data sources</p>
53
- <p className="ui-meganav-media-copy">Augment your apps with realtime updates like weather or transit.</p>
51
+ <a href={absUrl("/examples/live-cursors")} className="ui-meganav-media group">
52
+ <p className="ui-meganav-media-heading">Live Cursors</p>
53
+ <p className="ui-meganav-media-copy">Track all cursors in realtime.</p>
54
+ </a>
55
+ </li>
56
+ <li>
57
+ <a href={absUrl("/examples/typing-indicator")} className="ui-meganav-media group">
58
+ <p className="ui-meganav-media-heading">Typing Indicator</p>
59
+ <p className="ui-meganav-media-copy">See when a user is typing a message.</p>
54
60
  </a>
55
61
  </li>
56
62
  </ul>
57
-
58
- <FeaturedLink url={absUrl("/platform")}>Explore all platform features</FeaturedLink>
63
+ <FeaturedLink url={absUrl("/examples/live-examples")}>Explore all live examples</FeaturedLink>
59
64
  </div>
60
65
 
61
66
  <div>
@@ -1,6 +1,6 @@
1
1
  <%= button_tag(type: "button",
2
- class: ["ui-meganav-link", "h-64", "flex", "items-center", "group", theme(:text_color)],
2
+ class: ["ui-meganav-link", "h-64", "flex", "items-center", "group", theme(:text_color), additional_css],
3
3
  data: { id: "meganav-control" },
4
- aria: { expanded: false, controls: @aria_controls, label: "Show #{content}" }) do -%>
4
+ aria: { expanded: false, controls: aria_controls, label: "Show #{content}" }) do -%>
5
5
  <%= content -%><%= render(AblyUi::Core::Icon.new(name: "icon-gui-disclosure-arrow", size: "1.5rem", color: "text-cool-black", additional_css: "transform rotate-90 group-hover:text-gui-hover group-focus:text-gui-focus")) %>
6
6
  <% end %>
@@ -1,4 +1,4 @@
1
- import { queryIdAll } from "../dom-query";
1
+ import { queryId, queryIdAll } from "../dom-query";
2
2
 
3
3
  const MeganavControl = () => {
4
4
  const controls = Array.from(queryIdAll("meganav-control"));
@@ -12,11 +12,21 @@ const MeganavControl = () => {
12
12
  `(hover: hover) and (pointer: fine) and (min-width: ${mdBreakpoint})`
13
13
  ).matches;
14
14
 
15
+ const isSearchControl = (node) => node.dataset.control === "search";
16
+
17
+ const isSearchPanelOpen = () => {
18
+ const searchPanel = document.querySelector(
19
+ '[data-id="meganav-panel"]#panel-search'
20
+ );
21
+ if (!searchPanel) return;
22
+ return !searchPanel.classList.contains("invisible");
23
+ };
24
+
15
25
  const controlsHaveFocus = () =>
16
26
  controls.some((control) => control === document.activeElement);
17
27
 
18
28
  const hover = (control, panel, open) => {
19
- if (hoverEnabled() && !controlsHaveFocus()) {
29
+ if (hoverEnabled() && !controlsHaveFocus() && !isSearchPanelOpen()) {
20
30
  const classes = ["invisible", "visible"];
21
31
  panel.classList.replace(...(open ? classes : classes.reverse()));
22
32
  control.setAttribute("aria-expanded", open);
@@ -40,12 +50,20 @@ const MeganavControl = () => {
40
50
 
41
51
  const ariaExpanded = control.getAttribute("aria-expanded");
42
52
 
43
- if (ariaExpanded) {
53
+ if (ariaExpanded === "true") {
54
+ control.setAttribute("aria-expanded", false);
55
+ panel.classList.replace("visible", "invisible");
56
+ } else {
44
57
  control.setAttribute("aria-expanded", true);
45
58
  panel.classList.replace("invisible", "visible");
59
+ }
60
+
61
+ if (isSearchControl(control)) {
62
+ const searchInput = queryId("meganav-search-input", panel);
63
+ if (!searchInput) return;
64
+ searchInput.focus();
46
65
  } else {
47
- control.setAttribute("aria-expanded", false);
48
- panel.classList.replace("visible", "invisible");
66
+ control.focus();
49
67
  }
50
68
  };
51
69
 
@@ -56,18 +74,25 @@ const MeganavControl = () => {
56
74
  `#${control.getAttribute("aria-controls")}`
57
75
  );
58
76
  const click = clickHandler(control, panel);
59
- const mouseenter = mouseenterHandler(control, panel);
60
- const mouseleave = mouseleaveHandler(control, panel);
61
-
62
- item.addEventListener("mouseenter", mouseenter);
63
- item.addEventListener("mouseleave", mouseleave);
64
77
  control.addEventListener("click", click);
78
+ let mouseenter, mouseleave;
79
+
80
+ if (!isSearchControl(control)) {
81
+ mouseenter = mouseenterHandler(control, panel);
82
+ mouseleave = mouseleaveHandler(control, panel);
83
+
84
+ item.addEventListener("mouseenter", mouseenter);
85
+ item.addEventListener("mouseleave", mouseleave);
86
+ }
65
87
 
66
88
  return [
67
89
  {
68
90
  teardown: () => {
69
- item.removeEventListener("mouseenter", mouseenter);
70
- item.removeEventListener("mouseleave", mouseleave);
91
+ if (mouseenter && mouseleave) {
92
+ item.removeEventListener("mouseenter", mouseenter);
93
+ item.removeEventListener("mouseleave", mouseleave);
94
+ }
95
+
71
96
  control.removeEventListener("click", click);
72
97
  },
73
98
  clear: () => {
@@ -3,11 +3,11 @@ import T from "prop-types";
3
3
 
4
4
  import Icon from "../Icon/component.jsx";
5
5
 
6
- const MeganavControl = ({ ariaControls, children, theme }) => (
6
+ const MeganavControl = ({ ariaControls, children, theme, additionalCSS }) => (
7
7
  <button
8
8
  type="button"
9
9
  data-id="meganav-control"
10
- className={`ui-meganav-link h-64 flex items-center group ${theme.textColor}`}
10
+ className={`ui-meganav-link h-64 flex items-center group ${additionalCSS} ${theme.textColor}`}
11
11
  aria-expanded="false"
12
12
  aria-controls={ariaControls}
13
13
  aria-label={`Show ${children}`}
@@ -26,6 +26,7 @@ MeganavControl.propTypes = {
26
26
  ariaControls: T.string,
27
27
  children: T.node,
28
28
  theme: T.object,
29
+ additionalCSS: T.string,
29
30
  };
30
31
 
31
32
  export default MeganavControl;
@@ -2,9 +2,11 @@ module AblyUi
2
2
  module Core
3
3
  class MeganavControl < ViewComponent::Base
4
4
  include AblyUi::Core::MeganavConfig
5
+ attr_reader :aria_controls, :additional_css
5
6
 
6
- def initialize(aria_controls:, theme_name:)
7
+ def initialize(aria_controls:, theme_name:, additional_css: '')
7
8
  @aria_controls = aria_controls
9
+ @additional_css = additional_css
8
10
  theme_setup(theme_name)
9
11
  end
10
12
  end
@@ -11,8 +11,36 @@
11
11
  <%= render(AblyUi::Core::MeganavControlMobileDropdown.new(theme_name: @theme_name)) %>
12
12
 
13
13
  <div class="ui-meganav-mobile-dropdown invisible" id="meganav-mobile-dropdown" data-id="meganav-mobile-dropdown">
14
- <div class="py-16 ui-grid-px bg-white">
15
- <ul class="mb-16">
14
+ <div class="pt-24 pb-16 ui-grid-px bg-white">
15
+ <%= tag.form class: "mb-16", action: abs_url("/search"), method: "get" do %>
16
+ <div class="relative w-full">
17
+ <%= render(AblyUi::Core::Icon.new(name: "icon-gui-search", size: "1.5rem", color: "text-cool-black", additional_css: "absolute top-12 left-16 hover:text-gui-hover")) %>
18
+ <button
19
+ type="button"
20
+ class="absolute top-12 right-16 p-0 focus:outline-gui-focus m-0 md:hidden invisible"
21
+ data-id="meganav-search-input-clear"
22
+ >
23
+ <%= render(AblyUi::Core::Icon.new(name: "icon-gui-cross-circled-fill", size: "1.5rem", color: "text-dark-grey")) %>
24
+ </button>
25
+ <input
26
+ type="search"
27
+ name="q"
28
+ class="ui-input px-48 h-48"
29
+ style={{ maxWidth: "none" }}
30
+ placeholder="Search"
31
+ autocomplete="off"
32
+ data-id="meganav-mobile-search-input"
33
+ />
34
+
35
+ <%= render(AblyUi::Core::MeganavSearchAutocomplete.new) %>
36
+ </div>
37
+ <% end %>
38
+
39
+ <div class="max-h-0 overflow-hidden transition-all" data-id="meganav-mobile-search-suggestions">
40
+ <%= render(AblyUi::Core::MeganavSearchSuggestions.new(url_base: url_base, display_support_link: false)) %>
41
+ </div>
42
+
43
+ <ul class="mb-16" data-id="meganav-mobile-panel-controls">
16
44
  <% panels.each do |panel| %>
17
45
  <li class="ui-meganav-mobile-item">
18
46
  <%= render(AblyUi::Core::MeganavControlMobilePanelOpen.new(aria_controls: "#{panel[:id]}-mobile")) do %>
@@ -2,11 +2,14 @@ import React from "react";
2
2
  import T from "prop-types";
3
3
 
4
4
  import SignOutLink from "../SignOutLink/component.jsx";
5
+ import MeganavSearchSuggestions from "../MeganavSearchSuggestions/component.jsx";
6
+ import Icon from "../Icon/component.jsx";
5
7
 
6
8
  import MeganavData from "../Meganav/component.json";
7
9
  import MeganavControlMobileDropdown from "../MeganavControlMobileDropdown/component.jsx";
8
10
  import MeganavControlMobilePanelClose from "../MeganavControlMobilePanelClose/component.jsx";
9
11
  import MeganavControlMobilePanelOpen from "../MeganavControlMobilePanelOpen/component.jsx";
12
+ import MeganavSearchAutocomplete from "../MeganavSearchAutocomplete/component.jsx";
10
13
 
11
14
  const MeganavItemsMobile = ({ panels, paths, sessionState, theme, loginLink, absUrl }) => {
12
15
  const classNames = `ui-meganav-link ${theme.textColor}`;
@@ -33,8 +36,36 @@ const MeganavItemsMobile = ({ panels, paths, sessionState, theme, loginLink, abs
33
36
  <MeganavControlMobileDropdown theme={theme} />
34
37
 
35
38
  <div className="ui-meganav-mobile-dropdown invisible" id="meganav-mobile-dropdown" data-id="meganav-mobile-dropdown">
36
- <div className="py-16 ui-grid-px bg-white">
37
- <ul className="mb-16">
39
+ <div className="pt-24 pb-16 ui-grid-px bg-white">
40
+ <form className="mb-16" action={absUrl("/search")} method="get">
41
+ <div className="relative w-full">
42
+ <Icon name="icon-gui-search" color="text-cool-black" size="1.5rem" additionalCSS="absolute top-12 left-16 hover:text-gui-hover" />
43
+ <button
44
+ type="button"
45
+ className="absolute top-12 right-16 p-0 focus:outline-gui-focus m-0 md:hidden invisible"
46
+ data-id="meganav-search-input-clear"
47
+ >
48
+ <Icon name="icon-gui-cross-circled-fill" color="text-dark-grey" size="1.5rem" additionalCSS="" />
49
+ </button>
50
+ <input
51
+ type="search"
52
+ name="q"
53
+ className="ui-input px-48 h-48"
54
+ style={{ maxWidth: "none" }}
55
+ placeholder="Search"
56
+ autoComplete="off"
57
+ data-id="meganav-mobile-search-input"
58
+ />
59
+
60
+ <MeganavSearchAutocomplete />
61
+ </div>
62
+ </form>
63
+
64
+ <div className="max-h-0 overflow-hidden transition-all" data-id="meganav-mobile-search-suggestions">
65
+ <MeganavSearchSuggestions absUrl={absUrl} displaySupportLink={false} />
66
+ </div>
67
+
68
+ <ul className="mb-16" data-id="meganav-mobile-panel-controls">
38
69
  {MeganavData.panels.map((panel) => {
39
70
  const PanelComponent = panels[panel.component];
40
71
 
@@ -1,6 +1,6 @@
1
1
  <ul class="hidden md:flex items-center">
2
2
  <li class="ui-meganav-item relative">
3
- <%= render(AblyUi::Core::MeganavControl.new(aria_controls: "account-panel", theme_name: @theme_name)) do %>
3
+ <%= render(AblyUi::Core::MeganavControl.new(aria_controls: "account-panel", theme_name: @theme_name, additional_css: "mr-0")) do %>
4
4
  <%= account_name %>
5
5
  <% end %>
6
6
 
@@ -41,8 +41,12 @@
41
41
  </div>
42
42
  </li>
43
43
 
44
+ <li>
45
+ <%= render(AblyUi::Core::MeganavSearch.new(url_base: url_base)) %>
46
+ </li>
47
+
44
48
  <% if account? %>
45
- <li class="ml-16">
49
+ <li>
46
50
  <%= link_to "Dashboard", @session_data[:account][:links][:dashboard][:href], class: "ui-btn-secondary p-btn-small" %>
47
51
  </li>
48
52
  <% end %>
@@ -3,6 +3,7 @@ import T from "prop-types";
3
3
 
4
4
  import MeganavControl from "../MeganavControl/component.jsx";
5
5
  import SignOutLink from "../SignOutLink/component.jsx";
6
+ import MeganavSearch from "../MeganavSearch/component.jsx";
6
7
 
7
8
  const truncate = (string, length) => {
8
9
  return string?.length && string.length > length ? `${string.slice(0, length - 1)}…` : string;
@@ -16,7 +17,7 @@ const MeganavItemsSignedIn = ({ sessionState, theme, absUrl }) => {
16
17
  return (
17
18
  <ul className="hidden md:flex items-center">
18
19
  <li className="ui-meganav-item relative">
19
- <MeganavControl ariaControls="account-panel" theme={theme}>
20
+ <MeganavControl ariaControls="account-panel" theme={theme} additionalCSS="mr-0">
20
21
  {accountName}
21
22
  </MeganavControl>
22
23
 
@@ -70,8 +71,12 @@ const MeganavItemsSignedIn = ({ sessionState, theme, absUrl }) => {
70
71
  </div>
71
72
  </li>
72
73
 
74
+ <li>
75
+ <MeganavSearch absUrl={absUrl} />
76
+ </li>
77
+
73
78
  {sessionState.account && (
74
- <li className="ml-16">
79
+ <li>
75
80
  <a href={absUrl(sessionState.account.links.dashboard.href)} className="ui-btn-secondary p-btn-small">
76
81
  Dashboard
77
82
  </a>
@@ -0,0 +1,15 @@
1
+ <button
2
+ type="button"
3
+ data-id="meganav-control"
4
+ data-control="search"
5
+ class="h-64 w-24 px-24 pr-48 py-20 group focus:outline-none"
6
+ aria-expanded="false"
7
+ aria-controls="panel-search"
8
+ aria-label="Show Search Panel"
9
+ >
10
+ <%= render(AblyUi::Core::Icon.new(name: "icon-gui-search", size: "1.5rem", color: "text-cool-black", additional_css: "group-hover:text-gui-hover group-focus:text-gui-focus")) %>
11
+ </button>
12
+
13
+ <div class="ui-meganav-panel invisible" id="panel-search" data-id="meganav-panel">
14
+ <%= render(AblyUi::Core::MeganavSearchPanel.new(url_base: url_base)) %>
15
+ </div>
File without changes
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import T from "prop-types";
3
+
4
+ import Icon from "../Icon/component.jsx";
5
+ import MeganavSearchPanel from "../MeganavSearchPanel/component.jsx";
6
+
7
+ const MeganavSearch = ({ absUrl }) => {
8
+ return (
9
+ <>
10
+ <button
11
+ type="button"
12
+ data-id="meganav-control"
13
+ data-control="search"
14
+ className="h-64 w-24 px-24 pr-48 py-20 group focus:outline-none"
15
+ aria-expanded="false"
16
+ aria-controls="panel-search"
17
+ aria-label={`Show Search Panel`}
18
+ >
19
+ <Icon name="icon-gui-search" color="text-cool-black" size="1.5rem" additionalCSS="group-hover:text-gui-hover group-focus:text-gui-focus" />
20
+ </button>
21
+
22
+ <div className="ui-meganav-panel invisible" id="panel-search" data-id="meganav-panel">
23
+ <MeganavSearchPanel absUrl={absUrl} />
24
+ </div>
25
+ </>
26
+ );
27
+ };
28
+
29
+ MeganavSearch.propTypes = {
30
+ absUrl: T.func,
31
+ };
32
+
33
+ export default MeganavSearch;
@@ -0,0 +1,13 @@
1
+ module AblyUi
2
+ module Core
3
+ class MeganavSearch < ViewComponent::Base
4
+ include Util
5
+
6
+ attr_reader :url_base
7
+
8
+ def initialize(url_base:)
9
+ @url_base = url_base
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ <div
2
+ class="absolute w-full mt-8 z-10 hidden shadow-container rounded-lg bg-white border border-mid-grey"
3
+ data-id="meganav-search-autocomplete-container"
4
+ >
5
+ <ol class="m-16" data-id="meganav-search-autocomplete-list"></ol>
6
+ </div>