Skip to content

Syntax lookup: Fix routing #445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion misc_docs/syntax/extension_raw_expression.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var canUseCanvas = function canUseCanvas() {

</CodeTab>

See `%%raw` for embedding top level blocks of JavaScript code rather than expressions.
See [`%%raw`](#raw-top-level-expression) for embedding top level blocks of JavaScript code rather than expressions.

### References

Expand Down
2 changes: 1 addition & 1 deletion misc_docs/syntax/extension_raw_top_level_expression.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import "main.css";

</CodeTab>

See `%raw` for embedding JavaScript expressions rather than top level blocks of code.
See [`%raw`](#raw-expression) for embedding JavaScript expressions rather than top level blocks of code.

### References

Expand Down
2 changes: 1 addition & 1 deletion misc_docs/syntax/operators_float_addition.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ var result = 1.3 + 0.5;

</CodeTab>

For adding *integers* see the `+` operator.
For adding *integers* see the [`+`](#integer-addition) operator.
4 changes: 2 additions & 2 deletions misc_docs/syntax/operators_integer_addition.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ val result = 3;

</CodeTab>

For adding *floats* see the `+.` operator.
For adding *floats* see the [`+.`](#float-addition) operator.

For contatenating *strings* see the `++` operator.
For contatenating *strings* see the [`++`](#string-concatenation) operator.
127 changes: 52 additions & 75 deletions src/SyntaxLookup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import * as SearchBox from "./components/SearchBox.mjs";
import * as Belt_Array from "rescript/lib/es6/belt_Array.js";
import * as Belt_Option from "rescript/lib/es6/belt_Option.js";
import * as Caml_option from "rescript/lib/es6/caml_option.js";
import * as GithubSlugger from "github-slugger";

var indexData = (require('index_data/syntax_index.json'));

Expand Down Expand Up @@ -150,6 +149,24 @@ function SyntaxLookup$DetailBox(Props) {
}, children));
}

function findItemById(id) {
return Caml_option.undefined_to_opt(allItems.find(function (item) {
return item.id === id;
}));
}

function findItemByExactName(name) {
return Caml_option.undefined_to_opt(allItems.find(function (item) {
return item.name === name;
}));
}

function searchItems(value) {
return Belt_Array.map(fuse.search(value), (function (m) {
return m.item;
}));
}

function SyntaxLookup(Props) {
var router = Next.Router.useRouter(undefined);
var match = React.useState(function () {
Expand All @@ -160,76 +177,44 @@ function SyntaxLookup(Props) {
React.useEffect((function () {
var anchor = getAnchor(router.asPath);
if (anchor !== undefined) {
Belt_Option.forEach(Caml_option.undefined_to_opt(allItems.find(function (item) {
return GithubSlugger.slug(item.id) === anchor;
})), (function (item) {
return Curry._1(setState, (function (param) {
return {
TAG: 1,
_0: item,
[Symbol.for("name")]: "ShowDetails"
};
}));
}));
}

}), []);
React.useEffect((function () {
var match = getAnchor(router.asPath);
var exit = 0;
if (typeof state === "number" || state.TAG === /* ShowFiltered */0) {
exit = 1;
} else {
var item = state._0;
if (match !== undefined) {
var slug = GithubSlugger.slug(item.id);
if (slug !== match) {
Next.Router.replace(router, "syntax-lookup#" + match);
}

var item = findItemById(anchor);
if (item !== undefined) {
Curry._1(setState, (function (param) {
return {
TAG: 1,
_0: item,
[Symbol.for("name")]: "ShowDetails"
};
}));
window.scrollTo(0, 0);
} else {
Next.Router.replace(router, "syntax-lookup#" + GithubSlugger.slug(item.id));
}
}
if (exit === 1) {
if (match !== undefined) {
Next.Router.replace(router, "syntax-lookup");
Curry._1(setState, (function (param) {
return /* ShowAll */0;
}));
}

} else {
Curry._1(setState, (function (param) {
return /* ShowAll */0;
}));
}

}), [state]);
}), [router]);
var onSearchValueChange = function (value) {
if (value === "") {
return Next.Router.push(router, "/syntax-lookup");
}
var item = findItemByExactName(value);
if (item !== undefined) {
return Next.Router.push(router, "/syntax-lookup#" + item.id);
}
var filtered = searchItems(value);
return Curry._1(setState, (function (param) {
if (value === "") {
return /* ShowAll */0;
}
var filtered = Belt_Array.map(fuse.search(value), (function (m) {
return m.item;
}));
if (filtered.length !== 1) {
return {
TAG: 0,
_0: value,
_1: filtered,
[Symbol.for("name")]: "ShowFiltered"
};
}
var item = Belt_Array.getExn(filtered, 0);
if (item.name === value) {
return {
TAG: 1,
_0: item,
[Symbol.for("name")]: "ShowDetails"
};
} else {
return {
TAG: 0,
_0: value,
_1: filtered,
[Symbol.for("name")]: "ShowFiltered"
};
}
return {
TAG: 0,
_0: value,
_1: filtered,
[Symbol.for("name")]: "ShowFiltered"
};
}));
};
var details;
Expand Down Expand Up @@ -277,13 +262,7 @@ function SyntaxLookup(Props) {
var children = Belt_Array.map(items, (function (item) {
var onMouseDown = function (evt) {
evt.preventDefault();
return Curry._1(setState, (function (param) {
return {
TAG: 1,
_0: item,
[Symbol.for("name")]: "ShowDetails"
};
}));
return onSearchValueChange(item.name);
};
return React.createElement("span", {
key: item.name,
Expand Down Expand Up @@ -322,9 +301,7 @@ function SyntaxLookup(Props) {
];
}
var onSearchClear = function (param) {
return Curry._1(setState, (function (param) {
return /* ShowAll */0;
}));
return onSearchValueChange("");
};
return React.createElement("div", undefined, React.createElement("div", {
className: "flex flex-col items-center"
Expand Down
108 changes: 51 additions & 57 deletions src/SyntaxLookup.res
Original file line number Diff line number Diff line change
Expand Up @@ -155,74 +155,68 @@ type state =
| ShowFiltered(string, array<item>) // (search, filteredItems)
| ShowDetails(item)

@val @scope("window")
external scrollTo: (int, int) => unit = "scrollTo"

let scrollToTop = () => scrollTo(0, 0)

let findItemById = id => allItems->Js.Array2.find(item => item.id === id)

let findItemByExactName = name => allItems->Js.Array2.find(item => item.name === name)

let searchItems = value =>
fuse
->Fuse.search(value)
->Belt.Array.map(m => {
m["item"]
})

@react.component
let make = () => {
let router = Next.Router.useRouter()
let (state, setState) = React.useState(_ => ShowAll)

React.useEffect0(() => {
// This effect is responsible for updating the view state when the router anchor changes.
// This effect is triggered when:
// [A] The page first loads.
// [B] The search box is cleared.
// [C] The search box value exactly matches an item name.
React.useEffect1(() => {
switch getAnchor(router.asPath) {
| None => setState(_ => ShowAll)
| Some(anchor) =>
Js.Array2.find(allItems, item =>
GithubSlugger.slug(item.id) === anchor
)->Belt.Option.forEach(item => {
setState(_ => ShowDetails(item))
})
| None => ()
}
None
})

/*
This effect syncs the url anchor with the currently shown final match
within the fuzzy finder. If there is a new match that doesn't align with
the current anchor, the url will be rewritten with the correct anchor.

In case a selection got removed, it will also remove the anchor from the url.
We don't replace on every state change, because replacing the url is expensive
*/
React.useEffect1(() => {
switch (state, getAnchor(router.asPath)) {
| (ShowDetails(item), Some(anchor)) =>
let slug = GithubSlugger.slug(item.id)

if slug !== anchor {
router->Next.Router.replace("syntax-lookup#" ++ anchor)
} else {
()
switch findItemById(anchor) {
| None => setState(_ => ShowAll)
| Some(item) => {
setState(_ => ShowDetails(item))
scrollToTop()
}
}
| (ShowDetails(item), None) =>
router->Next.Router.replace("syntax-lookup#" ++ GithubSlugger.slug(item.id))
| (_, Some(_)) => router->Next.Router.replace("syntax-lookup")
| _ => ()
}
None
}, [state])

}, [router])

// onSearchValueChange() is called when:
// [A] The search value changes.
// [B] The search is cleared.
// [C] One of the tags is selected.
//
// We then handle three cases:
// [1] Search is empty - trigger a route change, and allow the EFFECT to update the view state.
// [2] Search exactly matches an item - trigger a route change, and allow the EFFECT to update the view state.
// [3] Search does not match an item - immediately update the view state to show filtered items.
let onSearchValueChange = value => {
setState(_ =>
switch value {
| "" => ShowAll
| search =>
let filtered =
fuse
->Fuse.search(search)
->Belt.Array.map(m => {
m["item"]
})

if Js.Array.length(filtered) === 1 {
let item = Belt.Array.getExn(filtered, 0)
if item.name === value {
ShowDetails(item)
} else {
ShowFiltered(value, filtered)
}
} else {
ShowFiltered(value, filtered)
switch value {
| "" => router->Next.Router.push("/syntax-lookup")
| value =>
switch findItemByExactName(value) {
| None => {
let filtered = searchItems(value)
setState(_ => ShowFiltered(value, filtered))
}
| Some(item) => router->Next.Router.push("/syntax-lookup#" ++ item.id)
}
)
}
}

let details = switch state {
Expand Down Expand Up @@ -273,7 +267,7 @@ let make = () => {
let children = Belt.Array.map(items, item => {
let onMouseDown = evt => {
ReactEvent.Mouse.preventDefault(evt)
setState(_ => ShowDetails(item))
onSearchValueChange(item.name)
}
<span className="mr-2 mb-2 cursor-pointer" onMouseDown key=item.name>
<Tag text={item.name} />
Expand All @@ -296,7 +290,7 @@ let make = () => {
}

let onSearchClear = () => {
setState(_ => ShowAll)
onSearchValueChange("")
}

<div>
Expand Down