Skip to content

Commit dced0c6

Browse files
committed
Add simple search modal
1 parent de64432 commit dced0c6

13 files changed

Lines changed: 2373 additions & 3 deletions

File tree

‎makefile‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# https://stackoverflow.com/questions/2145590/what-is-the-purpose-of-phony-in-a-makefile
22
.PHONY: serve
33

4-
serve: clean generate-pages generate-maps
4+
serve: clean generate-pages generate-maps generate-search-index
55
zola serve
66

77
generate-pages: clean
@@ -10,13 +10,16 @@ generate-pages: clean
1010
generate-maps:
1111
bundle exec ruby scripts/generate-maps.rb
1212

13+
generate-search-index:
14+
bundle exec ruby scripts/generate-search-index.rb
15+
1316
debug: generate-pages
1417
TERA_DEBUG=true zola serve
1518

1619
clean:
1720
find content -name '*.md' ! \( -name '_index.md' -o -name 'smithy.md' -o -name 'calculator.md' \) -exec rm {} +
1821

19-
build: clean generate-pages generate-maps
22+
build: clean generate-pages generate-maps generate-search-index
2023
TERA_PRODUCTION=true zola build
2124

2225
deploy: build

‎sass/_components.scss‎

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,4 +1061,201 @@ table.min {
10611061
to {
10621062
opacity: 0.6;
10631063
}
1064-
}
1064+
}
1065+
1066+
.search-trigger-wrap {
1067+
margin-left: auto;
1068+
}
1069+
1070+
.search-trigger {
1071+
align-items: center;
1072+
background: none;
1073+
border: none;
1074+
color: rgba(255, 255, 255, 0.6);
1075+
cursor: pointer;
1076+
display: flex;
1077+
width: 52px;
1078+
height: 52px;
1079+
padding: 0;
1080+
1081+
&:hover {
1082+
color: white;
1083+
}
1084+
1085+
img {
1086+
height: 100%;
1087+
}
1088+
}
1089+
1090+
.search-modal {
1091+
display: none;
1092+
inset: 0;
1093+
position: fixed;
1094+
height: 100%;
1095+
z-index: 1000;
1096+
1097+
&.is-open {
1098+
align-items: flex-start;
1099+
display: flex;
1100+
justify-content: center;
1101+
padding-top: 8vh;
1102+
1103+
@media screen and (max-width: 600px) {
1104+
align-items: stretch;
1105+
padding-top: 0;
1106+
}
1107+
}
1108+
1109+
&__backdrop {
1110+
background: rgba(0, 0, 0, 0.7);
1111+
inset: 0;
1112+
position: absolute;
1113+
}
1114+
1115+
&__panel {
1116+
// Override .card defaults that don't suit a modal
1117+
flex: 0 1 62rem;
1118+
margin: 0 1.4rem;
1119+
overflow: visible;
1120+
height: 80%;
1121+
position: relative;
1122+
z-index: 1;
1123+
1124+
@media screen and (max-width: 600px) {
1125+
flex: 1 1 auto;
1126+
margin: 0;
1127+
}
1128+
}
1129+
1130+
&__header {
1131+
align-items: center;
1132+
display: flex;
1133+
gap: 1rem;
1134+
padding: 1.3rem 0 0;
1135+
1136+
svg {
1137+
flex-shrink: 0;
1138+
opacity: 0.6;
1139+
}
1140+
}
1141+
1142+
&__input {
1143+
background: transparent;
1144+
border: none;
1145+
color: white;
1146+
flex: 1;
1147+
font-family: inherit;
1148+
font-size: 2.2rem;
1149+
padding: 0 1.6rem;
1150+
min-width: 0;
1151+
outline: none;
1152+
1153+
&::placeholder {
1154+
color: rgba(255, 255, 255, 0.35);
1155+
}
1156+
}
1157+
1158+
&__close {
1159+
background: none;
1160+
border: 1px solid #6e905f;
1161+
color: rgba(255, 255, 255, 0.7);
1162+
cursor: pointer;
1163+
flex-shrink: 0;
1164+
font-family: inherit;
1165+
font-size: 1.6rem;
1166+
line-height: 1;
1167+
padding: 0.4rem 0.7rem;
1168+
1169+
&:hover {
1170+
color: white;
1171+
}
1172+
}
1173+
1174+
&__results {
1175+
height: 100%;
1176+
padding: 0 1.6rem;
1177+
overflow-y: auto;
1178+
1179+
@media screen and (max-width: 600px) {
1180+
max-height: calc(100dvh - 9rem);
1181+
}
1182+
}
1183+
1184+
&__footer {
1185+
border-top: 2px solid rgba(110, 144, 95, 0.4);
1186+
color: rgba(255, 255, 255, 0.4);
1187+
display: flex;
1188+
font-size: 1.3rem;
1189+
gap: 1.6rem;
1190+
padding: 0.8rem 1.6rem;
1191+
1192+
@media screen and (max-width: 600px) {
1193+
display: none;
1194+
}
1195+
}
1196+
1197+
hr {
1198+
margin: 1.3rem 0 0;
1199+
}
1200+
}
1201+
1202+
.search-result {
1203+
position: relative;
1204+
align-items: center;
1205+
color: white;
1206+
display: flex;
1207+
gap: 1.2rem;
1208+
text-decoration: none;
1209+
1210+
&::before {
1211+
background: transparent;
1212+
content: '';
1213+
display: block;
1214+
height: 100%;
1215+
left: calc(100% - 50vw + 2.3rem);
1216+
position: absolute;
1217+
top: 0;
1218+
width: calc(50vw - 2.3rem);
1219+
z-index: -1;
1220+
1221+
@media screen and (max-width: 600px) {
1222+
left: 0;
1223+
width: 100%;
1224+
}
1225+
}
1226+
1227+
&:focus::before,
1228+
&:hover::before,
1229+
&--selected {
1230+
animation: opacity-alternate 1s ease-in-out 0s infinite alternate;
1231+
background: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0)), color-stop(rgba(3, 119, 3, 0.84)), to(rgba(0, 0, 0, 0)));
1232+
background: -o-linear-gradient(left, rgba(0, 0, 0, 0), rgba(3, 119, 3, 0.84), rgba(0, 0, 0, 0));
1233+
background: linear-gradient(0.25turn, rgba(0, 0, 0, 0), rgba(3, 119, 3, 0.84), rgba(0, 0, 0, 0));
1234+
}
1235+
1236+
&__text {
1237+
flex: 1;
1238+
min-width: 0;
1239+
}
1240+
1241+
&__name {
1242+
margin: 0;
1243+
overflow: hidden;
1244+
text-overflow: ellipsis;
1245+
white-space: nowrap;
1246+
}
1247+
1248+
&__meta {
1249+
color: rgba(255, 255, 255, 0.5);
1250+
font-size: 1.4rem;
1251+
margin: 0;
1252+
text-transform: capitalize;
1253+
}
1254+
}
1255+
1256+
.search-results__empty {
1257+
color: rgba(255, 255, 255, 0.4);
1258+
font-size: 1.8rem;
1259+
padding: 2rem 1.6rem;
1260+
text-align: center;
1261+
}

‎scripts/generate-search-index.rb‎

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
require 'json'
2+
require_relative 'lib/slug'
3+
4+
entries = []
5+
6+
# Weapons
7+
Dir.glob("content/blacksmith/**/*-crafting.json").each do |path|
8+
data = JSON.load(File.read(path))
9+
url_prefix = "/" + File.dirname(path).delete_prefix("content/") + "/"
10+
11+
data["weapons"].each do |w|
12+
next if w.key?("donotrender") || w["name"].nil?
13+
14+
slug = slugify(w["name"])
15+
rank = if w["hr"]
16+
if w["hr"].to_i <= 5
17+
"low-rank"
18+
elsif w["hr"].to_i <= 8
19+
"high-rank"
20+
elsif w["elder"]
21+
"g-rank"
22+
end
23+
else
24+
case w["rarity"].to_i
25+
when 1..4 then "low-rank"
26+
when 5..7 then "high-rank"
27+
else "g-rank"
28+
end
29+
end
30+
31+
entries << {
32+
name: w["name"],
33+
slug: slug,
34+
url: "#{url_prefix}#{slug}/",
35+
category: "weapon",
36+
type: w["type"] || File.basename(File.dirname(path)),
37+
rank: rank,
38+
rarity: w["rarity"].to_i,
39+
elements: (w["elements"] || []).map { |e| e["name"] }.join(" "),
40+
skills: ""
41+
}
42+
end
43+
end
44+
45+
# Armor
46+
Dir.glob("content/armorsmith/**/*-crafting.json").each do |path|
47+
data = JSON.load(File.read(path))
48+
url_prefix = "/" + File.dirname(path).delete_prefix("content/") + "/"
49+
50+
rank = if path.include?("g-rank")
51+
"g-rank"
52+
elsif path.include?("high-rank")
53+
"high-rank"
54+
else
55+
"low-rank"
56+
end
57+
58+
data["weapons"].each do |a|
59+
next if a.key?("donotrender") || a["name"].nil?
60+
61+
slug = slugify(a["name"])
62+
skill_names = (a["skills"] || []).map { |s| s.is_a?(Hash) ? s["name"] : s }.join(" ")
63+
64+
entries << {
65+
name: a["name"],
66+
slug: slug,
67+
url: "#{url_prefix}#{slug}/",
68+
category: "armor",
69+
type: a["type"],
70+
rank: rank,
71+
rarity: a["rarity"].to_i,
72+
elements: "",
73+
skills: skill_names
74+
}
75+
end
76+
end
77+
78+
# Decorations
79+
Dir.glob("content/decorations/*-crafting.json").each do |path|
80+
data = JSON.load(File.read(path))
81+
url_prefix = "/" + File.dirname(path).delete_prefix("content/") + "/"
82+
83+
data["weapons"].each do |d|
84+
next if d.key?("donotrender") || d["name"].nil?
85+
86+
slug = slugify(d["name"])
87+
skill_names = (d["skills"] || []).join(" ")
88+
89+
entries << {
90+
name: d["name"],
91+
slug: slug,
92+
url: "#{url_prefix}#{slug}/",
93+
category: "decoration",
94+
type: "decoration",
95+
rank: nil,
96+
rarity: d["rarity"].to_i,
97+
elements: "",
98+
skills: skill_names
99+
}
100+
end
101+
end
102+
103+
# Monsters
104+
Dir.glob("content/monsters/**/*.json").each do |path|
105+
data = JSON.load(File.read(path))
106+
url_prefix = "/" + File.dirname(path).delete_prefix("content/") + "/"
107+
108+
(data["monsters"] || []).each do |m|
109+
next unless m["name"]
110+
111+
slug = slugify(m["name"])
112+
113+
entries << {
114+
name: m["name"],
115+
slug: slug,
116+
url: "#{url_prefix}#{slug}/",
117+
category: "monster",
118+
type: m["type"] || "",
119+
rank: nil,
120+
rarity: 0,
121+
elements: "",
122+
skills: ""
123+
}
124+
end
125+
end
126+
127+
File.write("static/search-data.json", JSON.generate(entries))
128+
puts "Generated #{entries.length} search entries"

‎static/images/binoculars-mini.png‎

628 Bytes
Loading

‎static/images/binoculars.png‎

960 Bytes
Loading

0 commit comments

Comments
 (0)