astro-accelerator 0.0.88 → 0.0.90
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/package.json +2 -2
- package/public/css/main.css +70 -0
- package/public/js/main.js +5 -0
- package/public/js/modules/detail-tabs.js +196 -0
- package/public/js/modules/string.js +13 -2
- package/public/js/search.js +15 -2
- package/src/config.ts +1 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.0.
|
|
2
|
+
"version": "0.0.90",
|
|
3
3
|
"author": "Steve Fenton",
|
|
4
4
|
"name": "astro-accelerator",
|
|
5
5
|
"description": "A super-lightweight, accessible, SEO-friendly starter project for Astro",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@astrojs/mdx": "^0.19.6",
|
|
28
28
|
"astro": "^2.5.7",
|
|
29
|
-
"astro-accelerator-utils": "^0.2.
|
|
29
|
+
"astro-accelerator-utils": "^0.2.28",
|
|
30
30
|
"hast-util-from-selector": "^2.0.1",
|
|
31
31
|
"remark-directive": "^2.0.1",
|
|
32
32
|
"sharp": "^0.32.1"
|
package/public/css/main.css
CHANGED
|
@@ -1015,6 +1015,76 @@ button[data-share]:focus {
|
|
|
1015
1015
|
stroke: var(--fore-link-alt);
|
|
1016
1016
|
}
|
|
1017
1017
|
|
|
1018
|
+
/* Detail tabs */
|
|
1019
|
+
|
|
1020
|
+
.tablist [role="tablist"] {
|
|
1021
|
+
min-width: 100%;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
.tablist [role="tab"],
|
|
1025
|
+
.tablist [role="tab"]:focus,
|
|
1026
|
+
.tablist [role="tab"]:hover {
|
|
1027
|
+
display: inline-block;
|
|
1028
|
+
position: relative;
|
|
1029
|
+
z-index: 2;
|
|
1030
|
+
top: 2px;
|
|
1031
|
+
margin: 0;
|
|
1032
|
+
margin-top: 4px;
|
|
1033
|
+
border: 2px solid var(--aft-block);
|
|
1034
|
+
border-radius: var(--block-radius) var(--block-radius) 0 0;
|
|
1035
|
+
background: var(--aft-block);
|
|
1036
|
+
outline: none;
|
|
1037
|
+
font-weight: bold;
|
|
1038
|
+
text-align: left;
|
|
1039
|
+
cursor: pointer;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
.tablist [role="tab"][aria-selected="true"] {
|
|
1043
|
+
margin-top: 0;
|
|
1044
|
+
border-width: 2px;
|
|
1045
|
+
border-top-width: 6px;
|
|
1046
|
+
border-top-color: var(--fore-link);
|
|
1047
|
+
border-inline-color: var(--fore-link);
|
|
1048
|
+
border-bottom-color: var(--aft);
|
|
1049
|
+
background-color: var(--aft);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
.tablist [role="tab"][aria-selected="false"] {
|
|
1053
|
+
top: 0px;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
.tablist [role="tab"] span.focus {
|
|
1057
|
+
display: inline-block;
|
|
1058
|
+
margin: 2px;
|
|
1059
|
+
padding: 4px 6px;
|
|
1060
|
+
outline: none;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
.input-keyboard .tablist [role="tab"]:hover span.focus,
|
|
1064
|
+
.input-keyboard .tablist [role="tab"]:focus span.focus,
|
|
1065
|
+
.input-keyboard .tablist [role="tab"]:active span.focus {
|
|
1066
|
+
outline: 2px solid var(--fore-link);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
[role="tabpanel"] {
|
|
1070
|
+
position:relative;
|
|
1071
|
+
padding: 1rem;
|
|
1072
|
+
border: 2px solid var(--fore-link);
|
|
1073
|
+
border-radius: 0 var(--block-radius) var(--block-radius);
|
|
1074
|
+
background: var(--aft);
|
|
1075
|
+
min-height: 10em;
|
|
1076
|
+
width: 100%;
|
|
1077
|
+
overflow: auto;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
[role="tabpanel"].is-hidden {
|
|
1081
|
+
display: none;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
[role="tabpanel"] p {
|
|
1085
|
+
margin: 0;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1018
1088
|
/* Animation */
|
|
1019
1089
|
|
|
1020
1090
|
@media (prefers-reduced-motion: no-preference) {
|
package/public/js/main.js
CHANGED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { qs, qsa } from './query.js';
|
|
2
|
+
|
|
3
|
+
function unique(value, index, array) {
|
|
4
|
+
return array.indexOf(value) === index;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Converts <detail data-group="id"> into tabs
|
|
9
|
+
*/
|
|
10
|
+
function enhanceDetailGroups() {
|
|
11
|
+
const details = qsa('details[data-group]');
|
|
12
|
+
const groups = [];
|
|
13
|
+
|
|
14
|
+
details.forEach(d => d.dataset && d.dataset.group && groups.push(d.dataset.group));
|
|
15
|
+
let uniqueGroups = groups.filter(unique);
|
|
16
|
+
|
|
17
|
+
uniqueGroups.forEach(g => {
|
|
18
|
+
const participants = qsa(`details[data-group='${ g }']`);
|
|
19
|
+
if (participants.length === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const tablist = document.createElement('div');
|
|
24
|
+
tablist.role = 'tablist';
|
|
25
|
+
tablist.className = 'tablist';
|
|
26
|
+
participants[0].parentNode.insertBefore(tablist, participants[0]);
|
|
27
|
+
|
|
28
|
+
participants.forEach((p, i) => {
|
|
29
|
+
|
|
30
|
+
const heading = qs('summary', p);
|
|
31
|
+
|
|
32
|
+
// Create the tab panel
|
|
33
|
+
|
|
34
|
+
const tabPanel = document.createElement('div');
|
|
35
|
+
tabPanel.role = 'tabpanel';
|
|
36
|
+
tabPanel.setAttribute('aria-labelledby', `aatb_${ g }_${ i }`);
|
|
37
|
+
tabPanel.id = `aatb_panel_${ g }_${ i }`;
|
|
38
|
+
|
|
39
|
+
const content = document.createElement('div');
|
|
40
|
+
content.innerHTML = p.innerHTML;
|
|
41
|
+
const contentSummary = qs('summary', content);
|
|
42
|
+
content.removeChild(contentSummary);
|
|
43
|
+
|
|
44
|
+
tabPanel.appendChild(content);
|
|
45
|
+
|
|
46
|
+
participants[0].parentNode.insertBefore(tabPanel, participants[0]);
|
|
47
|
+
|
|
48
|
+
// Create the tab control
|
|
49
|
+
|
|
50
|
+
const tabButton = document.createElement('button');
|
|
51
|
+
tabButton.id = `aatb_${ g }_${ i }`;
|
|
52
|
+
tabButton.type = 'button';
|
|
53
|
+
tabButton.role = 'tab';
|
|
54
|
+
tabButton.ariaSelected = i == 0 ? 'true' : 'false';
|
|
55
|
+
tabButton.setAttribute('aria-controls', tabPanel.id);
|
|
56
|
+
|
|
57
|
+
const tabHeading = document.createElement('span');
|
|
58
|
+
tabHeading.className = 'focus';
|
|
59
|
+
tabHeading.innerText = heading.innerText;
|
|
60
|
+
|
|
61
|
+
tabButton.appendChild(tabHeading);
|
|
62
|
+
tablist.appendChild(tabButton);
|
|
63
|
+
|
|
64
|
+
console.log(heading.innerText, content)
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
new TabsManual(tablist);
|
|
68
|
+
|
|
69
|
+
participants.forEach((p, i) => {
|
|
70
|
+
p.parentNode.removeChild(p);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// remove details elements
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class TabsManual {
|
|
79
|
+
constructor(groupNode) {
|
|
80
|
+
this.tablistNode = groupNode;
|
|
81
|
+
|
|
82
|
+
this.tabs = [];
|
|
83
|
+
|
|
84
|
+
this.firstTab = null;
|
|
85
|
+
this.lastTab = null;
|
|
86
|
+
|
|
87
|
+
this.tabs = Array.from(this.tablistNode.querySelectorAll('[role=tab]'));
|
|
88
|
+
this.tabpanels = [];
|
|
89
|
+
|
|
90
|
+
for (var i = 0; i < this.tabs.length; i += 1) {
|
|
91
|
+
var tab = this.tabs[i];
|
|
92
|
+
var tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
|
|
93
|
+
|
|
94
|
+
tab.tabIndex = -1;
|
|
95
|
+
tab.setAttribute('aria-selected', 'false');
|
|
96
|
+
this.tabpanels.push(tabpanel);
|
|
97
|
+
|
|
98
|
+
tab.addEventListener('keydown', this.onKeydown.bind(this));
|
|
99
|
+
tab.addEventListener('click', this.onClick.bind(this));
|
|
100
|
+
|
|
101
|
+
if (!this.firstTab) {
|
|
102
|
+
this.firstTab = tab;
|
|
103
|
+
}
|
|
104
|
+
this.lastTab = tab;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.setSelectedTab(this.firstTab);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setSelectedTab(currentTab) {
|
|
111
|
+
for (var i = 0; i < this.tabs.length; i += 1) {
|
|
112
|
+
var tab = this.tabs[i];
|
|
113
|
+
if (currentTab === tab) {
|
|
114
|
+
tab.setAttribute('aria-selected', 'true');
|
|
115
|
+
tab.removeAttribute('tabindex');
|
|
116
|
+
this.tabpanels[i].classList.remove('is-hidden');
|
|
117
|
+
} else {
|
|
118
|
+
tab.setAttribute('aria-selected', 'false');
|
|
119
|
+
tab.tabIndex = -1;
|
|
120
|
+
this.tabpanels[i].classList.add('is-hidden');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
moveFocusToTab(currentTab) {
|
|
126
|
+
currentTab.focus();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
moveFocusToPreviousTab(currentTab) {
|
|
130
|
+
var index;
|
|
131
|
+
|
|
132
|
+
if (currentTab === this.firstTab) {
|
|
133
|
+
this.moveFocusToTab(this.lastTab);
|
|
134
|
+
} else {
|
|
135
|
+
index = this.tabs.indexOf(currentTab);
|
|
136
|
+
this.moveFocusToTab(this.tabs[index - 1]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
moveFocusToNextTab(currentTab) {
|
|
141
|
+
var index;
|
|
142
|
+
|
|
143
|
+
if (currentTab === this.lastTab) {
|
|
144
|
+
this.moveFocusToTab(this.firstTab);
|
|
145
|
+
} else {
|
|
146
|
+
index = this.tabs.indexOf(currentTab);
|
|
147
|
+
this.moveFocusToTab(this.tabs[index + 1]);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* EVENT HANDLERS */
|
|
152
|
+
|
|
153
|
+
onKeydown(event) {
|
|
154
|
+
var tgt = event.currentTarget,
|
|
155
|
+
flag = false;
|
|
156
|
+
|
|
157
|
+
switch (event.key) {
|
|
158
|
+
case 'ArrowLeft':
|
|
159
|
+
this.moveFocusToPreviousTab(tgt);
|
|
160
|
+
flag = true;
|
|
161
|
+
break;
|
|
162
|
+
|
|
163
|
+
case 'ArrowRight':
|
|
164
|
+
this.moveFocusToNextTab(tgt);
|
|
165
|
+
flag = true;
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
case 'Home':
|
|
169
|
+
this.moveFocusToTab(this.firstTab);
|
|
170
|
+
flag = true;
|
|
171
|
+
break;
|
|
172
|
+
|
|
173
|
+
case 'End':
|
|
174
|
+
this.moveFocusToTab(this.lastTab);
|
|
175
|
+
flag = true;
|
|
176
|
+
break;
|
|
177
|
+
|
|
178
|
+
default:
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (flag) {
|
|
183
|
+
event.stopPropagation();
|
|
184
|
+
event.preventDefault();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Since this example uses buttons for the tabs, the click onr also is activated
|
|
189
|
+
// with the space and enter keys
|
|
190
|
+
onClick(event) {
|
|
191
|
+
this.setSelectedTab(event.currentTarget);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
export { enhanceDetailGroups }
|
|
@@ -7,10 +7,21 @@
|
|
|
7
7
|
* @param {string} search
|
|
8
8
|
* @returns
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
function contains(string, search) {
|
|
11
11
|
return string.indexOf(search) > -1;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Looks for a search within a string
|
|
16
|
+
*
|
|
17
|
+
* @param {string} string
|
|
18
|
+
* @param {string} search
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
function containsWord(string, search) {
|
|
22
|
+
return string.split(' ').indexOf(search) > -1;
|
|
23
|
+
}
|
|
24
|
+
|
|
14
25
|
/**
|
|
15
26
|
*
|
|
16
27
|
* @param {string} string
|
|
@@ -63,4 +74,4 @@ function explode(string) {
|
|
|
63
74
|
return string.split(' ').filter(isLongEnough).map(sanitise);
|
|
64
75
|
}
|
|
65
76
|
|
|
66
|
-
export { contains, sanitise, explode, highlight };
|
|
77
|
+
export { contains, containsWord, sanitise, explode, highlight };
|
package/public/js/search.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { qs } from './modules/query.js';
|
|
4
4
|
import { raiseEvent } from './modules/events.js';
|
|
5
|
-
import { contains, sanitise, explode, highlight } from './modules/string.js';
|
|
5
|
+
import { contains, containsWord, sanitise, explode, highlight } from './modules/string.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
type Heading = {
|
|
@@ -60,6 +60,13 @@ function search(s) {
|
|
|
60
60
|
// Imagine the user searched for "Kitchen Sink"
|
|
61
61
|
// The scores are arranged below from highest to lowest relevance
|
|
62
62
|
|
|
63
|
+
// If the title contains a whole word match
|
|
64
|
+
queryTerms.forEach(t => {
|
|
65
|
+
if (containsWord(item.safeTitle, t)) {
|
|
66
|
+
item.score = item.score + 120;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
63
70
|
// If the title contains "Kitchen Sink"
|
|
64
71
|
if (contains(item.safeTitle, currentQuery)) {
|
|
65
72
|
item.score = item.score + 60;
|
|
@@ -67,8 +74,14 @@ function search(s) {
|
|
|
67
74
|
|
|
68
75
|
// If a heading contains "Kitchen Sink"
|
|
69
76
|
item.headings.forEach(c => {
|
|
77
|
+
queryTerms.forEach(t => {
|
|
78
|
+
if (containsWord(c.safeText, t)) {
|
|
79
|
+
item.score = item.score + 40;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
70
83
|
if (contains(c.safeText, currentQuery)) {
|
|
71
|
-
item.score = item.score +
|
|
84
|
+
item.score = item.score + 20;
|
|
72
85
|
item.matchedHeadings.push(c);
|
|
73
86
|
}
|
|
74
87
|
});
|