@ably/ui 7.10.0-dev.e980a00 → 8.0.0-dev.467d57d

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 (32) hide show
  1. package/core/Meganav/component.js +1 -1
  2. package/core/Meganav.jsx +8471 -6616
  3. package/core/MeganavControlMobileDropdown/component.js +1 -1
  4. package/core/MeganavItemsMobile.jsx +7 -6
  5. package/core/MeganavItemsSignedIn.jsx +9 -8
  6. package/core/MeganavSearch.jsx +9 -8
  7. package/core/MeganavSearchAutocomplete/component.js +1 -1
  8. package/core/MeganavSearchPanel.jsx +7 -6
  9. package/core/MeganavSearchSuggestions/component.js +1 -1
  10. package/core/MeganavSearchSuggestions.jsx +6 -6
  11. package/core/fonts/source-code-pro.css +3 -0
  12. package/core/styles.css +17 -22
  13. package/package.json +2 -2
  14. package/src/core/Code/component.css +1 -3
  15. package/src/core/Meganav/component.js +8 -2
  16. package/src/core/Meganav/component.jsx +3 -2
  17. package/src/core/MeganavControlMobileDropdown/component.js +0 -31
  18. package/src/core/MeganavItemsMobile/component.html.erb +1 -0
  19. package/src/core/MeganavItemsMobile/component.jsx +1 -0
  20. package/src/core/MeganavItemsSignedIn/component.html.erb +2 -2
  21. package/src/core/MeganavSearch/component.html.erb +2 -2
  22. package/src/core/MeganavSearch/component.jsx +2 -2
  23. package/src/core/MeganavSearchAutocomplete/component.js +18 -10
  24. package/src/core/MeganavSearchPanel/component.html.erb +3 -3
  25. package/src/core/MeganavSearchPanel/component.jsx +1 -1
  26. package/src/core/MeganavSearchSuggestions/component.html.erb +4 -4
  27. package/src/core/MeganavSearchSuggestions/component.js +123 -0
  28. package/src/core/MeganavSearchSuggestions/component.jsx +6 -6
  29. package/src/core/fonts/source-code-pro.css +3 -0
  30. package/src/core/styles/properties.css +4 -3
  31. package/src/core/styles/text.css +12 -16
  32. package/tailwind.config.js +12 -4
package/core/styles.css CHANGED
@@ -15,9 +15,10 @@
15
15
  --color-charcoal-grey: #292831;
16
16
  --color-gui-default: #0073e6;
17
17
  --color-gui-hover: #0867c4;
18
- --color-gui-focus: #80b9f2;
18
+ --color-gui-focus: #0073e6;
19
+ --color-gui-focus-outline: #80b9f2;
19
20
  --color-gui-active: #074095;
20
- --color-gui-viewed: #4887c2;
21
+ --color-gui-visited: #4887c2;
21
22
  --color-gui-unavailable: #a8a8a8;
22
23
  --color-gui-error: #fb0c0c;
23
24
  --color-gui-success: #11cb24;
@@ -157,7 +158,7 @@
157
158
  /* In components, when looking at implementing viewport margin and spacing between elements,
158
159
  the values in the comments can be used as guide as they represent the grid the elements (should) sit on.
159
160
  alternatively, look for ui-grid-* helpers. */
160
- --bp-xs: 375px; /* gutters 8px, side-margin 24px */
161
+ --bp-xs: 428px; /* gutters 8px, side-margin 24px */
161
162
  --bp-sm: 768px; /* gutters 16px, side-margin 32px */
162
163
  --bp-md: 1040px; /* gutters 24px, side-margin 40px, meganav desktop */
163
164
  --bp-lg: 1280px; /* gutters 24px, side-margin 64px */
@@ -345,17 +346,17 @@
345
346
  }
346
347
 
347
348
  .ui-text-p1 {
348
- @apply font-sans font-light text-cool-black;
349
+ @apply font-sans font-light text-charcoal-grey;
349
350
  @apply text-p1;
350
351
  }
351
352
 
352
353
  .ui-text-p2 {
353
- @apply font-sans font-light text-cool-black;
354
+ @apply font-sans font-light text-charcoal-grey;
354
355
  @apply text-p2;
355
356
  }
356
357
 
357
358
  .ui-text-p3 {
358
- @apply font-sans font-light text-cool-black;
359
+ @apply font-sans font-light text-charcoal-grey;
359
360
  @apply text-p3;
360
361
  }
361
362
 
@@ -461,24 +462,18 @@
461
462
  @apply list-square;
462
463
  }
463
464
 
464
- /* visited needs to come before :hover et all else it overrides them */
465
- .ui-link:visited {
466
- @apply text-gui-viewed;
465
+ .ui-link {
466
+ @apply visited:text-gui-visited;
467
+ @apply hover:text-gui-hover active:text-gui-active disabled:text-gui-unavailable;
468
+ @apply focus:text-gui-focus focus:outline-gui-focus;
469
+ @apply underline;
467
470
  }
468
471
 
469
- .ui-link {
470
- @apply hover:text-active-orange active:text-red-orange;
471
- -webkit-text-decoration-color: var(--color-active-orange);
472
- text-decoration-color: var(--color-active-orange);
473
- text-underline-offset: 4px; /* px used here as behaves weird with rem's */
474
- -webkit-text-decoration-line: underline;
475
- text-decoration-line: underline;
476
- text-decoration-thickness: 0.125rem;
477
- }
478
-
479
- .ui-link:focus {
480
- @apply focus:text-white focus:bg-active-orange focus:outline-none;
481
- text-decoration: none;
472
+ .ui-link-neutral {
473
+ @apply visited:text-dark-grey;
474
+ @apply hover:text-dark-grey active:text-cool-black disabled:text-gui-unavailable;
475
+ @apply focus:text-gui-focus focus:outline-gui-focus-neutral;
476
+ @apply underline;
482
477
  }
483
478
  }
484
479
  @layer components {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ably/ui",
3
- "version": "7.10.0-dev.e980a00",
3
+ "version": "8.0.0-dev.467d57d",
4
4
  "description": "Home of the Ably design system library ([design.ably.com](https://design.ably.com)). It provides a showcase, development/test environment and a publishing pipeline for different distributables.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -65,7 +65,7 @@
65
65
  "release": "./scripts/release.sh"
66
66
  },
67
67
  "dependencies": {
68
- "addsearch-js-client": "^0.6.7",
68
+ "addsearch-js-client": "^0.7.0",
69
69
  "array-flat-polyfill": "^1.0.1",
70
70
  "deepmerge": "^4.2.2",
71
71
  "dompurify": "^2.2.9",
@@ -1,6 +1,4 @@
1
- @layer base {
2
- @import url("https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@600&display=swap");
3
- }
1
+ @import "../fonts/source-code-pro.css";
4
2
 
5
3
  @layer components {
6
4
  .hljs {
@@ -18,6 +18,7 @@ import MeganavControlMobileDropdown from "../MeganavControlMobileDropdown/compon
18
18
  import MobilePanelOpenClick from "../MeganavControlMobilePanelOpen/component";
19
19
  import MobilePanelCloseClick from "../MeganavControlMobilePanelClose/component";
20
20
  import MeganavSearchAutocomplete from "../MeganavSearchAutocomplete/component";
21
+ import MeganavSearchSuggestions from "../MeganavSearchSuggestions/component";
21
22
 
22
23
  // Close menu when user clicks outside of viewport
23
24
  const windowOnBlur = (closeAll) => {
@@ -112,11 +113,14 @@ const documentScroll = (themeName) => {
112
113
  };
113
114
  };
114
115
 
115
- export default function Meganav({ themeName } = { themeName: null }) {
116
+ export default function Meganav(
117
+ { themeName, addSearchApiKey } = { themeName: null }
118
+ ) {
116
119
  const controls = MeganavControl();
117
120
  const panelOpenControls = MobilePanelOpenClick();
118
121
  const panelCloseControls = MobilePanelCloseClick();
119
- const search = MeganavSearchAutocomplete();
122
+ const search = MeganavSearchAutocomplete(addSearchApiKey);
123
+ const searchSuggestions = MeganavSearchSuggestions();
120
124
 
121
125
  const mobileDropdownControl = MeganavControlMobileDropdown({
122
126
  clearPanels: () =>
@@ -126,6 +130,7 @@ export default function Meganav({ themeName } = { themeName: null }) {
126
130
  const closeAll = () =>
127
131
  [
128
132
  mobileDropdownControl,
133
+ searchSuggestions,
129
134
  ...panelOpenControls,
130
135
  ...panelCloseControls,
131
136
  ...controls,
@@ -137,6 +142,7 @@ export default function Meganav({ themeName } = { themeName: null }) {
137
142
  documentClick(closeAll),
138
143
  windowOnBlur(closeAll),
139
144
  mobileDropdownControl,
145
+ searchSuggestions,
140
146
  ...controls,
141
147
  ...panelOpenControls,
142
148
  ...panelCloseControls,
@@ -64,7 +64,7 @@ const panels = {
64
64
  MeganavContentDevelopers: MeganavContentDevelopers,
65
65
  };
66
66
 
67
- export default function Meganav({ paths, themeName = "white", notice, loginLink = "/login", urlBase }) {
67
+ export default function Meganav({ paths, themeName = "white", notice, loginLink = "/login", urlBase, addSearchApiKey }) {
68
68
  const [sessionState, setSessionState] = useState(null);
69
69
 
70
70
  useEffect(() => {
@@ -74,7 +74,7 @@ export default function Meganav({ paths, themeName = "white", notice, loginLink
74
74
  }, []);
75
75
 
76
76
  useEffect(() => {
77
- const teardown = MeganavScripts({ themeName });
77
+ const teardown = MeganavScripts({ themeName, addSearchApiKey });
78
78
  return () => teardown();
79
79
  }, [sessionState]);
80
80
 
@@ -119,4 +119,5 @@ Meganav.propTypes = {
119
119
  }),
120
120
  loginLink: T.string,
121
121
  urlBase: T.string,
122
+ addSearchApiKey: T.string,
122
123
  };
@@ -7,8 +7,6 @@ const MeganavControlMobileDropdown = ({ clearPanels }) => {
7
7
  const dropdown = queryId("meganav-mobile-dropdown");
8
8
  const menuIcon = queryId("meganav-control-mobile-dropdown-menu");
9
9
  const closeIcon = queryId("meganav-control-mobile-dropdown-close");
10
- const meganavSearchSuggestionsToggle = queryId("meganav-mobile-search-input");
11
- const meganavSearchSuggestions = queryId("meganav-mobile-search-suggestions");
12
10
 
13
11
  const clickHandler = () => {
14
12
  const ariaExpanded = control.getAttribute("aria-expanded");
@@ -28,40 +26,12 @@ const MeganavControlMobileDropdown = ({ clearPanels }) => {
28
26
  closeIcon.classList.toggle("hidden");
29
27
  };
30
28
 
31
- const focusSuggestionsHandler = () => {
32
- meganavSearchSuggestions.classList.add("max-h-96");
33
- };
34
- const blurSuggestionsHandler = (event) => {
35
- if (
36
- event.relatedTarget === meganavSearchSuggestions.querySelectorAll("a")[0]
37
- ) {
38
- return;
39
- }
40
- meganavSearchSuggestions.classList.remove("max-h-96");
41
- };
42
-
43
29
  control.addEventListener("click", clickHandler);
44
- meganavSearchSuggestionsToggle.addEventListener(
45
- "focus",
46
- focusSuggestionsHandler
47
- );
48
- meganavSearchSuggestionsToggle.addEventListener(
49
- "blur",
50
- blurSuggestionsHandler
51
- );
52
30
 
53
31
  return {
54
32
  teardown: () => {
55
33
  control.removeEventListener("click", clickHandler);
56
34
  scrollLock.enablePageScroll();
57
- meganavSearchSuggestionsToggle.removeEventListener(
58
- "focus",
59
- focusSuggestionsHandler
60
- );
61
- meganavSearchSuggestionsToggle.removeEventListener(
62
- "blur",
63
- blurSuggestionsHandler
64
- );
65
35
  },
66
36
  clear: () => {
67
37
  dropdown.classList.replace("visible", "invisible");
@@ -69,7 +39,6 @@ const MeganavControlMobileDropdown = ({ clearPanels }) => {
69
39
  menuIcon.classList.remove("hidden");
70
40
  closeIcon.classList.add("hidden");
71
41
  scrollLock.enablePageScroll();
72
- meganavSearchSuggestions.classList.remove("max-h-96");
73
42
  },
74
43
  };
75
44
  };
@@ -28,6 +28,7 @@
28
28
  class="ui-input px-48 h-48"
29
29
  style={{ maxWidth: "none" }}
30
30
  placeholder="Search"
31
+ autocomplete="off"
31
32
  data-id="meganav-mobile-search-input"
32
33
  />
33
34
 
@@ -53,6 +53,7 @@ const MeganavItemsMobile = ({ panels, paths, sessionState, theme, loginLink, abs
53
53
  className="ui-input px-48 h-48"
54
54
  style={{ maxWidth: "none" }}
55
55
  placeholder="Search"
56
+ autoComplete="off"
56
57
  data-id="meganav-mobile-search-input"
57
58
  />
58
59
 
@@ -43,10 +43,10 @@
43
43
 
44
44
  <li>
45
45
  <%= render(AblyUi::Core::MeganavSearch.new(url_base: url_base)) %>
46
- <li>
46
+ </li>
47
47
 
48
48
  <% if account? %>
49
- <li class="ml-16">
49
+ <li>
50
50
  <%= link_to "Dashboard", @session_data[:account][:links][:dashboard][:href], class: "ui-btn-secondary p-btn-small" %>
51
51
  </li>
52
52
  <% end %>
@@ -2,12 +2,12 @@
2
2
  type="button"
3
3
  data-id="meganav-control"
4
4
  data-control="search"
5
- class="h-64 w-24 px-24 pr-48 py-20"
5
+ class="h-64 w-24 px-24 pr-48 py-20 group focus:outline-none"
6
6
  aria-expanded="false"
7
7
  aria-controls="panel-search"
8
8
  aria-label="Show Search Panel"
9
9
  >
10
- <%= render(AblyUi::Core::Icon.new(name: "icon-gui-search", size: "1.5rem", color: "text-cool-black", additional_css: "hover:text-gui-hover")) %>
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
11
  </button>
12
12
 
13
13
  <div class="ui-meganav-panel invisible" id="panel-search" data-id="meganav-panel">
@@ -11,12 +11,12 @@ const MeganavSearch = ({ absUrl }) => {
11
11
  type="button"
12
12
  data-id="meganav-control"
13
13
  data-control="search"
14
- className="h-64 w-24 px-24 pr-48 py-20"
14
+ className="h-64 w-24 px-24 pr-48 py-20 group focus:outline-none"
15
15
  aria-expanded="false"
16
16
  aria-controls="panel-search"
17
17
  aria-label={`Show Search Panel`}
18
18
  >
19
- <Icon name="icon-gui-search" color="text-cool-black" size="1.5rem" additionalCSS="hover:text-gui-hover" />
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
20
  </button>
21
21
 
22
22
  <div className="ui-meganav-panel invisible" id="panel-search" data-id="meganav-panel">
@@ -18,8 +18,8 @@ const init = ({ input, container, listContainer, clear, client }) => {
18
18
  }
19
19
  };
20
20
 
21
- const markQueryInSuggestion = (suggestion, query) => {
22
- return suggestion.value.replace(
21
+ const markQueryInSuggestion = (suggestionValue, query) => {
22
+ return suggestionValue.replace(
23
23
  query.toLowerCase(),
24
24
  `<span class="font-light">${query}</span>`
25
25
  );
@@ -47,9 +47,16 @@ const init = ({ input, container, listContainer, clear, client }) => {
47
47
 
48
48
  const renderResults =
49
49
  (query) =>
50
- (results = { suggestions: [] }) => {
50
+ (results = {}) => {
51
51
  toggleClearBtn(query);
52
52
 
53
+ // Prevent invalid access error when key is invalid
54
+ if (!Array.isArray(results.suggestions)) {
55
+ clearResults();
56
+ return;
57
+ }
58
+
59
+ // Prevent key error from invalid key
53
60
  if (results.suggestions.length === 0) {
54
61
  clearResults();
55
62
  return;
@@ -58,6 +65,7 @@ const init = ({ input, container, listContainer, clear, client }) => {
58
65
  const items = results.suggestions.map((suggestion, index) => {
59
66
  const li = document.createElement("li");
60
67
  const button = document.createElement("button");
68
+ button.type = "button";
61
69
 
62
70
  button.classList.add(
63
71
  "ui-text-menu2",
@@ -71,25 +79,25 @@ const init = ({ input, container, listContainer, clear, client }) => {
71
79
  "hover:bg-light-grey"
72
80
  );
73
81
 
74
- button.innerHTML = markQueryInSuggestion(suggestion, query);
82
+ button.innerHTML = markQueryInSuggestion(suggestion.value, query);
75
83
 
76
84
  button.dataset.suggestionIndex = index;
77
85
 
78
86
  button.addEventListener("click", () => {
79
- navigateToUrl(suggestion);
87
+ navigateToUrl(suggestion.value);
80
88
  });
81
89
 
82
90
  button.addEventListener("keydown", (e) => {
83
91
  const key = e.key;
84
92
 
85
93
  if (key === "ArrowDown") {
86
- focusNext();
94
+ focusNext(index);
87
95
  } else if (key === "ArrowUp" && index - 1 < 0) {
88
96
  input.focus();
89
97
  } else if (key === "ArrowUp" && index - 1 >= 0) {
90
- focusPrevious();
98
+ focusPrevious(index);
91
99
  } else if (key === "Enter" || key === "Space") {
92
- navigateToUrl(suggestion);
100
+ navigateToUrl(suggestion.value);
93
101
  }
94
102
  });
95
103
 
@@ -142,12 +150,12 @@ const init = ({ input, container, listContainer, clear, client }) => {
142
150
  };
143
151
  };
144
152
 
145
- export default () => {
146
- const apiKey = document.body.dataset.addSearchApiKey;
153
+ export default (apiKey) => {
147
154
  if (!apiKey) {
148
155
  console.log(`No AddSearch API key provided, skipping search suggestions.`);
149
156
  return [];
150
157
  }
158
+
151
159
  const client = new AddSearchClient(apiKey);
152
160
 
153
161
  return [
@@ -1,10 +1,10 @@
1
1
  <section class="ui-meganav-content grid-cols-12">
2
2
  <div class="col-span-8">
3
3
  <div class="mb-32">
4
- <form class="flex items-start" action={absUrl("/search")} method="get">
4
+ <%= tag.form class: "flex items-start", action: abs_url("/search"), method: "get" do %>
5
5
  <div class="relative w-full">
6
6
  <%= render(AblyUi::Core::Icon.new(name: "icon-gui-search", size: "1.5rem", color: "text-cool-black", additional_css:"absolute top-12 left-16")) %>
7
- <input type="search" name="q" class="ui-input pl-48 h-48" placeholder="Search" data-id="meganav-search-input" />
7
+ <input type="search" name="q" class="ui-input pl-48 h-48" placeholder="Search" autocomplete="off" data-id="meganav-search-input" />
8
8
 
9
9
  <%= render(AblyUi::Core::MeganavSearchAutocomplete.new) %>
10
10
  </div>
@@ -12,7 +12,7 @@
12
12
  <button type="submit" class="ui-btn-secondary ml-8 sm:ml-16 md:ml-24 xl:ml-32">
13
13
  Search
14
14
  </button>
15
- </form>
15
+ <% end %>
16
16
  </div>
17
17
  </div>
18
18
 
@@ -13,7 +13,7 @@ const MeganavSearchPanel = ({ absUrl }) => {
13
13
  <form className="flex items-start" action={absUrl("/search")} method="get">
14
14
  <div className="relative w-full">
15
15
  <Icon name="icon-gui-search" color="text-cool-black" size="1.5rem" additionalCSS="absolute top-12 left-16" />
16
- <input type="search" name="q" className="ui-input pl-48 h-48" placeholder="Search" data-id="meganav-search-input" />
16
+ <input type="search" name="q" className="ui-input pl-48 h-48" placeholder="Search" autoComplete="off" data-id="meganav-search-input" />
17
17
 
18
18
  <MeganavSearchAutocomplete />
19
19
  </div>
@@ -3,16 +3,16 @@
3
3
  <div class="flex justify-between items-center overflow-x-scroll md:overflow-auto">
4
4
  <ul class="flex">
5
5
  <li class="py-12 pr-8 flex-shrink-0">
6
- <%= link_to 'How does Ably work?', abs_url("/docs/how-ably-works"), class: "ui-text-p2" %>
6
+ <%= link_to 'How does Ably work?', abs_url("/docs/how-ably-works"), class: "ui-text-p2 hover:text-gui-hover active:text-gui-active focus:text-gui-focus" %>
7
7
  </li>
8
8
  <li class="py-12 px-8 flex-shrink-0">
9
- <%= link_to 'Quickstart guide', abs_url("/docs/quick-start-guide"), class: "ui-text-p2" %>
9
+ <%= link_to 'Quickstart guide', abs_url("/docs/quick-start-guide"), class: "ui-text-p2 hover:text-gui-hover active:text-gui-active focus:text-gui-focus" %>
10
10
  </li>
11
11
  <li class="py-12 px-8 flex-shrink-0">
12
- <%= link_to 'Publish/Subscribe Messaging', abs_url("/docs/core-features/pubsub"), class: "ui-text-p2" %>
12
+ <%= link_to 'Publish/Subscribe Messaging', abs_url("/docs/core-features/pubsub"), class: "ui-text-p2 hover:text-gui-hover active:text-gui-active focus:text-gui-focus" %>
13
13
  </li>
14
14
  <li class="py-12 pl-8 flex-shrink-0">
15
- <%= link_to 'Platform', abs_url("/docs/how-ably-works"), class: "ui-text-p2" %>
15
+ <%= link_to 'Platform', abs_url("/docs/how-ably-works"), class: "ui-text-p2 hover:text-gui-hover active:text-gui-active focus:text-gui-focus" %>
16
16
  </li>
17
17
  </ul>
18
18
 
@@ -0,0 +1,123 @@
1
+ import { queryId } from "../dom-query";
2
+
3
+ const DRAG_BUFFER = 5;
4
+
5
+ const getTranslateX = (node) =>
6
+ new DOMMatrix(window.getComputedStyle(node).transform).e;
7
+
8
+ const updateTranslateX = (node, value) =>
9
+ (node.style.transform = `translateX(${value}px)`);
10
+
11
+ const dragLeftBoundary = (translateX, threshold) => translateX >= threshold;
12
+
13
+ const dragRightBoundary = (translateX, itemsWidth, windowWidth, threshold) =>
14
+ Math.abs(translateX - windowWidth + threshold) > itemsWidth;
15
+
16
+ const getDistance = (e, touchStartX) =>
17
+ e.changedTouches[0]?.clientX - touchStartX;
18
+
19
+ const withinBuffer = (distance) => Math.abs(distance) < DRAG_BUFFER;
20
+
21
+ const MeganavSearchSuggestions = () => {
22
+ const suggestionsToggle = queryId("meganav-mobile-search-input");
23
+ const suggestions = queryId("meganav-mobile-search-suggestions");
24
+ const list = suggestions.querySelector("ul");
25
+ const listItems = list.querySelectorAll("li");
26
+
27
+ const itemsTotalWidth = Array.from(listItems)
28
+ .map((item) => item.getBoundingClientRect().width)
29
+ .reduce((acc, val) => acc + val, 0);
30
+
31
+ const dragLeft = (distance, threshold) => {
32
+ const currentTranslateX = getTranslateX(list);
33
+ const translateX = Math.round(currentTranslateX + distance);
34
+ if (dragLeftBoundary(translateX, threshold)) return;
35
+ updateTranslateX(list, translateX);
36
+ };
37
+
38
+ const dragLeftEnd = (distance, threshold) => {
39
+ const currentTranslateX = getTranslateX(list);
40
+ let translateX = Math.round(currentTranslateX + distance);
41
+
42
+ if (dragLeftBoundary(translateX, threshold)) {
43
+ translateX = 0;
44
+ }
45
+
46
+ updateTranslateX(list, translateX);
47
+ };
48
+
49
+ const dragRight = (distance, threshold) => {
50
+ const listWidth = list.getBoundingClientRect().width;
51
+ const currentTranslateX = getTranslateX(list);
52
+ const translateX = Math.round(currentTranslateX + distance);
53
+
54
+ if (dragRightBoundary(translateX, itemsTotalWidth, listWidth, threshold)) {
55
+ return;
56
+ }
57
+
58
+ updateTranslateX(list, translateX);
59
+ };
60
+
61
+ const dragRightEnd = (distance, threshold) => {
62
+ const listWidth = list.getBoundingClientRect().width;
63
+ const currentTranslateX = getTranslateX(list);
64
+ let translateX = Math.round(currentTranslateX + distance);
65
+
66
+ if (dragRightBoundary(translateX, itemsTotalWidth, listWidth, threshold)) {
67
+ translateX = -(itemsTotalWidth - listWidth + threshold);
68
+ }
69
+
70
+ updateTranslateX(list, translateX);
71
+ };
72
+
73
+ let touchStartX;
74
+
75
+ const touchstartHandler = (e) => {
76
+ touchStartX = e.touches[0]?.clientX;
77
+ };
78
+
79
+ const touchmoveHandler = (e) => {
80
+ const distance = getDistance(e, touchStartX);
81
+ if (withinBuffer(distance)) return;
82
+ distance > 0 ? dragLeft(distance, 24) : dragRight(distance, 96);
83
+ };
84
+
85
+ const touchendHandler = (e) => {
86
+ const distance = getDistance(e, touchStartX);
87
+ if (withinBuffer(distance)) return;
88
+ distance > 0 ? dragLeftEnd(distance, 24) : dragRightEnd(distance, 48);
89
+ };
90
+
91
+ const focusSuggestionsHandler = () => {
92
+ suggestions.classList.add("max-h-96");
93
+ };
94
+
95
+ const blurSuggestionsHandler = (e) => {
96
+ if (e.relatedTarget === suggestions.querySelectorAll("a")[0]) {
97
+ return;
98
+ }
99
+ suggestions.classList.remove("max-h-96");
100
+ };
101
+
102
+ suggestionsToggle.addEventListener("focus", focusSuggestionsHandler);
103
+ suggestionsToggle.addEventListener("blur", blurSuggestionsHandler);
104
+ suggestions.addEventListener("touchstart", touchstartHandler);
105
+ suggestions.addEventListener("touchmove", touchmoveHandler);
106
+ suggestions.addEventListener("touchend", touchendHandler);
107
+
108
+ return {
109
+ teardown: () => {
110
+ suggestionsToggle.removeEventListener("focus", focusSuggestionsHandler);
111
+ suggestionsToggle.removeEventListener("blur", blurSuggestionsHandler);
112
+ suggestions.removeEventListener("touchstart", touchstartHandler);
113
+ suggestions.removeEventListener("touchmove", touchmoveHandler);
114
+ suggestions.removeEventListener("touchend", touchendHandler);
115
+ },
116
+ clear: () => {
117
+ suggestions.classList.remove("max-h-96");
118
+ list.style.transform = `translateX(0px)`;
119
+ },
120
+ };
121
+ };
122
+
123
+ export default MeganavSearchSuggestions;
@@ -8,25 +8,25 @@ const MeganavSearchSuggestions = ({ absUrl, displaySupportLink }) => {
8
8
  <>
9
9
  <p className="ui-text-overline2 text-cool-black py-12">Popular pages</p>
10
10
 
11
- <div className="flex justify-between items-center overflow-x-scroll md:overflow-auto">
12
- <ul className="flex">
11
+ <div className="flex justify-between items-center overflow-hidden">
12
+ <ul className="flex transition-transform">
13
13
  <li className="py-12 pr-8 flex-shrink-0">
14
- <a href={absUrl("/docs/how-ably-works")} className="ui-text-p2">
14
+ <a href={absUrl("/docs/how-ably-works")} className="ui-text-p2 hover:text-gui-hover active:text-gui-active focus:text-gui-focus">
15
15
  How does Ably work?
16
16
  </a>
17
17
  </li>
18
18
  <li className="py-12 px-8 flex-shrink-0">
19
- <a href={absUrl("/docs/quick-start-guide")} className="ui-text-p2">
19
+ <a href={absUrl("/docs/quick-start-guide")} className="ui-text-p2 hover:text-gui-hover active:text-gui-active focus:text-gui-focus">
20
20
  Quickstart guide
21
21
  </a>
22
22
  </li>
23
23
  <li className="py-12 px-8 flex-shrink-0">
24
- <a href={absUrl("/docs/core-features/pubsub")} className="ui-text-p2">
24
+ <a href={absUrl("/docs/core-features/pubsub")} className="ui-text-p2 hover:text-gui-hover active:text-gui-active focus:text-gui-focus">
25
25
  Publish/Subscribe Messaging
26
26
  </a>
27
27
  </li>
28
28
  <li className="py-12 pl-8 flex-shrink-0">
29
- <a href={absUrl("/platform")} className="ui-text-p2">
29
+ <a href={absUrl("/platform")} className="ui-text-p2 hover:text-gui-hover active:text-gui-active focus:text-gui-focus">
30
30
  Platform
31
31
  </a>
32
32
  </li>
@@ -0,0 +1,3 @@
1
+ @layer base {
2
+ @import url("https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@600&display=swap");
3
+ }
@@ -15,9 +15,10 @@
15
15
  --color-charcoal-grey: #292831;
16
16
  --color-gui-default: #0073e6;
17
17
  --color-gui-hover: #0867c4;
18
- --color-gui-focus: #80b9f2;
18
+ --color-gui-focus: #0073e6;
19
+ --color-gui-focus-outline: #80b9f2;
19
20
  --color-gui-active: #074095;
20
- --color-gui-viewed: #4887c2;
21
+ --color-gui-visited: #4887c2;
21
22
  --color-gui-unavailable: #a8a8a8;
22
23
  --color-gui-error: #fb0c0c;
23
24
  --color-gui-success: #11cb24;
@@ -157,7 +158,7 @@
157
158
  /* In components, when looking at implementing viewport margin and spacing between elements,
158
159
  the values in the comments can be used as guide as they represent the grid the elements (should) sit on.
159
160
  alternatively, look for ui-grid-* helpers. */
160
- --bp-xs: 375px; /* gutters 8px, side-margin 24px */
161
+ --bp-xs: 428px; /* gutters 8px, side-margin 24px */
161
162
  --bp-sm: 768px; /* gutters 16px, side-margin 32px */
162
163
  --bp-md: 1040px; /* gutters 24px, side-margin 40px, meganav desktop */
163
164
  --bp-lg: 1280px; /* gutters 24px, side-margin 64px */