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 CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.88",
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.27",
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"
@@ -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
@@ -54,3 +54,8 @@ if (enabled(f.headers, 'link')) {
54
54
  const headers = await import('./modules/headers.js');
55
55
  headers.enhanceHeaders();
56
56
  }
57
+
58
+ if (enabled(f.details, 'tabs')) {
59
+ const tabs = await import('./modules/detail-tabs.js');
60
+ tabs.enhanceDetailGroups();
61
+ }
@@ -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
- function contains(string, search) {
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 };
@@ -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 + 25;
84
+ item.score = item.score + 20;
72
85
  item.matchedHeadings.push(c);
73
86
  }
74
87
  });
package/src/config.ts CHANGED
@@ -36,6 +36,7 @@ const SITE: Site = {
36
36
  figures: ['enlarge'],
37
37
  youTubeLinks: ['embed'],
38
38
  headers: ['link'],
39
+ details: ['tabs'],
39
40
  },
40
41
  images: {
41
42
  // Generated using https://ausi.github.io/respimagelint/