mirror of
https://github.com/zyachel/quetre.git
synced 2025-04-01 20:17:36 +03:00
fix(search): remove broken search route
can't do searches now unless logged-in BREAKING CHANGE: any search request will be responded with a 410
This commit is contained in:
parent
a0ac36a174
commit
f49062d44a
13 changed files with 36 additions and 489 deletions
|
@ -187,7 +187,7 @@ From [their privacy policy](https://www.quora.com/about/privacy)
|
|||
|
||||
## To-Do
|
||||
|
||||
- [x] add missing routes like topics, profile, and search
|
||||
- [x] add missing routes like topics and profile
|
||||
- [x] use redis
|
||||
- [x] serve images and other assets from Quetre
|
||||
- [x] implement a better installation method
|
||||
|
|
|
@ -7,9 +7,8 @@ import catchAsyncErrors from '../utils/catchAsyncErrors.js';
|
|||
import getAnswers from '../fetchers/getAnswers.js';
|
||||
import getTopic from '../fetchers/getTopic.js';
|
||||
import getProfile from '../fetchers/getProfile.js';
|
||||
import getSearch from '../fetchers/getSearch.js';
|
||||
import getOrSetCache from '../utils/getOrSetCache.js';
|
||||
import { answersKey, profileKey, searchKey, topicKey } from '../utils/cacheKeys.js';
|
||||
import { answersKey, profileKey, topicKey } from '../utils/cacheKeys.js';
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// EXPORTS
|
||||
|
@ -18,7 +17,7 @@ export const about = (req, res, next) => {
|
|||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: `make a request.
|
||||
available endpoints are: '/slug', '/unanswered/slug', '/topic/slug', '/profile/slug', '/search?q=query', /?q=query.`,
|
||||
available endpoints are: '/slug', '/unanswered/slug', '/topic/slug', '/profile/slug'`,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -55,21 +54,6 @@ export const profile = catchAsyncErrors(async (req, res, next) => {
|
|||
res.status(200).json({ status: 'success', data });
|
||||
});
|
||||
|
||||
export const search = catchAsyncErrors(async (req, res, next) => {
|
||||
const {
|
||||
urlObj,
|
||||
query: { lang },
|
||||
} = req;
|
||||
|
||||
const searchText = urlObj.searchParams.get('q')?.trim(); // no search to perform if there isn't any query
|
||||
let searchData = null;
|
||||
|
||||
if (searchText)
|
||||
searchData = await getOrSetCache(searchKey(urlObj), getSearch, urlObj.search, lang);
|
||||
|
||||
res.status(200).json({ status: 'success', data: {searchData, searchText} });
|
||||
});
|
||||
|
||||
export const unimplemented = (req, res, next) => {
|
||||
res.status(501).json({
|
||||
status: 'fail',
|
||||
|
@ -77,6 +61,14 @@ export const unimplemented = (req, res, next) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const gone = (req, res, next) => {
|
||||
res.status(501).json({
|
||||
status: 'fail',
|
||||
message: "This route doesn't exist anymore.",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const image = catchAsyncErrors(async (req, res, next) => {
|
||||
const { domain, path } = req.params;
|
||||
if (!domain.endsWith('quoracdn.net')) {
|
||||
|
|
|
@ -7,9 +7,8 @@ import getAnswers from '../fetchers/getAnswers.js';
|
|||
import getTopic from '../fetchers/getTopic.js';
|
||||
import { acceptedLanguages, nonSlugRoutes } from '../utils/constants.js';
|
||||
import getProfile from '../fetchers/getProfile.js';
|
||||
import getSearch from '../fetchers/getSearch.js';
|
||||
import getOrSetCache from '../utils/getOrSetCache.js';
|
||||
import { answersKey, profileKey, searchKey, topicKey } from '../utils/cacheKeys.js';
|
||||
import { answersKey, profileKey, topicKey } from '../utils/cacheKeys.js';
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// EXPORTS
|
||||
|
@ -101,28 +100,6 @@ export const profile = catchAsyncErrors(async (req, res, next) => {
|
|||
});
|
||||
});
|
||||
|
||||
export const search = catchAsyncErrors(async (req, res, next) => {
|
||||
const {
|
||||
urlObj,
|
||||
query: { lang },
|
||||
} = req;
|
||||
const searchText = urlObj.searchParams.get('q')?.trim();
|
||||
let searchData = null;
|
||||
|
||||
if (searchText)
|
||||
searchData = await getOrSetCache(searchKey(urlObj), getSearch, urlObj.search, lang);
|
||||
|
||||
res.status(200).render('search', {
|
||||
data: { searchData, searchText },
|
||||
meta: {
|
||||
title: searchText ? `Results for '${searchText}'` : 'Search',
|
||||
url: urlObj,
|
||||
imageUrl: `${urlObj.origin}/icon.svg`,
|
||||
description: searchText ? `results for '${searchText}'` : 'search page',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const regex = /^https:\/\/(.{2,})\.quora\.com(\/.*)$/; // local helper constant
|
||||
export const redirect = (req, res, next) => {
|
||||
const url = req.originalUrl.replace('/redirect/', ''); // removing `/redirect/` part.
|
||||
|
@ -158,3 +135,20 @@ export const unimplemented = (req, res, next) => {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const gone = (req, res, next) => {
|
||||
const data = {
|
||||
message: "This route doesn't exist anymore.",
|
||||
statusCode: 410,
|
||||
};
|
||||
|
||||
res.status(data.statusCode).render('error', {
|
||||
data,
|
||||
meta: {
|
||||
title: 'Gone',
|
||||
url: req.urlObj,
|
||||
imageUrl: `${req.urlObj.origin}/icon.svg`,
|
||||
description: data.message,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,168 +0,0 @@
|
|||
////////////////////////////////////////////////////////
|
||||
// IMPORTS
|
||||
////////////////////////////////////////////////////////
|
||||
import AppError from '../utils/AppError.js';
|
||||
import fetcher from './fetcher.js';
|
||||
import { quetrefy } from '../utils/urlModifiers.js';
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// HELPER FUNCTIONS
|
||||
////////////////////////////////////////////////////////
|
||||
const topicCleaner = topic => ({
|
||||
type: 'topic',
|
||||
url: quetrefy(topic.url),
|
||||
name: topic.name,
|
||||
numFollowers: topic.numFollowers,
|
||||
image: topic.photoUrl,
|
||||
isSensitive: topic.isSensitive,
|
||||
});
|
||||
const spaceCleaner = space => ({
|
||||
type: 'space',
|
||||
numUsers: space.tribeUserCount,
|
||||
url: quetrefy(space.url),
|
||||
name: space.nameString,
|
||||
description: space.descriptionString,
|
||||
image: space.iconRetinaUrl,
|
||||
isSensitive: space.isSensitive,
|
||||
});
|
||||
const profileCleaner = profile => ({
|
||||
type: 'profile',
|
||||
credential: profile.bestCredential?.translatedString,
|
||||
isAnon: profile.isAnon,
|
||||
name: `${profile.names[0]?.givenName} ${profile.names[0]?.familyName}`,
|
||||
url: quetrefy(profile.profileUrl),
|
||||
image: profile.profileImageUrl,
|
||||
numFollowers: profile.followerCount,
|
||||
isVerified: profile.isVerified,
|
||||
isBusiness: profile.businessStatus,
|
||||
isPlusUser: profile.consumerBundleActive,
|
||||
});
|
||||
const questionCleaner = question => ({
|
||||
type: 'question',
|
||||
text: JSON.parse(question.title).sections,
|
||||
url: quetrefy(question.url),
|
||||
isDeleted: question.isDeleted,
|
||||
numFollowers: question.followerCount,
|
||||
creationTime: question.creationTime,
|
||||
numComments: question.numDisplayComments,
|
||||
isSensitive: question.isSensitive,
|
||||
});
|
||||
const answerCleaner = ({ question, previewAnswer: answer }) => ({
|
||||
type: 'answer',
|
||||
question: {
|
||||
...questionCleaner(question),
|
||||
},
|
||||
...(answer.originalQuestionIfDifferent && {
|
||||
originalQuestion: {
|
||||
text: JSON.parse(answer.originalQuestionIfDifferent.question.title).sections,
|
||||
url: quetrefy(answer.originalQuestionIfDifferent.question.url),
|
||||
qid: answer.originalQuestionIfDifferent.question.qid,
|
||||
},
|
||||
}),
|
||||
isViewable: !!answer.viewerHasAccess,
|
||||
text: JSON.parse(answer.content).sections,
|
||||
creationTime: answer.creationTime,
|
||||
updatedTime: answer.updatedTime,
|
||||
numComments: answer.numDisplayComments,
|
||||
numUpvotes: answer.numUpvotes,
|
||||
numViews: answer.numViews,
|
||||
numShares: answer.numShares,
|
||||
numAnswerRequests: answer.numRequesters,
|
||||
isBusinessAnswer: answer.businessAnswer,
|
||||
url: quetrefy(answer.url),
|
||||
isSensitive: answer.isSensitive,
|
||||
author: {
|
||||
uid: answer.author.uid,
|
||||
isAnon: answer.author.isAnon,
|
||||
image: answer.author.profileImageUrl,
|
||||
isVerified: answer.author.isVerified,
|
||||
isPlusUser: answer.author.consumerBundleActive,
|
||||
url: quetrefy(answer.author.profileUrl),
|
||||
name: `${answer.author.names[0].givenName} ${answer.author.names[0].familyName}`,
|
||||
credential: answer.authorCredential?.translatedString,
|
||||
},
|
||||
});
|
||||
const postCleaner = post => ({
|
||||
type: 'post',
|
||||
pid: post.pid,
|
||||
isViewable: post.viewerHasAccess,
|
||||
url: quetrefy(post.url),
|
||||
title: JSON.parse(post.title).sections,
|
||||
isDeleted: post.isDeleted,
|
||||
isSensitive: post.isSensitive,
|
||||
text: JSON.parse(post.content).sections,
|
||||
creationTime: post.creationTime,
|
||||
updatedTime: post.updatedTime,
|
||||
numComments: post.numDisplayComments,
|
||||
numUpvotes: post.numUpvotes,
|
||||
numViews: post.numViews,
|
||||
numShares: post.numShares,
|
||||
author: {
|
||||
uid: post.author.uid,
|
||||
isAnon: post.author.isAnon,
|
||||
image: post.author.profileImageUrl,
|
||||
isVerified: post.author.isVerified,
|
||||
isPlusUser: post.author.consumerBundleActive,
|
||||
url: quetrefy(post.author.profileUrl),
|
||||
name: `${post.author.names[0].givenName} ${post.author.names[0].familyName}`,
|
||||
credential: post.authorCredential?.translatedString,
|
||||
},
|
||||
...(post.tribeItem && {
|
||||
space: {
|
||||
isSensitive: post.tribeItem.tribe.isSensitive,
|
||||
name: post.tribeItem.tribe.nameString,
|
||||
url: quetrefy(post.tribeItem.tribe.url),
|
||||
image: post.tribeItem.tribe.iconRetinaUrl,
|
||||
description: post.tribeItem.descriptionString,
|
||||
numFollowers: post.tribeItem.tribe.numFollowers,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const resultsCleaner = results => {
|
||||
const cleanedResults = results.map(result => {
|
||||
const resultToClean = result.node;
|
||||
|
||||
if (resultToClean.topic) return topicCleaner(resultToClean.topic);
|
||||
if (resultToClean.tribe) return spaceCleaner(resultToClean.tribe);
|
||||
if (resultToClean.post) return postCleaner(resultToClean.post);
|
||||
if (resultToClean.user) return profileCleaner(resultToClean.user);
|
||||
if (resultToClean.previewAnswer) return answerCleaner(resultToClean);
|
||||
if (resultToClean.question) return questionCleaner(resultToClean.question);
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
return cleanedResults;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// FUNCTION
|
||||
////////////////////////////////////////////////////////
|
||||
const KEYWORD = 'searchConnection';
|
||||
|
||||
const getSearch = async (querySlug, lang) => {
|
||||
const options = { keyword: KEYWORD, lang, toEncode: false };
|
||||
const res = await fetcher(`search/${querySlug}`, options);
|
||||
|
||||
const {
|
||||
data: { [KEYWORD]: rawData },
|
||||
} = JSON.parse(res);
|
||||
|
||||
if (!rawData)
|
||||
throw new AppError(
|
||||
"Search couldn't be done. Recheck the URL, or resend the request if you believe the URL is correct.",
|
||||
404
|
||||
);
|
||||
|
||||
const data = {
|
||||
results: resultsCleaner(rawData.edges),
|
||||
};
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// EXPORTS
|
||||
////////////////////////////////////////////////////////
|
||||
export default getSearch;
|
|
@ -6,13 +6,13 @@ import {
|
|||
topic,
|
||||
image,
|
||||
profile,
|
||||
search,
|
||||
gone,
|
||||
} from '../controllers/apiController.js';
|
||||
|
||||
const apiRouter = express.Router();
|
||||
|
||||
apiRouter.get('/(|search)', search);
|
||||
apiRouter.get('/about', about);
|
||||
apiRouter.get('/search', gone);
|
||||
apiRouter.get('/(|about)', about);
|
||||
apiRouter.get('/image/:domain/:path', image);
|
||||
apiRouter.get('/profile/:name', profile);
|
||||
apiRouter.get('/topic/:slug', topic);
|
||||
|
|
|
@ -6,14 +6,14 @@ import {
|
|||
topic,
|
||||
unimplemented,
|
||||
profile,
|
||||
search,
|
||||
gone,
|
||||
redirect,
|
||||
} from '../controllers/viewController.js';
|
||||
|
||||
const viewRouter = express.Router();
|
||||
|
||||
viewRouter.get('/(|search)', search); // search on / or /search
|
||||
viewRouter.get('/about', about);
|
||||
viewRouter.get('/search', gone);
|
||||
viewRouter.get('/(|about)', about);
|
||||
viewRouter.get('/privacy', privacy);
|
||||
viewRouter.get('/profile/:name', profile);
|
||||
viewRouter.get('/topic/:slug', topic);
|
||||
|
|
|
@ -8,11 +8,6 @@ const formatSlug = (slug, charToRemove) =>
|
|||
////////////////////////////////////////////////////////
|
||||
// EXPORTS
|
||||
////////////////////////////////////////////////////////
|
||||
export const searchKey = urlObj => {
|
||||
const slug = formatSlug(urlObj.search, '?');
|
||||
return `search:${slug}`;
|
||||
};
|
||||
|
||||
export const answersKey = urlObj => {
|
||||
const slug = formatSlug(urlObj.pathname, '/');
|
||||
const lang = getLang(urlObj);
|
||||
|
|
|
@ -11,7 +11,6 @@ footer.footer(class=`${meta.title ==='About' ? 'footer__about' : ''}`)
|
|||
ul.footer__nav
|
||||
- if (meta.title !=='About')
|
||||
li.footer__nav-item: a.footer__nav-link.footer__link(href="/about") About
|
||||
li.footer__nav-item: a.footer__nav-link.footer__link(href="/search") Search
|
||||
li.footer__nav-item: a.footer__nav-link.footer__link(href="https://github.com/zyachel/quetre") Source Code
|
||||
li.footer__nav-item: a.footer__nav-link.footer__link(href="/privacy") Privacy
|
||||
li.footer__nav-item: a.footer__nav-link.footer__link(href="#") Back to top
|
||||
|
|
|
@ -10,8 +10,6 @@ header.header(class=`${meta.title === 'About' ? 'header__about': ''}`)
|
|||
|
||||
//- BUTTON FOR CHANGING THEME
|
||||
.header__misc
|
||||
a.link.header__search(href="/search", aria-label='search page')
|
||||
svg.icon: use(href='/misc/sprite.svg#icon-search')
|
||||
button.button.theme-changer.header__theme(aria-label='Change Theme')
|
||||
svg.icon.icon__theme.theme-changer__icon.theme-changer__icon--sun: use(href='/misc/sprite.svg#icon-sun')
|
||||
svg.icon.icon__theme.theme-changer__icon.theme-changer__icon--moon: use(href='/misc/sprite.svg#icon-moon')
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
//-//////////////////////////////////////////////////////
|
||||
//- INCLUDES/EXTENDS
|
||||
//-//////////////////////////////////////////////////////
|
||||
extends ../base
|
||||
include ../mixins/_formatText
|
||||
include ../mixins/_utils
|
||||
include ../mixins/_answer
|
||||
include ../mixins/_question
|
||||
include ../mixins/_post
|
||||
include ../mixins/_metadata
|
||||
|
||||
|
||||
//-//////////////////////////////////////////////////////
|
||||
//- MAIN CONTENT
|
||||
//-//////////////////////////////////////////////////////
|
||||
block content
|
||||
main#main(class=`main search ${data.searchData ? '' :'search--no-results'}`)
|
||||
-
|
||||
const typesArr = [{key: 'question', text: 'Questions'}, {key: 'answer', text: 'Answers'}, {key: 'post', text: 'Posts'}, {key: 'profile', text: 'Profiles'}, {key: 'topic', text: 'Topics'}, {key: 'tribe', text: 'Spaces'}]
|
||||
const timesArr = [{key: 'hour', text: 'Hour'}, {key: 'day', text: 'Day'}, {key: 'week', text: 'Week'}, {key: 'month', text: 'Month'}, {key: 'year', text: 'Year'}]
|
||||
const languagesArr = ['en','es','fr','de','it','jp','id','pt','hi','nl','da','fi','nb','sv','mr','bn','ta','ar','he','gu','kn','ml','te','po'];
|
||||
|
||||
form.search__form.search-form(action="/search", method='get', autocomplete='off', name='search')
|
||||
.search-form__search-container
|
||||
input.search-form__searchbar(type="search", name="q", placeholder='Enter your query...', minlength='3', aria-label='search for anything')
|
||||
button.search-form__button.search-form__button--reset(type="reset", aria-label='clear searchbar and filters'): svg.icon: use(href='/misc/sprite.svg#icon-cross')
|
||||
button.search-form__button.search-form__button--submit(type="submit", aria-label='search'): svg.icon: use(href='/misc/sprite.svg#icon-search')
|
||||
.search-form__filters-container.search-form__filters-container--type
|
||||
p.search-form__filters-heading Filter by Type
|
||||
each item in typesArr
|
||||
.search-form__filters-group
|
||||
input.search-form__radio.visually-hidden(type="radio", name="type", value=item.key, id=`type--${item.key}`)
|
||||
label.search-form__label(for=`type--${item.key}`)= item.text
|
||||
|
||||
.search-form__filters-container.search-form__filters-container--time
|
||||
p.search-form__filters-heading Filter by Time
|
||||
each item in timesArr
|
||||
.search-form__filters-group
|
||||
input.search-form__radio.visually-hidden(type="radio", name='time', value=item.key, id=`time--${item.key}`)
|
||||
label.search-form__label(for=`time--${item.key}`)= item.text
|
||||
|
||||
.search-form__filters-container.search-form__filters-container--lang
|
||||
p.search-form__filters-heading Filter by Language
|
||||
each lang in languagesArr
|
||||
.search-form__filters-group
|
||||
input.search-form__radio.visually-hidden(type="radio", name='lang', value=lang, id=`lang--${lang}`)
|
||||
label.search-form__label(for=`lang--${lang}`)= lang.toUpperCase()
|
||||
|
||||
//- TODO: refactor 'profile', 'topic', and 'space' into resusable mixins.
|
||||
- if (data.searchData?.results)
|
||||
section.search__results.search-results
|
||||
h1.heading.heading__primary= `Results for '${data.searchText}'`
|
||||
- if (data.searchData.results.length === 0)
|
||||
p No results found for the query. Try being less specific and/or removing filters.
|
||||
- else
|
||||
.search-results__container
|
||||
each item in data.searchData.results
|
||||
.search-results__item
|
||||
- if (item.type === 'answer')
|
||||
+addAnswer(item, true)
|
||||
- else if (item.type === 'question')
|
||||
+addQuestion(item)
|
||||
- else if (item.type === 'post')
|
||||
+addPost(item)
|
||||
- else if (item.type === 'profile')
|
||||
.metadata-primary
|
||||
p.metadata-primary__heading
|
||||
if item.isAnon
|
||||
span Anonymous
|
||||
else
|
||||
a.link.metadata-primary__link(href=item.url)= item.name
|
||||
if item.isVerified
|
||||
svg.icon.metadata-primary__icon
|
||||
title verified
|
||||
use(href='/misc/sprite.svg#icon-verified')
|
||||
+proxifyImg(item.image)(class='metadata-primary__image', alt='', aria-hidden='true')
|
||||
p.metadata-primary__misc(aria-label=`${item.name}'s credentials`)= item.credential || ''
|
||||
- else if (item.type === 'topic')
|
||||
.metadata-primary
|
||||
a.link.metadata-primary__heading(href=item.url)= item.name
|
||||
+proxifyImg(item.image)(class='metadata-primary__image', aria-hidden='true', alt='')
|
||||
p.metadata-primary__misc
|
||||
+formatNumber(item.numFollowers)
|
||||
| Followers
|
||||
- else if (item.type === 'space')
|
||||
.metadata-primary.profile-spaces__list-item
|
||||
+proxifyImg(item.image)(class='metadata-primary__image', alt='', aria-hidden='true')
|
||||
a.link.metadata-primary__heading(href=item.url)= item.name
|
||||
p.metadata-primary__misc= item.description
|
|
@ -536,119 +536,3 @@
|
|||
gap: var(--space-800);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// SEARCH PAGE COMPONENTS
|
||||
////////////////////////////////////////////////////////
|
||||
///
|
||||
.search-form {
|
||||
display: grid;
|
||||
gap: var(--space-200);
|
||||
// justify-items: center;
|
||||
|
||||
&__search-container {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: minmax(15rem, auto) 3rem 3rem;
|
||||
grid-auto-rows: 3rem;
|
||||
border: 1px solid var(--clr-base-icon-alt-alpha);
|
||||
border-radius: 100vmax;
|
||||
overflow: hidden;
|
||||
padding: var(--space-100);
|
||||
gap: var(--space-050);
|
||||
|
||||
&:focus-within {
|
||||
background-color: var(--clr-code-bg);
|
||||
color: var(--clr-code-text);
|
||||
}
|
||||
}
|
||||
|
||||
&__searchbar {
|
||||
outline: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
font: inherit;
|
||||
caret-color: var(--clr-base-icon);
|
||||
|
||||
// fix for browsers with non-standard properties. yes, webkit and blink suck.
|
||||
-webkit-appearance: none;
|
||||
&::-webkit-search-cancel-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
border-radius: 100vmax;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
&--reset {
|
||||
}
|
||||
|
||||
&--submit {
|
||||
}
|
||||
}
|
||||
|
||||
&__filters-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem 1rem;
|
||||
}
|
||||
|
||||
&__filters-heading {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
&__filters-group {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__radio {
|
||||
}
|
||||
|
||||
&__label {
|
||||
cursor: pointer;
|
||||
background: var(--clr-code-bg);
|
||||
color: var(--clr-code-text);
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
&__radio:checked + &__label {
|
||||
color: var(--clr-selection-text);
|
||||
background: var(--clr-selection-bg);
|
||||
}
|
||||
|
||||
&__radio:focus + &__label {
|
||||
@include focus-rules;
|
||||
}
|
||||
|
||||
@supports selector(:focus-visible) {
|
||||
&__radio:focus + &__label {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&__radio:focus-visible + &__label {
|
||||
@include focus-rules;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-results {
|
||||
--img-dim: var(--fs-600);
|
||||
|
||||
display: grid;
|
||||
gap: var(--space-500);
|
||||
|
||||
&__container {
|
||||
display: grid;
|
||||
gap: var(--space-500);
|
||||
}
|
||||
|
||||
&__item {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,8 +81,7 @@
|
|||
gap: var(--space-200);
|
||||
}
|
||||
|
||||
&__theme,
|
||||
&__search {
|
||||
&__theme {
|
||||
height: var(--fs-300);
|
||||
width: var(--fs-300);
|
||||
|
||||
|
@ -92,12 +91,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// search icon viewBox is larger than content, hence the fix
|
||||
&__search > svg {
|
||||
height: 105%;
|
||||
width: 105%;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
|
|
@ -405,54 +405,3 @@
|
|||
padding-inline: var(--space-200);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// SEARCH
|
||||
////////////////////////////////////////////////////////
|
||||
.search {
|
||||
// justify-self: center;
|
||||
padding: var(--space-500) var(--space-800);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1.2fr;
|
||||
gap: var(--space-500);
|
||||
align-items: start;
|
||||
|
||||
&--no-results {
|
||||
grid-template-columns: unset;
|
||||
place-content: center;
|
||||
max-width: 100rem;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
&__results {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 2;
|
||||
}
|
||||
|
||||
&__form {
|
||||
grid-row: 1 / 2;
|
||||
|
||||
grid-column: -2 / -1;
|
||||
}
|
||||
|
||||
@include respond-to(bp-1200) {
|
||||
padding: var(--space-500);
|
||||
gap: var(--space-500);
|
||||
}
|
||||
@include respond-to(bp-900) {
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: max-content auto;
|
||||
|
||||
&__results {
|
||||
grid-row: 2 / 3;
|
||||
}
|
||||
|
||||
&__form {
|
||||
grid-row: 1 / 2;
|
||||
}
|
||||
}
|
||||
@include respond-to(bp-550) {
|
||||
padding-inline: var(--space-200);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue