Added emoji index to icon search

This commit is contained in:
squidfunk 2021-02-15 15:57:44 +01:00
parent a776d0018f
commit 356cde2ad8
18 changed files with 120 additions and 64 deletions

View File

@ -14,7 +14,7 @@ and used in `mkdocs.yml`, documents and templates.
<div class="mdx-icon-search" data-mdx-component="icon-search">
<input
class="md-input md-input--stretch mdx-icon-search__input"
placeholder="Search the icon database"
placeholder="Search the icon and emoji database"
data-mdx-component="icon-search-query"
/>
<div class="mdx-icon-search-result" data-mdx-component="icon-search-result">
@ -24,8 +24,8 @@ and used in `mkdocs.yml`, documents and templates.
</div>
<small>
:octicons-light-bulb-16:
**Tip:** Enter some keywords to find the perfect icon and click on the
shortcode to copy it to your clipboard.
**Tip:** Enter some keywords to find the perfect icon or emoji and click on
the shortcode to copy it to your clipboard.
</small>
## Configuration

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"assets/javascripts/bundle.js": "assets/javascripts/bundle.fbcb1fc3.min.js",
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.fbcb1fc3.min.js.map",
"assets/javascripts/bundle.js": "assets/javascripts/bundle.21e94a31.min.js",
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.21e94a31.min.js.map",
"assets/javascripts/vendor.js": "assets/javascripts/vendor.00ecb175.min.js",
"assets/javascripts/vendor.js.map": "assets/javascripts/vendor.00ecb175.min.js.map",
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.3f4c5856.min.js",
@ -9,8 +9,8 @@
"assets/stylesheets/main.css.map": "assets/stylesheets/main.45122f27.min.css.map",
"assets/stylesheets/palette.css": "assets/stylesheets/palette.e03a20ad.min.css",
"assets/stylesheets/palette.css.map": "assets/stylesheets/palette.e03a20ad.min.css.map",
"overrides/assets/javascripts/bundle.js": "overrides/assets/javascripts/bundle.759c98a8.min.js",
"overrides/assets/javascripts/bundle.js.map": "overrides/assets/javascripts/bundle.759c98a8.min.js.map",
"overrides/assets/javascripts/bundle.js": "overrides/assets/javascripts/bundle.f4aeaef7.min.js",
"overrides/assets/javascripts/bundle.js.map": "overrides/assets/javascripts/bundle.f4aeaef7.min.js.map",
"overrides/assets/stylesheets/main.css": "overrides/assets/stylesheets/main.c2cc92d1.min.css",
"overrides/assets/stylesheets/main.css.map": "overrides/assets/stylesheets/main.c2cc92d1.min.css.map"
}

View File

@ -217,7 +217,7 @@
{% endblock %}
{% block scripts %}
<script src="{{ 'assets/javascripts/vendor.00ecb175.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.fbcb1fc3.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.21e94a31.min.js' | url }}"></script>
{% for path in config["extra_javascript"] %}
<script src="{{ path | url }}"></script>
{% endfor %}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -53,5 +53,5 @@
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="{{ 'overrides/assets/javascripts/bundle.759c98a8.min.js' | url }}"></script>
<script src="{{ 'overrides/assets/javascripts/bundle.f4aeaef7.min.js' | url }}"></script>
{% endblock %}

View File

@ -20,7 +20,7 @@
* IN THE SOFTWARE.
*/
import { Observable, ObservableInput, merge } from "rxjs"
import { NEVER, Observable, ObservableInput, merge } from "rxjs"
import { filter, sample, take } from "rxjs/operators"
import { configuration } from "~/_"
@ -99,6 +99,10 @@ function fetchSearchIndex(url: string): ObservableInput<SearchIndex> {
export function mountSearch(
el: HTMLElement, { keyboard$ }: MountOptions
): Observable<Component<Search>> {
if (location.protocol === "file:")
return NEVER
/* Set up search worker */
const config = configuration()
const worker = setupSearchWorker(config.search, fetchSearchIndex(
`${config.base}/search/search_index.json`

View File

@ -184,7 +184,7 @@ export function setupInstantLoading(
const el = ev.target.closest("a")
if (el && !el.target && urls.includes(el.href)) {
ev.preventDefault()
return of<HistoryState>({
return of({
url: new URL(el.href)
})
}
@ -193,7 +193,7 @@ export function setupInstantLoading(
})
)
),
share()
share<HistoryState>()
)
/* Intercept history back and forward */
@ -203,8 +203,8 @@ export function setupInstantLoading(
map(ev => ({
url: new URL(location.href),
offset: ev.state
} as HistoryState)),
share()
})),
share<HistoryState>()
)
/* Emit location change */

View File

@ -39,10 +39,33 @@ import {
* Types
* ------------------------------------------------------------------------- */
/**
* Icon
*/
export interface Icon {
shortcode: string /* Icon shortcode */
url: string /* Icon URL */
}
/* ------------------------------------------------------------------------- */
/**
* Icon search database
*/
export interface IconSearchDatabase {
base: string /* Category base URL */
data: Record<string, string> /* Category data */
}
/**
* Icon search index
*/
export type IconSearchIndex = string[]
export interface IconSearchIndex {
icons: IconSearchDatabase /* Icon database */
emojis: IconSearchDatabase /* Emoji database */
}
/* ------------------------------------------------------------------------- */
/**
* Icon search
@ -67,7 +90,7 @@ export function mountIconSearch(
): Observable<Component<IconSearch>> {
const config = configuration()
const index$ = requestJSON<IconSearchIndex>(
`${config.base}/overrides/assets/javascripts/icons.json`
`${config.base}/overrides/assets/javascripts/icon_search_index.json`
)
/* Retrieve nested components */

View File

@ -55,7 +55,7 @@ import {
import { renderIconSearchResult } from "../../../templates"
import { Component } from "../../_"
import { IconSearchIndex } from "../_"
import { Icon, IconSearchIndex } from "../_"
import { IconSearchQuery } from "../query"
/* ----------------------------------------------------------------------------
@ -66,7 +66,7 @@ import { IconSearchQuery } from "../query"
* Icon search result
*/
export interface IconSearchResult {
data: string[] /* Search result data */
data: Icon[] /* Search result data */
}
/* ----------------------------------------------------------------------------
@ -139,11 +139,35 @@ export function mountIconSearchResult(
/* Create and return component */
return combineLatest([
index$,
query$.pipe(distinctUntilKeyChanged("value"))
query$.pipe(distinctUntilKeyChanged("value")),
index$
.pipe(
map(({ icons, emojis }) => [
...Object.keys(icons.data),
...Object.keys(emojis.data)
])
)
])
.pipe(
map(([index, { value }]) => ({ data: search(index, value) })),
withLatestFrom(index$),
map(([[{ value }, data], index]) => {
const results = search(data, value)
return {
data: results.map(name => {
if (name in index.icons.data) {
return {
shortcode: name,
url: `${index.icons.base}${index.icons.data[name]}`
}
} else {
return {
shortcode: name,
url: `${index.emojis.base}${index.emojis.data[name]}`
}
}
})
}
}),
tap(internal$),
finalize(() => internal$.complete()),
map(state => ({ ref: el, ...state }))

View File

@ -25,33 +25,12 @@ import { wrap } from "fuzzaldrin-plus"
import { translation } from "~/_"
import { h } from "~/utilities"
/* ----------------------------------------------------------------------------
* Data
* ------------------------------------------------------------------------- */
/**
* Icon CDN URL
*/
const base =
"https://raw.githubusercontent.com/" +
"squidfunk/mkdocs-material/" +
"master/material/.icons/"
import { Icon } from "../../components"
/* ----------------------------------------------------------------------------
* Helper functions
* ------------------------------------------------------------------------- */
/**
* Convert icon search result to shortcode
*
* @param value - Icon search result
*
* @returns Shortcode
*/
function shortcode(value: string): string {
return `:${value.replace(/\.svg$/, "").replace(/\//g, "-")}:`
}
/**
* Highlight an icon search result
*
@ -61,7 +40,7 @@ function shortcode(value: string): string {
* @returns Highlighted result
*/
function highlight(value: string, query: string) {
return wrap(shortcode(value), query, {
return wrap(value, query, {
wrap: {
tagOpen: "<b>",
tagClose: "</b>"
@ -76,25 +55,25 @@ function highlight(value: string, query: string) {
/**
* Render an icon search result
*
* @param value - Icon search result
* @param icon - Icon search result
* @param query - Icon search query
*
* @returns Element
*/
export function renderIconSearchResult(
value: string, query: string
icon: Icon, query: string
): HTMLElement {
return (
<li class="mdx-icon-search-result__item">
<span class="twemoji">
<img src={base + value} />
<img src={icon.url} />
</span>
<button
class="md-clipboard--inline"
title={translation("clipboard.copy")}
data-clipboard-text={shortcode(value)}
data-clipboard-text={`:${icon.shortcode}:`}
>
<code>{highlight(value, query)}</code>
<code>{`:${highlight(icon.shortcode, query)}:`}</code>
</button>
</li>
)

View File

@ -345,14 +345,40 @@ export default (_env: never, args: Configuration): Configuration[] => {
/* Save template with replaced assets */
fs.writeFileSync(file, template, "utf8")
}
}
/* Build search index for bundled icons */
const index = await glob("**/*.svg", { cwd: "material/.icons" })
fs.writeFileSync(
"material/overrides/assets/javascripts/icons.json",
JSON.stringify(index)
)
/* Build search index for bundled icons */
const icons: Record<string, string> = {}
for (const file of await glob("**/*.svg", {
cwd: "material/.icons"
})) {
const name = file.replace(/\.svg$/, "").replace(/\//g, "-")
icons[name] = file
}
/* Build search index for emojis (based on Twemoji) */
const emojis: Record<string, string> = {}
const [database] = await glob("venv/**/twemoji_db.py")
if (typeof database !== "undefined") {
const contents = fs.readFileSync(database, "utf8")
const [, content] = contents.match(/^emoji = ({.*})$.alias/ms)!
for (const [name, data] of toPairs(JSON.parse(content))) {
emojis[name.replace(/(^:|:$)/g, "")] = `${data.unicode}.svg`
}
}
fs.writeFileSync(
"material/overrides/assets/javascripts/icon_search_index.json",
JSON.stringify({
icons: {
base: "https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/material/.icons/",
data: icons
},
emojis: {
base: "https://raw.githubusercontent.com/twitter/twemoji/master/assets/svg/",
data: emojis
}
})
)
}
}
}),