@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.
- package/core/Meganav/component.js +1 -1
- package/core/Meganav.jsx +8471 -6616
- package/core/MeganavControlMobileDropdown/component.js +1 -1
- package/core/MeganavItemsMobile.jsx +7 -6
- package/core/MeganavItemsSignedIn.jsx +9 -8
- package/core/MeganavSearch.jsx +9 -8
- package/core/MeganavSearchAutocomplete/component.js +1 -1
- package/core/MeganavSearchPanel.jsx +7 -6
- package/core/MeganavSearchSuggestions/component.js +1 -1
- package/core/MeganavSearchSuggestions.jsx +6 -6
- package/core/fonts/source-code-pro.css +3 -0
- package/core/styles.css +17 -22
- package/package.json +2 -2
- package/src/core/Code/component.css +1 -3
- package/src/core/Meganav/component.js +8 -2
- package/src/core/Meganav/component.jsx +3 -2
- package/src/core/MeganavControlMobileDropdown/component.js +0 -31
- package/src/core/MeganavItemsMobile/component.html.erb +1 -0
- package/src/core/MeganavItemsMobile/component.jsx +1 -0
- package/src/core/MeganavItemsSignedIn/component.html.erb +2 -2
- package/src/core/MeganavSearch/component.html.erb +2 -2
- package/src/core/MeganavSearch/component.jsx +2 -2
- package/src/core/MeganavSearchAutocomplete/component.js +18 -10
- package/src/core/MeganavSearchPanel/component.html.erb +3 -3
- package/src/core/MeganavSearchPanel/component.jsx +1 -1
- package/src/core/MeganavSearchSuggestions/component.html.erb +4 -4
- package/src/core/MeganavSearchSuggestions/component.js +123 -0
- package/src/core/MeganavSearchSuggestions/component.jsx +6 -6
- package/src/core/fonts/source-code-pro.css +3 -0
- package/src/core/styles/properties.css +4 -3
- package/src/core/styles/text.css +12 -16
- 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: #
|
|
18
|
+
--color-gui-focus: #0073e6;
|
|
19
|
+
--color-gui-focus-outline: #80b9f2;
|
|
19
20
|
--color-gui-active: #074095;
|
|
20
|
-
--color-gui-
|
|
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:
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
@apply text-gui-
|
|
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
|
|
471
|
-
-
|
|
472
|
-
|
|
473
|
-
|
|
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": "
|
|
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.
|
|
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",
|
|
@@ -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(
|
|
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
|
};
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
|
|
44
44
|
<li>
|
|
45
45
|
<%= render(AblyUi::Core::MeganavSearch.new(url_base: url_base)) %>
|
|
46
|
-
|
|
46
|
+
</li>
|
|
47
47
|
|
|
48
48
|
<% if account? %>
|
|
49
|
-
<li
|
|
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 = (
|
|
22
|
-
return
|
|
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 = {
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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>
|
|
@@ -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: #
|
|
18
|
+
--color-gui-focus: #0073e6;
|
|
19
|
+
--color-gui-focus-outline: #80b9f2;
|
|
19
20
|
--color-gui-active: #074095;
|
|
20
|
-
--color-gui-
|
|
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:
|
|
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 */
|