Skip to content

Commit e4a0b35

Browse files
Syntax lookup: Fix routing (#445)
* Syntax lookup: Fix routing * Add links to raw syntax lookup pages
1 parent 09bb722 commit e4a0b35

File tree

6 files changed

+108
-137
lines changed

6 files changed

+108
-137
lines changed

misc_docs/syntax/extension_raw_expression.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ var canUseCanvas = function canUseCanvas() {
2626

2727
</CodeTab>
2828

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

3131
### References
3232

misc_docs/syntax/extension_raw_top_level_expression.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import "main.css";
4848

4949
</CodeTab>
5050

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

5353
### References
5454

misc_docs/syntax/operators_float_addition.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ var result = 1.3 + 0.5;
2020

2121
</CodeTab>
2222

23-
For adding *integers* see the `+` operator.
23+
For adding *integers* see the [`+`](#integer-addition) operator.

misc_docs/syntax/operators_integer_addition.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ val result = 3;
2020

2121
</CodeTab>
2222

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

25-
For contatenating *strings* see the `++` operator.
25+
For contatenating *strings* see the [`++`](#string-concatenation) operator.

src/SyntaxLookup.mjs

Lines changed: 52 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import * as SearchBox from "./components/SearchBox.mjs";
1010
import * as Belt_Array from "rescript/lib/es6/belt_Array.js";
1111
import * as Belt_Option from "rescript/lib/es6/belt_Option.js";
1212
import * as Caml_option from "rescript/lib/es6/caml_option.js";
13-
import * as GithubSlugger from "github-slugger";
1413

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

@@ -150,6 +149,24 @@ function SyntaxLookup$DetailBox(Props) {
150149
}, children));
151150
}
152151

152+
function findItemById(id) {
153+
return Caml_option.undefined_to_opt(allItems.find(function (item) {
154+
return item.id === id;
155+
}));
156+
}
157+
158+
function findItemByExactName(name) {
159+
return Caml_option.undefined_to_opt(allItems.find(function (item) {
160+
return item.name === name;
161+
}));
162+
}
163+
164+
function searchItems(value) {
165+
return Belt_Array.map(fuse.search(value), (function (m) {
166+
return m.item;
167+
}));
168+
}
169+
153170
function SyntaxLookup(Props) {
154171
var router = Next.Router.useRouter(undefined);
155172
var match = React.useState(function () {
@@ -160,76 +177,44 @@ function SyntaxLookup(Props) {
160177
React.useEffect((function () {
161178
var anchor = getAnchor(router.asPath);
162179
if (anchor !== undefined) {
163-
Belt_Option.forEach(Caml_option.undefined_to_opt(allItems.find(function (item) {
164-
return GithubSlugger.slug(item.id) === anchor;
165-
})), (function (item) {
166-
return Curry._1(setState, (function (param) {
167-
return {
168-
TAG: 1,
169-
_0: item,
170-
[Symbol.for("name")]: "ShowDetails"
171-
};
172-
}));
173-
}));
174-
}
175-
176-
}), []);
177-
React.useEffect((function () {
178-
var match = getAnchor(router.asPath);
179-
var exit = 0;
180-
if (typeof state === "number" || state.TAG === /* ShowFiltered */0) {
181-
exit = 1;
182-
} else {
183-
var item = state._0;
184-
if (match !== undefined) {
185-
var slug = GithubSlugger.slug(item.id);
186-
if (slug !== match) {
187-
Next.Router.replace(router, "syntax-lookup#" + match);
188-
}
189-
180+
var item = findItemById(anchor);
181+
if (item !== undefined) {
182+
Curry._1(setState, (function (param) {
183+
return {
184+
TAG: 1,
185+
_0: item,
186+
[Symbol.for("name")]: "ShowDetails"
187+
};
188+
}));
189+
window.scrollTo(0, 0);
190190
} else {
191-
Next.Router.replace(router, "syntax-lookup#" + GithubSlugger.slug(item.id));
192-
}
193-
}
194-
if (exit === 1) {
195-
if (match !== undefined) {
196-
Next.Router.replace(router, "syntax-lookup");
191+
Curry._1(setState, (function (param) {
192+
return /* ShowAll */0;
193+
}));
197194
}
198-
195+
} else {
196+
Curry._1(setState, (function (param) {
197+
return /* ShowAll */0;
198+
}));
199199
}
200200

201-
}), [state]);
201+
}), [router]);
202202
var onSearchValueChange = function (value) {
203+
if (value === "") {
204+
return Next.Router.push(router, "/syntax-lookup");
205+
}
206+
var item = findItemByExactName(value);
207+
if (item !== undefined) {
208+
return Next.Router.push(router, "/syntax-lookup#" + item.id);
209+
}
210+
var filtered = searchItems(value);
203211
return Curry._1(setState, (function (param) {
204-
if (value === "") {
205-
return /* ShowAll */0;
206-
}
207-
var filtered = Belt_Array.map(fuse.search(value), (function (m) {
208-
return m.item;
209-
}));
210-
if (filtered.length !== 1) {
211-
return {
212-
TAG: 0,
213-
_0: value,
214-
_1: filtered,
215-
[Symbol.for("name")]: "ShowFiltered"
216-
};
217-
}
218-
var item = Belt_Array.getExn(filtered, 0);
219-
if (item.name === value) {
220-
return {
221-
TAG: 1,
222-
_0: item,
223-
[Symbol.for("name")]: "ShowDetails"
224-
};
225-
} else {
226-
return {
227-
TAG: 0,
228-
_0: value,
229-
_1: filtered,
230-
[Symbol.for("name")]: "ShowFiltered"
231-
};
232-
}
212+
return {
213+
TAG: 0,
214+
_0: value,
215+
_1: filtered,
216+
[Symbol.for("name")]: "ShowFiltered"
217+
};
233218
}));
234219
};
235220
var details;
@@ -277,13 +262,7 @@ function SyntaxLookup(Props) {
277262
var children = Belt_Array.map(items, (function (item) {
278263
var onMouseDown = function (evt) {
279264
evt.preventDefault();
280-
return Curry._1(setState, (function (param) {
281-
return {
282-
TAG: 1,
283-
_0: item,
284-
[Symbol.for("name")]: "ShowDetails"
285-
};
286-
}));
265+
return onSearchValueChange(item.name);
287266
};
288267
return React.createElement("span", {
289268
key: item.name,
@@ -322,9 +301,7 @@ function SyntaxLookup(Props) {
322301
];
323302
}
324303
var onSearchClear = function (param) {
325-
return Curry._1(setState, (function (param) {
326-
return /* ShowAll */0;
327-
}));
304+
return onSearchValueChange("");
328305
};
329306
return React.createElement("div", undefined, React.createElement("div", {
330307
className: "flex flex-col items-center"

src/SyntaxLookup.res

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -155,74 +155,68 @@ type state =
155155
| ShowFiltered(string, array<item>) // (search, filteredItems)
156156
| ShowDetails(item)
157157

158+
@val @scope("window")
159+
external scrollTo: (int, int) => unit = "scrollTo"
160+
161+
let scrollToTop = () => scrollTo(0, 0)
162+
163+
let findItemById = id => allItems->Js.Array2.find(item => item.id === id)
164+
165+
let findItemByExactName = name => allItems->Js.Array2.find(item => item.name === name)
166+
167+
let searchItems = value =>
168+
fuse
169+
->Fuse.search(value)
170+
->Belt.Array.map(m => {
171+
m["item"]
172+
})
173+
158174
@react.component
159175
let make = () => {
160176
let router = Next.Router.useRouter()
161177
let (state, setState) = React.useState(_ => ShowAll)
162178

163-
React.useEffect0(() => {
179+
// This effect is responsible for updating the view state when the router anchor changes.
180+
// This effect is triggered when:
181+
// [A] The page first loads.
182+
// [B] The search box is cleared.
183+
// [C] The search box value exactly matches an item name.
184+
React.useEffect1(() => {
164185
switch getAnchor(router.asPath) {
186+
| None => setState(_ => ShowAll)
165187
| Some(anchor) =>
166-
Js.Array2.find(allItems, item =>
167-
GithubSlugger.slug(item.id) === anchor
168-
)->Belt.Option.forEach(item => {
169-
setState(_ => ShowDetails(item))
170-
})
171-
| None => ()
172-
}
173-
None
174-
})
175-
176-
/*
177-
This effect syncs the url anchor with the currently shown final match
178-
within the fuzzy finder. If there is a new match that doesn't align with
179-
the current anchor, the url will be rewritten with the correct anchor.
180-
181-
In case a selection got removed, it will also remove the anchor from the url.
182-
We don't replace on every state change, because replacing the url is expensive
183-
*/
184-
React.useEffect1(() => {
185-
switch (state, getAnchor(router.asPath)) {
186-
| (ShowDetails(item), Some(anchor)) =>
187-
let slug = GithubSlugger.slug(item.id)
188-
189-
if slug !== anchor {
190-
router->Next.Router.replace("syntax-lookup#" ++ anchor)
191-
} else {
192-
()
188+
switch findItemById(anchor) {
189+
| None => setState(_ => ShowAll)
190+
| Some(item) => {
191+
setState(_ => ShowDetails(item))
192+
scrollToTop()
193+
}
193194
}
194-
| (ShowDetails(item), None) =>
195-
router->Next.Router.replace("syntax-lookup#" ++ GithubSlugger.slug(item.id))
196-
| (_, Some(_)) => router->Next.Router.replace("syntax-lookup")
197-
| _ => ()
198195
}
199196
None
200-
}, [state])
201-
197+
}, [router])
198+
199+
// onSearchValueChange() is called when:
200+
// [A] The search value changes.
201+
// [B] The search is cleared.
202+
// [C] One of the tags is selected.
203+
//
204+
// We then handle three cases:
205+
// [1] Search is empty - trigger a route change, and allow the EFFECT to update the view state.
206+
// [2] Search exactly matches an item - trigger a route change, and allow the EFFECT to update the view state.
207+
// [3] Search does not match an item - immediately update the view state to show filtered items.
202208
let onSearchValueChange = value => {
203-
setState(_ =>
204-
switch value {
205-
| "" => ShowAll
206-
| search =>
207-
let filtered =
208-
fuse
209-
->Fuse.search(search)
210-
->Belt.Array.map(m => {
211-
m["item"]
212-
})
213-
214-
if Js.Array.length(filtered) === 1 {
215-
let item = Belt.Array.getExn(filtered, 0)
216-
if item.name === value {
217-
ShowDetails(item)
218-
} else {
219-
ShowFiltered(value, filtered)
220-
}
221-
} else {
222-
ShowFiltered(value, filtered)
209+
switch value {
210+
| "" => router->Next.Router.push("/syntax-lookup")
211+
| value =>
212+
switch findItemByExactName(value) {
213+
| None => {
214+
let filtered = searchItems(value)
215+
setState(_ => ShowFiltered(value, filtered))
223216
}
217+
| Some(item) => router->Next.Router.push("/syntax-lookup#" ++ item.id)
224218
}
225-
)
219+
}
226220
}
227221

228222
let details = switch state {
@@ -273,7 +267,7 @@ let make = () => {
273267
let children = Belt.Array.map(items, item => {
274268
let onMouseDown = evt => {
275269
ReactEvent.Mouse.preventDefault(evt)
276-
setState(_ => ShowDetails(item))
270+
onSearchValueChange(item.name)
277271
}
278272
<span className="mr-2 mb-2 cursor-pointer" onMouseDown key=item.name>
279273
<Tag text={item.name} />
@@ -296,7 +290,7 @@ let make = () => {
296290
}
297291

298292
let onSearchClear = () => {
299-
setState(_ => ShowAll)
293+
onSearchValueChange("")
300294
}
301295

302296
<div>

0 commit comments

Comments
 (0)