mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-06-14 11:52:32 +03:00
Added emoji index to icon search
This commit is contained in:
@@ -14,7 +14,7 @@ and used in `mkdocs.yml`, documents and templates.
|
|||||||
<div class="mdx-icon-search" data-mdx-component="icon-search">
|
<div class="mdx-icon-search" data-mdx-component="icon-search">
|
||||||
<input
|
<input
|
||||||
class="md-input md-input--stretch mdx-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"
|
data-mdx-component="icon-search-query"
|
||||||
/>
|
/>
|
||||||
<div class="mdx-icon-search-result" data-mdx-component="icon-search-result">
|
<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>
|
</div>
|
||||||
<small>
|
<small>
|
||||||
:octicons-light-bulb-16:
|
:octicons-light-bulb-16:
|
||||||
**Tip:** Enter some keywords to find the perfect icon and click on the
|
**Tip:** Enter some keywords to find the perfect icon or emoji and click on
|
||||||
shortcode to copy it to your clipboard.
|
the shortcode to copy it to your clipboard.
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
1
material/assets/javascripts/bundle.21e94a31.min.js.map
Normal file
1
material/assets/javascripts/bundle.21e94a31.min.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"assets/javascripts/bundle.js": "assets/javascripts/bundle.fbcb1fc3.min.js",
|
"assets/javascripts/bundle.js": "assets/javascripts/bundle.21e94a31.min.js",
|
||||||
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.fbcb1fc3.min.js.map",
|
"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": "assets/javascripts/vendor.00ecb175.min.js",
|
||||||
"assets/javascripts/vendor.js.map": "assets/javascripts/vendor.00ecb175.min.js.map",
|
"assets/javascripts/vendor.js.map": "assets/javascripts/vendor.00ecb175.min.js.map",
|
||||||
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.3f4c5856.min.js",
|
"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/main.css.map": "assets/stylesheets/main.45122f27.min.css.map",
|
||||||
"assets/stylesheets/palette.css": "assets/stylesheets/palette.e03a20ad.min.css",
|
"assets/stylesheets/palette.css": "assets/stylesheets/palette.e03a20ad.min.css",
|
||||||
"assets/stylesheets/palette.css.map": "assets/stylesheets/palette.e03a20ad.min.css.map",
|
"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": "overrides/assets/javascripts/bundle.f4aeaef7.min.js",
|
||||||
"overrides/assets/javascripts/bundle.js.map": "overrides/assets/javascripts/bundle.759c98a8.min.js.map",
|
"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": "overrides/assets/stylesheets/main.c2cc92d1.min.css",
|
||||||
"overrides/assets/stylesheets/main.css.map": "overrides/assets/stylesheets/main.c2cc92d1.min.css.map"
|
"overrides/assets/stylesheets/main.css.map": "overrides/assets/stylesheets/main.c2cc92d1.min.css.map"
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="{{ 'assets/javascripts/vendor.00ecb175.min.js' | url }}"></script>
|
<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"] %}
|
{% for path in config["extra_javascript"] %}
|
||||||
<script src="{{ path | url }}"></script>
|
<script src="{{ path | url }}"></script>
|
||||||
{% endfor %}
|
{% 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
File diff suppressed because one or more lines are too long
@@ -53,5 +53,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script src="{{ 'overrides/assets/javascripts/bundle.759c98a8.min.js' | url }}"></script>
|
<script src="{{ 'overrides/assets/javascripts/bundle.f4aeaef7.min.js' | url }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Observable, ObservableInput, merge } from "rxjs"
|
import { NEVER, Observable, ObservableInput, merge } from "rxjs"
|
||||||
import { filter, sample, take } from "rxjs/operators"
|
import { filter, sample, take } from "rxjs/operators"
|
||||||
|
|
||||||
import { configuration } from "~/_"
|
import { configuration } from "~/_"
|
||||||
@@ -99,6 +99,10 @@ function fetchSearchIndex(url: string): ObservableInput<SearchIndex> {
|
|||||||
export function mountSearch(
|
export function mountSearch(
|
||||||
el: HTMLElement, { keyboard$ }: MountOptions
|
el: HTMLElement, { keyboard$ }: MountOptions
|
||||||
): Observable<Component<Search>> {
|
): Observable<Component<Search>> {
|
||||||
|
if (location.protocol === "file:")
|
||||||
|
return NEVER
|
||||||
|
|
||||||
|
/* Set up search worker */
|
||||||
const config = configuration()
|
const config = configuration()
|
||||||
const worker = setupSearchWorker(config.search, fetchSearchIndex(
|
const worker = setupSearchWorker(config.search, fetchSearchIndex(
|
||||||
`${config.base}/search/search_index.json`
|
`${config.base}/search/search_index.json`
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export function setupInstantLoading(
|
|||||||
const el = ev.target.closest("a")
|
const el = ev.target.closest("a")
|
||||||
if (el && !el.target && urls.includes(el.href)) {
|
if (el && !el.target && urls.includes(el.href)) {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
return of<HistoryState>({
|
return of({
|
||||||
url: new URL(el.href)
|
url: new URL(el.href)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ export function setupInstantLoading(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
share()
|
share<HistoryState>()
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Intercept history back and forward */
|
/* Intercept history back and forward */
|
||||||
@@ -203,8 +203,8 @@ export function setupInstantLoading(
|
|||||||
map(ev => ({
|
map(ev => ({
|
||||||
url: new URL(location.href),
|
url: new URL(location.href),
|
||||||
offset: ev.state
|
offset: ev.state
|
||||||
} as HistoryState)),
|
})),
|
||||||
share()
|
share<HistoryState>()
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Emit location change */
|
/* Emit location change */
|
||||||
|
|||||||
@@ -39,10 +39,33 @@ import {
|
|||||||
* Types
|
* 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
|
* Icon search index
|
||||||
*/
|
*/
|
||||||
export type IconSearchIndex = string[]
|
export interface IconSearchIndex {
|
||||||
|
icons: IconSearchDatabase /* Icon database */
|
||||||
|
emojis: IconSearchDatabase /* Emoji database */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon search
|
* Icon search
|
||||||
@@ -67,7 +90,7 @@ export function mountIconSearch(
|
|||||||
): Observable<Component<IconSearch>> {
|
): Observable<Component<IconSearch>> {
|
||||||
const config = configuration()
|
const config = configuration()
|
||||||
const index$ = requestJSON<IconSearchIndex>(
|
const index$ = requestJSON<IconSearchIndex>(
|
||||||
`${config.base}/overrides/assets/javascripts/icons.json`
|
`${config.base}/overrides/assets/javascripts/icon_search_index.json`
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Retrieve nested components */
|
/* Retrieve nested components */
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ import {
|
|||||||
|
|
||||||
import { renderIconSearchResult } from "../../../templates"
|
import { renderIconSearchResult } from "../../../templates"
|
||||||
import { Component } from "../../_"
|
import { Component } from "../../_"
|
||||||
import { IconSearchIndex } from "../_"
|
import { Icon, IconSearchIndex } from "../_"
|
||||||
import { IconSearchQuery } from "../query"
|
import { IconSearchQuery } from "../query"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
@@ -66,7 +66,7 @@ import { IconSearchQuery } from "../query"
|
|||||||
* Icon search result
|
* Icon search result
|
||||||
*/
|
*/
|
||||||
export interface IconSearchResult {
|
export interface IconSearchResult {
|
||||||
data: string[] /* Search result data */
|
data: Icon[] /* Search result data */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
@@ -139,11 +139,35 @@ export function mountIconSearchResult(
|
|||||||
|
|
||||||
/* Create and return component */
|
/* Create and return component */
|
||||||
return combineLatest([
|
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(
|
.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$),
|
tap(internal$),
|
||||||
finalize(() => internal$.complete()),
|
finalize(() => internal$.complete()),
|
||||||
map(state => ({ ref: el, ...state }))
|
map(state => ({ ref: el, ...state }))
|
||||||
|
|||||||
@@ -25,33 +25,12 @@ import { wrap } from "fuzzaldrin-plus"
|
|||||||
import { translation } from "~/_"
|
import { translation } from "~/_"
|
||||||
import { h } from "~/utilities"
|
import { h } from "~/utilities"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
import { Icon } from "../../components"
|
||||||
* Data
|
|
||||||
* ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Icon CDN URL
|
|
||||||
*/
|
|
||||||
const base =
|
|
||||||
"https://raw.githubusercontent.com/" +
|
|
||||||
"squidfunk/mkdocs-material/" +
|
|
||||||
"master/material/.icons/"
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Helper functions
|
* 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
|
* Highlight an icon search result
|
||||||
*
|
*
|
||||||
@@ -61,7 +40,7 @@ function shortcode(value: string): string {
|
|||||||
* @returns Highlighted result
|
* @returns Highlighted result
|
||||||
*/
|
*/
|
||||||
function highlight(value: string, query: string) {
|
function highlight(value: string, query: string) {
|
||||||
return wrap(shortcode(value), query, {
|
return wrap(value, query, {
|
||||||
wrap: {
|
wrap: {
|
||||||
tagOpen: "<b>",
|
tagOpen: "<b>",
|
||||||
tagClose: "</b>"
|
tagClose: "</b>"
|
||||||
@@ -76,25 +55,25 @@ function highlight(value: string, query: string) {
|
|||||||
/**
|
/**
|
||||||
* Render an icon search result
|
* Render an icon search result
|
||||||
*
|
*
|
||||||
* @param value - Icon search result
|
* @param icon - Icon search result
|
||||||
* @param query - Icon search query
|
* @param query - Icon search query
|
||||||
*
|
*
|
||||||
* @returns Element
|
* @returns Element
|
||||||
*/
|
*/
|
||||||
export function renderIconSearchResult(
|
export function renderIconSearchResult(
|
||||||
value: string, query: string
|
icon: Icon, query: string
|
||||||
): HTMLElement {
|
): HTMLElement {
|
||||||
return (
|
return (
|
||||||
<li class="mdx-icon-search-result__item">
|
<li class="mdx-icon-search-result__item">
|
||||||
<span class="twemoji">
|
<span class="twemoji">
|
||||||
<img src={base + value} />
|
<img src={icon.url} />
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
class="md-clipboard--inline"
|
class="md-clipboard--inline"
|
||||||
title={translation("clipboard.copy")}
|
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>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -345,14 +345,40 @@ export default (_env: never, args: Configuration): Configuration[] => {
|
|||||||
/* Save template with replaced assets */
|
/* Save template with replaced assets */
|
||||||
fs.writeFileSync(file, template, "utf8")
|
fs.writeFileSync(file, template, "utf8")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Build search index for bundled icons */
|
/* Build search index for bundled icons */
|
||||||
const index = await glob("**/*.svg", { cwd: "material/.icons" })
|
const icons: Record<string, string> = {}
|
||||||
fs.writeFileSync(
|
for (const file of await glob("**/*.svg", {
|
||||||
"material/overrides/assets/javascripts/icons.json",
|
cwd: "material/.icons"
|
||||||
JSON.stringify(index)
|
})) {
|
||||||
)
|
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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user