Skip to content

Commit d68fe91

Browse files
committed
Merge of Insiders features tied to 'Biquinho Vermelho' funding goal
1 parent ffd62bc commit d68fe91

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+888
-223
lines changed

material/assets/javascripts/bundle.716f8af4.min.js

+29
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/assets/javascripts/bundle.716f8af4.min.js.map

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/assets/javascripts/bundle.ddd52ceb.min.js

-29
This file was deleted.

material/assets/javascripts/bundle.ddd52ceb.min.js.map

-7
This file was deleted.

material/assets/javascripts/workers/search.477d984a.min.js renamed to material/assets/javascripts/workers/search.53c85856.min.js

+9-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/assets/javascripts/workers/search.477d984a.min.js.map renamed to material/assets/javascripts/workers/search.53c85856.min.js.map

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/assets/stylesheets/main.3754935a.min.css

-2
This file was deleted.

material/assets/stylesheets/main.3754935a.min.css.map

-1
This file was deleted.

material/assets/stylesheets/main.fe914879.min.css

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/assets/stylesheets/main.fe914879.min.css.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/assets/stylesheets/palette.f1a3b89f.min.css renamed to material/assets/stylesheets/palette.ba0d045b.min.css

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/assets/stylesheets/palette.ba0d045b.min.css.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/assets/stylesheets/palette.f1a3b89f.min.css.map

-1
This file was deleted.

material/base.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@
3939
{% endif %}
4040
{% endblock %}
4141
{% block styles %}
42-
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.3754935a.min.css' | url }}">
42+
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.fe914879.min.css' | url }}">
4343
{% if config.theme.palette %}
4444
{% set palette = config.theme.palette %}
45-
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.f1a3b89f.min.css' | url }}">
45+
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.ba0d045b.min.css' | url }}">
4646
{% if palette.primary %}
4747
{% import "partials/palette.html" as map %}
4848
{% set primary = map.primary(
@@ -196,7 +196,7 @@ <h1>{{ page.title | d(config.site_name, true)}}</h1>
196196
"base": base_url,
197197
"features": features,
198198
"translations": {},
199-
"search": "assets/javascripts/workers/search.477d984a.min.js" | url,
199+
"search": "assets/javascripts/workers/search.53c85856.min.js" | url,
200200
"version": config.extra.version or None
201201
} -%}
202202
{%- set translations = app.translations -%}
@@ -223,7 +223,7 @@ <h1>{{ page.title | d(config.site_name, true)}}</h1>
223223
</script>
224224
{% endblock %}
225225
{% block scripts %}
226-
<script src="{{ 'assets/javascripts/bundle.ddd52ceb.min.js' | url }}"></script>
226+
<script src="{{ 'assets/javascripts/bundle.716f8af4.min.js' | url }}"></script>
227227
{% for path in config["extra_javascript"] %}
228228
<script src="{{ path | url }}"></script>
229229
{% endfor %}

material/overrides/assets/javascripts/bundle.89b9c269.min.js renamed to material/overrides/assets/javascripts/bundle.1d33a92e.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/overrides/assets/javascripts/bundle.89b9c269.min.js.map renamed to material/overrides/assets/javascripts/bundle.1d33a92e.min.js.map

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

material/overrides/main.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@
3535
{% endblock %}
3636
{% block scripts %}
3737
{{ super() }}
38-
<script src="{{ 'overrides/assets/javascripts/bundle.89b9c269.min.js' | url }}"></script>
38+
<script src="{{ 'overrides/assets/javascripts/bundle.1d33a92e.min.js' | url }}"></script>
3939
{% endblock %}

material/partials/languages/de.html

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"meta.source": "Quellcode",
1313
"search.config.lang": "de",
1414
"search.placeholder": "Suche",
15+
"search.share": "Teilen",
16+
"search.reset": "Zurücksetzen",
1517
"search.result.initializer": "Suche wird initialisiert",
1618
"search.result.placeholder": "Suchbegriff eingeben",
1719
"search.result.none": "Keine Suchergebnisse",
@@ -20,6 +22,7 @@
2022
"search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite",
2123
"search.result.more.other": "# weitere Suchergebnisse auf dieser Seite",
2224
"search.result.term.missing": "Es fehlt",
25+
"search.title": "Suche",
2326
"select.language.title": "Sprache wechseln",
2427
"select.version.title": "Version auswählen",
2528
"skip.link.title": "Zum Inhalt",

material/partials/languages/en.html

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"search.config.pipeline": "trimmer, stopWordFilter",
1919
"search.config.separator": "[\s\-]+",
2020
"search.placeholder": "Search",
21+
"search.share": "Share",
2122
"search.reset": "Clear",
2223
"search.result.initializer": "Initializing search",
2324
"search.result.placeholder": "Type to start searching",
@@ -27,6 +28,7 @@
2728
"search.result.more.one": "1 more on this page",
2829
"search.result.more.other": "# more on this page",
2930
"search.result.term.missing": "Missing",
31+
"search.title": "Search",
3032
"select.language.title": "Select language",
3133
"select.version.title": "Select version",
3234
"skip.link.title": "Skip to content",

material/partials/search.html

+14-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,24 @@
66
<label class="md-search__overlay" for="__search"></label>
77
<div class="md-search__inner" role="search">
88
<form class="md-search__form" name="search">
9-
<input type="text" class="md-search__input" name="query" aria-label="{{ lang.t('search.placeholder') }}" placeholder="{{ lang.t('search.placeholder') }}" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" data-md-state="active" required>
9+
<input type="text" class="md-search__input" name="query" aria-label="{{ lang.t('search.placeholder') }}" placeholder="{{ lang.t('search.placeholder') }}" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
1010
<label class="md-search__icon md-icon" for="__search">
1111
{% include ".icons/material/magnify.svg" %}
1212
{% include ".icons/material/arrow-left.svg" %}
1313
</label>
14-
<button type="reset" class="md-search__icon md-icon" aria-label="{{ lang.t('search.reset') }}" tabindex="-1">
15-
{% include ".icons/material/close.svg" %}
16-
</button>
14+
<nav class="md-search__options" aria-label="{{ lang.t('search.title') }}">
15+
{% if "search.share" in features %}
16+
<a href="javascript:void(0)" class="md-search__icon md-icon" aria-label="{{ lang.t('search.share') }}" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
17+
{% include ".icons/material/share-variant.svg" %}
18+
</a>
19+
{% endif %}
20+
<button type="reset" class="md-search__icon md-icon" aria-label="{{ lang.t('search.reset') }}" tabindex="-1">
21+
{% include ".icons/material/close.svg" %}
22+
</button>
23+
</nav>
24+
{% if "search.suggest" in features %}
25+
<div class="md-search__suggest" data-md-component="search-suggest"></div>
26+
{% endif %}
1727
</form>
1828
<div class="md-search__output">
1929
<div class="md-search__scrollwrap" data-md-scrollfix>

src/assets/javascripts/_/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ export type Flag =
3636
| "navigation.sections" /* Sections navigation */
3737
| "navigation.tabs" /* Tabs navigation */
3838
| "navigation.top" /* Back-to-top button */
39+
| "search.highlight" /* Search highlighting */
40+
| "search.share" /* Search sharing */
41+
| "search.suggest" /* Search suggestions */
3942
| "toc.integrate" /* Integrated table of contents */
4043

4144
/* ------------------------------------------------------------------------- */

src/assets/javascripts/bundle.ts

+8
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import {
5555
mountHeaderTitle,
5656
mountPalette,
5757
mountSearch,
58+
mountSearchHiglight,
5859
mountSidebar,
5960
mountSource,
6061
mountTableOfContents,
@@ -195,6 +196,13 @@ const content$ = defer(() => merge(
195196
...getComponentElements("content")
196197
.map(el => mountContent(el, { target$, viewport$, print$ })),
197198

199+
/* Search highlighting */
200+
...getComponentElements("content")
201+
.map(el => feature("search.highlight")
202+
? mountSearchHiglight(el, { index$, location$ })
203+
: NEVER
204+
),
205+
198206
/* Header title */
199207
...getComponentElements("header-title")
200208
.map(el => mountHeaderTitle(el, { viewport$, header$ })),

src/assets/javascripts/components/_/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export type ComponentType =
4242
| "search" /* Search */
4343
| "search-query" /* Search input */
4444
| "search-result" /* Search results */
45+
| "search-share" /* Search sharing */
46+
| "search-suggest" /* Search suggestions */
4547
| "sidebar" /* Sidebar */
4648
| "skip" /* Skip link */
4749
| "source" /* Repository information */
@@ -83,6 +85,8 @@ interface ComponentTypeMap {
8385
"search": HTMLElement /* Search */
8486
"search-query": HTMLInputElement /* Search input */
8587
"search-result": HTMLElement /* Search results */
88+
"search-share": HTMLAnchorElement /* Search sharing */
89+
"search-suggest": HTMLElement /* Search suggestions */
8690
"sidebar": HTMLElement /* Sidebar */
8791
"skip": HTMLAnchorElement /* Skip link */
8892
"source": HTMLAnchorElement /* Repository information */

src/assets/javascripts/components/search/_/index.ts

+54-13
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*/
2222

2323
import { NEVER, Observable, ObservableInput, merge } from "rxjs"
24-
import { filter, sample, take } from "rxjs/operators"
24+
import { filter, mergeWith, sample, take } from "rxjs/operators"
2525

2626
import { configuration } from "~/_"
2727
import {
@@ -34,14 +34,21 @@ import {
3434
} from "~/browser"
3535
import {
3636
SearchIndex,
37+
SearchResult,
3738
isSearchQueryMessage,
3839
isSearchReadyMessage,
3940
setupSearchWorker
4041
} from "~/integrations"
4142

42-
import { Component, getComponentElement } from "../../_"
43+
import {
44+
Component,
45+
getComponentElement,
46+
getComponentElements
47+
} from "../../_"
4348
import { SearchQuery, mountSearchQuery } from "../query"
44-
import { SearchResult, mountSearchResult } from "../result"
49+
import { mountSearchResult } from "../result"
50+
import { SearchShare, mountSearchShare } from "../share"
51+
import { SearchSuggest, mountSearchSuggest } from "../suggest"
4552

4653
/* ----------------------------------------------------------------------------
4754
* Types
@@ -53,6 +60,8 @@ import { SearchResult, mountSearchResult } from "../result"
5360
export type Search =
5461
| SearchQuery
5562
| SearchResult
63+
| SearchShare
64+
| SearchSuggest
5665

5766
/* ----------------------------------------------------------------------------
5867
* Helper types
@@ -88,7 +97,7 @@ export function mountSearch(
8897
try {
8998
const worker = setupSearchWorker(config.search, index$)
9099

91-
/* Retrieve nested components */
100+
/* Retrieve query and result components */
92101
const query = getComponentElement("search-query", el)
93102
const result = getComponentElement("search-result", el)
94103

@@ -97,8 +106,12 @@ export function mountSearch(
97106
tx$
98107
.pipe(
99108
filter(isSearchQueryMessage),
100-
sample(rx$.pipe(filter(isSearchReadyMessage))),
101-
take(1)
109+
sample(rx$
110+
.pipe(
111+
filter(isSearchReadyMessage),
112+
take(1)
113+
)
114+
)
102115
)
103116
.subscribe(tx$.next.bind(tx$))
104117

@@ -111,10 +124,28 @@ export function mountSearch(
111124
const active = getActiveElement()
112125
switch (key.type) {
113126

114-
/* Enter: prevent form submission */
127+
/* Enter: go to first (best) result */
115128
case "Enter":
116-
if (active === query)
129+
if (active === query) {
130+
const anchors = new Map<HTMLAnchorElement, number>()
131+
for (const anchor of getElements<HTMLAnchorElement>(
132+
":first-child [href]", result
133+
)) {
134+
const article = anchor.firstElementChild!
135+
anchors.set(anchor, parseFloat(
136+
article.getAttribute("data-md-score")!
137+
))
138+
}
139+
140+
/* Go to result with highest score, if any */
141+
if (anchors.size) {
142+
const [[best]] = [...anchors].sort(([, a], [, b]) => b - a)
143+
best.click()
144+
}
145+
146+
/* Otherwise omit form submission */
117147
key.claim()
148+
}
118149
break
119150

120151
/* Escape or Tab: close search */
@@ -173,11 +204,21 @@ export function mountSearch(
173204
})
174205

175206
/* Create and return component */
176-
const query$ = mountSearchQuery(query, worker)
177-
return merge(
178-
query$,
179-
mountSearchResult(result, worker, { query$ })
180-
)
207+
const query$ = mountSearchQuery(query, worker)
208+
const result$ = mountSearchResult(result, worker, { query$ })
209+
return merge(query$, result$)
210+
.pipe(
211+
mergeWith(
212+
213+
/* Search sharing */
214+
...getComponentElements("search-share", el)
215+
.map(child => mountSearchShare(child, { query$ })),
216+
217+
/* Search suggestions */
218+
...getComponentElements("search-suggest", el)
219+
.map(child => mountSearchSuggest(child, worker, { keyboard$ }))
220+
)
221+
)
181222

182223
/* Gracefully handle broken search */
183224
} catch (err) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"rules": {
3+
"no-null/no-null": "off"
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright (c) 2016-2021 Martin Donath <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to
6+
* deal in the Software without restriction, including without limitation the
7+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8+
* sell copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20+
* IN THE SOFTWARE.
21+
*/
22+
23+
import {
24+
Observable,
25+
ObservableInput,
26+
combineLatest
27+
} from "rxjs"
28+
import { filter, map, startWith } from "rxjs/operators"
29+
30+
import { getLocation } from "~/browser"
31+
import {
32+
SearchIndex,
33+
setupSearchHighlighter
34+
} from "~/integrations"
35+
import { h } from "~/utilities"
36+
37+
import { Component } from "../../_"
38+
39+
/* ----------------------------------------------------------------------------
40+
* Types
41+
* ------------------------------------------------------------------------- */
42+
43+
/**
44+
* Search highlighting
45+
*/
46+
export interface SearchHighlight {
47+
nodes: Map<ChildNode, string> /* Map of replacements */
48+
}
49+
50+
/* ----------------------------------------------------------------------------
51+
* Helper types
52+
* ------------------------------------------------------------------------- */
53+
54+
/**
55+
* Mount options
56+
*/
57+
interface MountOptions {
58+
index$: ObservableInput<SearchIndex> /* Search index observable */
59+
location$: Observable<URL> /* Location observable */
60+
}
61+
62+
/* ----------------------------------------------------------------------------
63+
* Functions
64+
* ------------------------------------------------------------------------- */
65+
66+
/**
67+
* Mount search highlighting
68+
*
69+
* @param el - Content element
70+
* @param options - Options
71+
*
72+
* @returns Search highlighting component observable
73+
*/
74+
export function mountSearchHiglight(
75+
el: HTMLElement, { index$, location$ }: MountOptions
76+
): Observable<Component<SearchHighlight>> {
77+
return combineLatest([
78+
index$,
79+
location$
80+
.pipe(
81+
startWith(getLocation()),
82+
filter(url => url.searchParams.has("h"))
83+
)
84+
])
85+
.pipe(
86+
map(([index, url]) => setupSearchHighlighter(index.config)(
87+
url.searchParams.get("h")!
88+
)),
89+
map(fn => {
90+
const nodes = new Map<ChildNode, string>()
91+
92+
/* Traverse text nodes and collect matches */
93+
const it = document.createNodeIterator(el, NodeFilter.SHOW_TEXT)
94+
for (let node = it.nextNode(); node; node = it.nextNode()) {
95+
if (node.parentElement?.offsetHeight) {
96+
const original = node.textContent!
97+
const replaced = fn(original)
98+
if (replaced.length > original.length)
99+
nodes.set(node as ChildNode, replaced)
100+
}
101+
}
102+
103+
/* Replace original nodes with matches */
104+
for (const [node, text] of nodes) {
105+
const { childNodes } = h("span", null, text)
106+
node.replaceWith(...Array.from(childNodes))
107+
}
108+
109+
/* Return component */
110+
return { ref: el, nodes }
111+
})
112+
)
113+
}

0 commit comments

Comments
 (0)