Merge branch 'develop' into spacy.io

This commit is contained in:
Ines Montani 2019-02-25 20:11:30 +01:00
commit 0a7a2c73e2
17 changed files with 603 additions and 93 deletions

View File

@ -9,18 +9,19 @@ from ...compat import unicode_
class RussianLemmatizer(Lemmatizer): class RussianLemmatizer(Lemmatizer):
_morph = None _morph = None
def __init__(self, pymorphy2_lang='ru'): def __init__(self):
super(RussianLemmatizer, self).__init__() super(RussianLemmatizer, self).__init__()
try: try:
from pymorphy2 import MorphAnalyzer from pymorphy2 import MorphAnalyzer
except ImportError: except ImportError:
raise ImportError( raise ImportError(
"The Russian lemmatizer requires the pymorphy2 library: " "The Russian lemmatizer requires the pymorphy2 library: "
'try to fix it with "pip install pymorphy2==0.8"' 'try to fix it with "pip install pymorphy2==0.8" '
'or "pip install git+https://github.com/kmike/pymorphy2.git pymorphy2-dicts-uk"'
"if you need Ukrainian too"
) )
if RussianLemmatizer._morph is None: if RussianLemmatizer._morph is None:
RussianLemmatizer._morph = MorphAnalyzer(lang=pymorphy2_lang) RussianLemmatizer._morph = MorphAnalyzer()
def __call__(self, string, univ_pos, morphology=None): def __call__(self, string, univ_pos, morphology=None):
univ_pos = self.normalize_univ_pos(univ_pos) univ_pos = self.normalize_univ_pos(univ_pos)

View File

@ -1,15 +1,207 @@
# coding: utf8 # coding: utf8
from __future__ import unicode_literals from ...symbols import ADJ, DET, NOUN, NUM, PRON, PROPN, PUNCT, VERB, POS
from ...lemmatizer import Lemmatizer
from ..ru.lemmatizer import RussianLemmatizer
class UkrainianLemmatizer(RussianLemmatizer): class UkrainianLemmatizer(Lemmatizer):
def __init__(self, pymorphy2_lang="ru"): _morph = None
def __init__(self):
super(UkrainianLemmatizer, self).__init__()
try: try:
super(UkrainianLemmatizer, self).__init__(pymorphy2_lang="uk") from pymorphy2 import MorphAnalyzer
except ImportError:
if UkrainianLemmatizer._morph is None:
UkrainianLemmatizer._morph = MorphAnalyzer(lang="uk")
except (ImportError, TypeError):
raise ImportError( raise ImportError(
"The Ukrainian lemmatizer requires the pymorphy2 library and dictionaries: " "The Ukrainian lemmatizer requires the pymorphy2 library and
'try to fix it with "pip install git+https://github.com/kmike/pymorphy2.git pymorphy2-dicts-uk"' 'dictionaries: try to fix it with "pip uninstall pymorphy2" and'
'"pip install git+https://github.com/kmike/pymorphy2.git pymorphy2-dicts-uk"'
) )
def __call__(self, string, univ_pos, morphology=None):
univ_pos = self.normalize_univ_pos(univ_pos)
if univ_pos == "PUNCT":
return [PUNCT_RULES.get(string, string)]
if univ_pos not in ("ADJ", "DET", "NOUN", "NUM", "PRON", "PROPN", "VERB"):
# Skip unchangeable pos
return [string.lower()]
analyses = self._morph.parse(string)
filtered_analyses = []
for analysis in analyses:
if not analysis.is_known:
# Skip suggested parse variant for unknown word for pymorphy
continue
analysis_pos, _ = oc2ud(str(analysis.tag))
if analysis_pos == univ_pos or (
analysis_pos in ("NOUN", "PROPN") and univ_pos in ("NOUN", "PROPN")
):
filtered_analyses.append(analysis)
if not len(filtered_analyses):
return [string.lower()]
if morphology is None or (len(morphology) == 1 and POS in morphology):
return list(set([analysis.normal_form for analysis in filtered_analyses]))
if univ_pos in ("ADJ", "DET", "NOUN", "PROPN"):
features_to_compare = ["Case", "Number", "Gender"]
elif univ_pos == "NUM":
features_to_compare = ["Case", "Gender"]
elif univ_pos == "PRON":
features_to_compare = ["Case", "Number", "Gender", "Person"]
else: # VERB
features_to_compare = [
"Aspect",
"Gender",
"Mood",
"Number",
"Tense",
"VerbForm",
"Voice",
]
analyses, filtered_analyses = filtered_analyses, []
for analysis in analyses:
_, analysis_morph = oc2ud(str(analysis.tag))
for feature in features_to_compare:
if (
feature in morphology
and feature in analysis_morph
and morphology[feature] != analysis_morph[feature]
):
break
else:
filtered_analyses.append(analysis)
if not len(filtered_analyses):
return [string.lower()]
return list(set([analysis.normal_form for analysis in filtered_analyses]))
@staticmethod
def normalize_univ_pos(univ_pos):
if isinstance(univ_pos, str):
return univ_pos.upper()
symbols_to_str = {
ADJ: "ADJ",
DET: "DET",
NOUN: "NOUN",
NUM: "NUM",
PRON: "PRON",
PROPN: "PROPN",
PUNCT: "PUNCT",
VERB: "VERB",
}
if univ_pos in symbols_to_str:
return symbols_to_str[univ_pos]
return None
def is_base_form(self, univ_pos, morphology=None):
# TODO
raise NotImplementedError
def det(self, string, morphology=None):
return self(string, "det", morphology)
def num(self, string, morphology=None):
return self(string, "num", morphology)
def pron(self, string, morphology=None):
return self(string, "pron", morphology)
def lookup(self, string):
analyses = self._morph.parse(string)
if len(analyses) == 1:
return analyses[0].normal_form
return string
def oc2ud(oc_tag):
gram_map = {
"_POS": {
"ADJF": "ADJ",
"ADJS": "ADJ",
"ADVB": "ADV",
"Apro": "DET",
"COMP": "ADJ", # Can also be an ADV - unchangeable
"CONJ": "CCONJ", # Can also be a SCONJ - both unchangeable ones
"GRND": "VERB",
"INFN": "VERB",
"INTJ": "INTJ",
"NOUN": "NOUN",
"NPRO": "PRON",
"NUMR": "NUM",
"NUMB": "NUM",
"PNCT": "PUNCT",
"PRCL": "PART",
"PREP": "ADP",
"PRTF": "VERB",
"PRTS": "VERB",
"VERB": "VERB",
},
"Animacy": {"anim": "Anim", "inan": "Inan"},
"Aspect": {"impf": "Imp", "perf": "Perf"},
"Case": {
"ablt": "Ins",
"accs": "Acc",
"datv": "Dat",
"gen1": "Gen",
"gen2": "Gen",
"gent": "Gen",
"loc2": "Loc",
"loct": "Loc",
"nomn": "Nom",
"voct": "Voc",
},
"Degree": {"COMP": "Cmp", "Supr": "Sup"},
"Gender": {"femn": "Fem", "masc": "Masc", "neut": "Neut"},
"Mood": {"impr": "Imp", "indc": "Ind"},
"Number": {"plur": "Plur", "sing": "Sing"},
"NumForm": {"NUMB": "Digit"},
"Person": {"1per": "1", "2per": "2", "3per": "3", "excl": "2", "incl": "1"},
"Tense": {"futr": "Fut", "past": "Past", "pres": "Pres"},
"Variant": {"ADJS": "Brev", "PRTS": "Brev"},
"VerbForm": {
"GRND": "Conv",
"INFN": "Inf",
"PRTF": "Part",
"PRTS": "Part",
"VERB": "Fin",
},
"Voice": {"actv": "Act", "pssv": "Pass"},
"Abbr": {"Abbr": "Yes"},
}
pos = "X"
morphology = dict()
unmatched = set()
grams = oc_tag.replace(" ", ",").split(",")
for gram in grams:
match = False
for categ, gmap in sorted(gram_map.items()):
if gram in gmap:
match = True
if categ == "_POS":
pos = gmap[gram]
else:
morphology[categ] = gmap[gram]
if not match:
unmatched.add(gram)
while len(unmatched) > 0:
gram = unmatched.pop()
if gram in ("Name", "Patr", "Surn", "Geox", "Orgn"):
pos = "PROPN"
elif gram == "Auxt":
pos = "AUX"
elif gram == "Pltm":
morphology["Number"] = "Ptan"
return pos, morphology
PUNCT_RULES = {"«": '"', "»": '"'}

View File

@ -6,8 +6,10 @@ STOP_WORDS = set(
"""а """а
або або
адже адже
аж
але але
алло алло
б
багато багато
без без
безперервно безперервно
@ -16,6 +18,7 @@ STOP_WORDS = set(
більше більше
біля біля
близько близько
бо
був був
буває буває
буде буде
@ -29,22 +32,27 @@ STOP_WORDS = set(
були були
було було
бути бути
бывь
в в
важлива
важливе
важливий
важливі
вам вам
вами вами
вас вас
ваш ваш
ваша ваша
ваше ваше
вашим
вашими
ваших
ваші ваші
вашій
вашого
вашої
вашому
вашою
вашу
вгорі вгорі
вгору вгору
вдалині вдалині
весь
вже вже
ви ви
від від
@ -59,7 +67,15 @@ STOP_WORDS = set(
вони вони
воно воно
восьмий восьмий
все
всею
всі
всім
всіх
всього всього
всьому
всю
вся
втім втім
г г
геть геть
@ -95,16 +111,15 @@ STOP_WORDS = set(
досить досить
другий другий
дуже дуже
дякую
е
є
ж
же же
життя
з з
за за
завжди завжди
зазвичай зазвичай
зайнята
зайнятий
зайняті
зайнято
занадто занадто
зараз зараз
зате зате
@ -112,22 +127,28 @@ STOP_WORDS = set(
звідси звідси
звідусіль звідусіль
здається здається
зі
значить значить
знову знову
зовсім зовсім
ім'я і
із
її
їй
їм
іноді іноді
інша інша
інше інше
інший інший
інших інших
інші інші
її
їй
їх їх
й
його його
йому йому
каже
ким ким
кілька
кого кого
кожен кожен
кожна кожна
@ -136,13 +157,13 @@ STOP_WORDS = set(
коли коли
кому кому
краще краще
крейдуючи крім
кругом
куди куди
ласка ласка
ледве
лише лише
люди м
людина має
майже майже
мало мало
мати мати
@ -157,20 +178,27 @@ STOP_WORDS = set(
мій мій
мільйонів мільйонів
мною мною
мого
могти могти
моє моє
мож моєї
моєму
моєю
може може
можна можна
можно можно
можуть можуть
можхо
мої мої
мор моїй
моїм
моїми
моїх
мою
моя моя
на на
навіть навіть
навіщо навіщо
навколо
навкруги навкруги
нагорі нагорі
над над
@ -183,10 +211,21 @@ STOP_WORDS = set(
наш наш
наша наша
наше наше
нашим
нашими
наших
наші наші
нашій
нашого
нашої
нашому
нашою
нашу
не не
небагато небагато
небудь
недалеко недалеко
неї
немає немає
нерідко нерідко
нещодавно нещодавно
@ -199,17 +238,22 @@ STOP_WORDS = set(
них них
ні ні
ніби ніби
ніж
ній
ніколи ніколи
нікуди нікуди
нім
нічого нічого
ну ну
нх
нього нього
ньому
о о
обидва
обоє обоє
один один
одинадцятий одинадцятий
одинадцять одинадцять
однак
однієї однієї
одній одній
одного одного
@ -218,11 +262,16 @@ STOP_WORDS = set(
он он
особливо особливо
ось ось
п'ятий
п'ятнадцятий
п'ятнадцять
п'ять
перед перед
перший перший
під під
пізніше пізніше
пір пір
після
по по
повинно повинно
подів подів
@ -233,18 +282,14 @@ STOP_WORDS = set(
потім потім
потрібно потрібно
почала почала
прекрасне початку
прекрасно
при при
про про
просто просто
проте проте
проти проти
п'ятий
п'ятнадцятий
п'ятнадцять
п'ять
раз раз
разу
раніше раніше
рано рано
раптом раптом
@ -252,6 +297,7 @@ STOP_WORDS = set(
роки роки
років років
року року
році
сам сам
сама сама
саме саме
@ -264,15 +310,15 @@ STOP_WORDS = set(
самого самого
самому самому
саму саму
світу
свого свого
своє своє
своєї
свої свої
своїй своїй
своїх своїх
свою свою
сеаой
себе себе
сих
сім сім
сімнадцятий сімнадцятий
сімнадцять сімнадцять
@ -300,7 +346,17 @@ STOP_WORDS = set(
також також
там там
твій твій
твого
твоє твоє
твоєї
твоєму
твоєю
твої
твоїй
твоїм
твоїми
твоїх
твою
твоя твоя
те те
тебе тебе
@ -312,15 +368,19 @@ STOP_WORDS = set(
тисяч тисяч
тих тих
ті ті
тієї
тією тією
тій
тільки тільки
тім
то
тобі тобі
тобою тобою
того того
тоді тоді
той той
том
тому тому
тою
треба треба
третій третій
три три
@ -338,7 +398,6 @@ STOP_WORDS = set(
усім усім
усіма усіма
усіх усіх
усію
усього усього
усьому усьому
усю усю
@ -356,6 +415,7 @@ STOP_WORDS = set(
цими цими
цих цих
ці ці
цієї
цій цій
цього цього
цьому цьому
@ -368,11 +428,24 @@ STOP_WORDS = set(
через через
четвертий четвертий
чи чи
чиє
чиєї
чиєму
чиї
чиїй
чиїм
чиїми
чиїх
чий
чийого
чийому
чим чим
численна численна
численне численне
численний численний
численні численні
чию
чия
чого чого
чому чому
чотири чотири
@ -385,6 +458,8 @@ STOP_WORDS = set(
ще ще
що що
щоб щоб
щодо
щось
я я
як як
яка яка
@ -393,6 +468,6 @@ STOP_WORDS = set(
які які
якій якій
якого якого
якщо якої
""".split() якщо""".split()
) )

View File

@ -573,7 +573,7 @@ class Language(object):
proc._rehearsal_model = deepcopy(proc.model) proc._rehearsal_model = deepcopy(proc.model)
return self._optimizer return self._optimizer
def evaluate(self, docs_golds, verbose=False): def evaluate(self, docs_golds, verbose=False, batch_size=256):
scorer = Scorer() scorer = Scorer()
docs, golds = zip(*docs_golds) docs, golds = zip(*docs_golds)
docs = list(docs) docs = list(docs)
@ -582,7 +582,7 @@ class Language(object):
if not hasattr(pipe, "pipe"): if not hasattr(pipe, "pipe"):
docs = (pipe(doc) for doc in docs) docs = (pipe(doc) for doc in docs)
else: else:
docs = pipe.pipe(docs, batch_size=256) docs = pipe.pipe(docs, batch_size=batch_size)
for doc, gold in zip(docs, golds): for doc, gold in zip(docs, golds):
if verbose: if verbose:
print(doc) print(doc)

View File

@ -179,6 +179,7 @@ def tt_tokenizer():
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def uk_tokenizer(): def uk_tokenizer():
pytest.importorskip("pymorphy2") pytest.importorskip("pymorphy2")
pytest.importorskip("pymorphy2.lang")
return get_lang_class("uk").Defaults.create_tokenizer() return get_lang_class("uk").Defaults.create_tokenizer()

View File

@ -92,6 +92,7 @@ def test_uk_tokenizer_splits_open_appostrophe(uk_tokenizer, text):
assert tokens[0].text == "'" assert tokens[0].text == "'"
@pytest.mark.xfail(reason="See #3327")
@pytest.mark.parametrize("text", ["Тест''"]) @pytest.mark.parametrize("text", ["Тест''"])
def test_uk_tokenizer_splits_double_end_quote(uk_tokenizer, text): def test_uk_tokenizer_splits_double_end_quote(uk_tokenizer, text):
tokens = uk_tokenizer(text) tokens = uk_tokenizer(text)

View File

@ -0,0 +1,21 @@
# coding: utf-8
from __future__ import unicode_literals
import pytest
from spacy.matcher import Matcher
from spacy.tokens import Doc
@pytest.mark.xfail
def test_issue3328(en_vocab):
doc = Doc(en_vocab, words=["Hello", ",", "how", "are", "you", "doing", "?"])
matcher = Matcher(en_vocab)
patterns = [
[{"LOWER": {"IN": ["hello", "how"]}}],
[{"LOWER": {"IN": ["you", "doing"]}}],
]
matcher.add("TEST", None, *patterns)
matches = matcher(doc)
assert len(matches) == 4
matched_texts = [doc[start:end].text for _, start, end in matches]
assert matched_texts == ["Hello", "how", "you", "doing"]

View File

@ -22,6 +22,10 @@
"id": "83b0498b1e7fa3c91ce68c3f1", "id": "83b0498b1e7fa3c91ce68c3f1",
"list": "89ad33e698" "list": "89ad33e698"
}, },
"docSearch": {
"apiKey": "371e26ed49d29a27bd36273dfdaf89af",
"indexName": "spacy"
},
"spacyVersion": "2.1", "spacyVersion": "2.1",
"binderUrl": "ines/spacy-io-binder", "binderUrl": "ines/spacy-io-binder",
"binderBranch": "nightly", "binderBranch": "nightly",

View File

@ -18,6 +18,7 @@ import { ReactComponent as YesIcon } from '../images/icons/yes.svg'
import { ReactComponent as NoIcon } from '../images/icons/no.svg' import { ReactComponent as NoIcon } from '../images/icons/no.svg'
import { ReactComponent as NeutralIcon } from '../images/icons/neutral.svg' import { ReactComponent as NeutralIcon } from '../images/icons/neutral.svg'
import { ReactComponent as OfflineIcon } from '../images/icons/offline.svg' import { ReactComponent as OfflineIcon } from '../images/icons/offline.svg'
import { ReactComponent as SearchIcon } from '../images/icons/search.svg'
import classes from '../styles/icon.module.sass' import classes from '../styles/icon.module.sass'
@ -39,6 +40,7 @@ const icons = {
no: NoIcon, no: NoIcon,
neutral: NeutralIcon, neutral: NeutralIcon,
offline: OfflineIcon, offline: OfflineIcon,
search: SearchIcon,
} }
const Icon = ({ name, width, height, inline, variant, className }) => { const Icon = ({ name, width, height, inline, variant, className }) => {

View File

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classNames from 'classnames' import classNames from 'classnames'
import { navigate } from 'gatsby'
import Link from './link' import Link from './link'
import Icon from './icon' import Icon from './icon'
@ -8,14 +9,39 @@ import { github } from './util'
import { ReactComponent as Logo } from '../images/logo.svg' import { ReactComponent as Logo } from '../images/logo.svg'
import classes from '../styles/navigation.module.sass' import classes from '../styles/navigation.module.sass'
const Navigation = ({ title, items, section, children }) => ( const Dropdown = ({ items, section }) => {
const active = items.find(({ text }) => text.toLowerCase() === section)
const defaultValue = active ? active.url : 'title'
return (
<select
defaultValue={defaultValue}
className={classes.dropdown}
onChange={({ target }) => navigate(target.value)}
>
<option value="title" disabled>
Menu
</option>
{items.map(({ text, url }, i) => (
<option key={i} value={url}>
{text}
</option>
))}
</select>
)
}
const Navigation = ({ title, items, section, search, children }) => {
return (
<nav className={classes.root}> <nav className={classes.root}>
<Link to="/" aria-label={title} hidden> <Link to="/" aria-label={title} hidden>
<h1 className={classes.title}>{title}</h1> <h1 className={classes.title}>{title}</h1>
<Logo className={classes.logo} width={300} height={96} /> <Logo className={classes.logo} width={300} height={96} />
</Link> </Link>
<ul className={classes.menu}> <div className={classes.menu}>
<Dropdown items={items} section={section} />
<ul className={classes.list}>
{items.map(({ text, url }, i) => { {items.map(({ text, url }, i) => {
const isActive = section && text.toLowerCase() === section const isActive = section && text.toLowerCase() === section
const itemClassNames = classNames(classes.item, { const itemClassNames = classNames(classes.item, {
@ -35,9 +61,12 @@ const Navigation = ({ title, items, section, children }) => (
</Link> </Link>
</li> </li>
</ul> </ul>
{search && <div className={classes.search}>{search}</div>}
</div>
{children} {children}
</nav> </nav>
) )
}
Navigation.defaultProps = { Navigation.defaultProps = {
items: [], items: [],
@ -52,6 +81,7 @@ Navigation.propTypes = {
}) })
), ),
section: PropTypes.string, section: PropTypes.string,
search: PropTypes.node,
} }
export default Navigation export default Navigation

View File

@ -0,0 +1,52 @@
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import { window } from 'browser-monads'
import Icon from './icon'
import classes from '../styles/search.module.sass'
const Search = ({ id, placeholder, settings }) => {
const { apiKey, indexName } = settings
const [isInitialized, setIsInitialized] = useState(false)
useEffect(() => {
if (!isInitialized) {
setIsInitialized(true)
window.docsearch({
apiKey,
indexName,
inputSelector: `#${id}`,
debug: false,
})
}
}, window.docsearch)
return (
<form className={classes.root}>
<label htmlFor={id} className={classes.icon}>
<Icon name="search" width={24} />
</label>
<input
id={id}
className={classes.input}
type="search"
placeholder={placeholder}
aria-label={placeholder}
/>
</form>
)
}
Search.defaultProps = {
id: 'docsearch',
placeholder: 'Search docs',
}
Search.propTypes = {
settings: PropTypes.shape({
apiKey: PropTypes.string.isRequired,
indexName: PropTypes.string.isRequired,
}).isRequired,
id: PropTypes.string.isRequired,
placeholder: PropTypes.string.isRequired,
}
export default Search

View File

@ -1,5 +1,6 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classNames from 'classnames'
import Button from './button' import Button from './button'
import Tag from './tag' import Tag from './tag'
@ -34,7 +35,7 @@ const Title = ({ title, tag, version, teaser, source, image, children, ...props
</Tag> </Tag>
)} )}
{teaser && <div className={classes.teaser}>{teaser}</div>} {teaser && <div className={classNames('heading-teaser', classes.teaser)}>{teaser}</div>}
{children} {children}
</header> </header>

43
website/src/html.js Normal file
View File

@ -0,0 +1,43 @@
import React from 'react'
import PropTypes from 'prop-types'
export default function HTML(props) {
return (
<html {...props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
{props.headComponents}
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css"
/>
</head>
<body {...props.bodyAttributes}>
{props.preBodyComponents}
<noscript key="noscript" id="gatsby-noscript">
This app works best with JavaScript enabled.
</noscript>
<div key={`body`} id="___gatsby" dangerouslySetInnerHTML={{ __html: props.body }} />
{props.postBodyComponents}
</body>
<script
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"
/>
</html>
)
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M9.516 14.016c2.484 0 4.5-2.016 4.5-4.5s-2.016-4.5-4.5-4.5-4.5 2.016-4.5 4.5 2.016 4.5 4.5 4.5zM15.516 14.016l4.969 4.969-1.5 1.5-4.969-4.969v-0.797l-0.281-0.281c-1.125 0.984-2.625 1.547-4.219 1.547-3.609 0-6.516-2.859-6.516-6.469s2.906-6.516 6.516-6.516 6.469 2.906 6.469 6.516c0 1.594-0.563 3.094-1.547 4.219l0.281 0.281h0.797z"></path>
</svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@ -18,6 +18,7 @@
box-shadow: var(--box-shadow) box-shadow: var(--box-shadow)
.logo .logo
min-width: 95px
width: 95px width: 95px
height: 30px height: 30px
color: var(--color-theme) !important color: var(--color-theme) !important
@ -26,23 +27,12 @@
.title .title
display: none display: none
.menu .list
display: flex display: flex
height: 100%
flex-flow: row nowrap flex-flow: row nowrap
border-color: inherit border-color: inherit
overflow-x: auto
overflow-y: hidden
-webkit-overflow-scrolling: touch
@include breakpoint(max, sm)
.menu
@include scroll-shadow-base(var(--color-shadow))
margin-left: 1rem
@include breakpoint(min, md)
.menu
justify-content: flex-end justify-content: flex-end
margin-bottom: 0.25rem
.item .item
display: inline-flex display: inline-flex
@ -58,13 +48,40 @@
margin-left: 2em margin-left: 2em
&:last-child &:last-child
@include scroll-shadow-cover(right, var(--color-back))
padding-right: 2rem padding-right: 2rem
&:first-child &:first-child
@include scroll-shadow-cover(left, var(--color-back))
padding-left: 2rem padding-left: 2rem
.menu
display: flex
flex-flow: row nowrap
margin-left: 0.5rem
.dropdown
font-family: var(--font-secondary)
font-size: 1.6rem
font-weight: bold
color: var(--color-theme)
text-transform: uppercase
background: transparent
border: 0
margin-right: 0.5rem
.is-active .is-active
color: var(--color-dark) color: var(--color-dark)
pointer-events: none pointer-events: none
.search
display: inline-flex
align-items: center
height: 100%
padding-right: 2rem
@include breakpoint(max, xs)
.list
display: none
@include breakpoint(min, sm)
.dropdown
display: none

View File

@ -0,0 +1,57 @@
@import base
.root
font: var(--font-size-sm)/var(--line-height-md) var(--font-primary)
border: 1px solid var(--color-subtle)
border-radius: 2em
max-width: 100%
display: flex
flex-flow: row nowrap
&:focus-within
border-color: var(--color-theme)
box-shadow: 0 0 0 1px var(--color-theme)
.input
width: 100%
max-width: 100%
font: inherit
padding: 0.5rem 1rem 0.5rem 0.5rem
.icon
display: inline-block
color: var(--color-subtle-dark)
padding-left: 0.5rem
position: relative
top: 0.25rem
cursor: pointer
/* Algolia DocSearch */
@include breakpoint(max, xs)
\:global(.algolia-autocomplete .ds-dropdown-menu)
max-width: 90vw
min-width: 90vw
\:global(.algolia-autocomplete .algolia-docsearch-suggestion--category-header)
display: block
font: bold var(--font-size-lg)/var(--line-height-md) var(--font-secondary)
text-transform: uppercase
color: var(--color-theme)
\:global(.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column)
color: var(--color-dark)
\:global(.algolia-autocomplete .algolia-docsearch-suggestion--title)
font-size: var(--font-size-sm)
\:global(.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion--content)
background: var(--color-subtle-opaque) !important
\:global(.algolia-autocomplete .algolia-docsearch-suggestion--text)
font-size: var(--font-size-sm)
\:global(.algolia-autocomplete .algolia-docsearch-suggestion--highlight)
box-shadow: none !important
background: var(--color-theme-opaque) !important
color: var(--color-theme-dark) !important

View File

@ -30,6 +30,7 @@ import Tag from '../components/tag'
import Grid from '../components/grid' import Grid from '../components/grid'
import { YouTube, SoundCloud, Iframe, Image } from '../components/embed' import { YouTube, SoundCloud, Iframe, Image } from '../components/embed'
import Alert from '../components/alert' import Alert from '../components/alert'
import Search from '../components/search'
const mdxComponents = { const mdxComponents = {
a: Link, a: Link,
@ -119,7 +120,12 @@ class Layout extends React.Component {
bodyClass={bodyClass} bodyClass={bodyClass}
/> />
<AlertSpace /> <AlertSpace />
<Navigation title={meta.title} items={meta.navigation} section={section}> <Navigation
title={meta.title}
items={meta.navigation}
section={section}
search={<Search settings={meta.docSearch} />}
>
<Progress key={location.href} /> <Progress key={location.href} />
</Navigation> </Navigation>
{isDocs ? ( {isDocs ? (
@ -184,6 +190,10 @@ export const pageQuery = graphql`
text text
url url
} }
docSearch {
apiKey
indexName
}
} }
} }
file(fields: { slug: { eq: $slug } }) { file(fields: { slug: { eq: $slug } }) {