mirror of
https://github.com/facebook/docusaurus.git
synced 2025-08-06 02:08:55 +02:00
refactor(website): various fixes and improvements on Showcase page (#5997)
* refactor(website): add various fixes and improvements on Showcase page * Maintain previous focused element (WIP) * Fix SSR * Fix again * Final fix Co-authored-by: Josh-Cena <sidachen2003@gmail.com>
This commit is contained in:
parent
8359ff36cd
commit
d25bf24753
10 changed files with 88 additions and 101 deletions
|
@ -16,17 +16,9 @@ import Tooltip from '../ShowcaseTooltip';
|
||||||
import {Tags, TagList, TagType, User, Tag} from '@site/src/data/users';
|
import {Tags, TagList, TagType, User, Tag} from '@site/src/data/users';
|
||||||
import {sortBy} from '@site/src/utils/jsUtils';
|
import {sortBy} from '@site/src/utils/jsUtils';
|
||||||
|
|
||||||
interface Props extends Tag {
|
const TagComp = React.forwardRef<HTMLLIElement, Tag>(
|
||||||
id: string;
|
({label, color, description}, ref) => (
|
||||||
}
|
<li ref={ref} className={styles.tag} title={description}>
|
||||||
|
|
||||||
const TagComp = React.forwardRef<HTMLLIElement, Props>(
|
|
||||||
({id, label, color, description}, ref) => (
|
|
||||||
<li
|
|
||||||
ref={ref}
|
|
||||||
aria-describedby={id}
|
|
||||||
className={styles.tag}
|
|
||||||
title={description}>
|
|
||||||
<span className={styles.textLabel}>{label.toLowerCase()}</span>
|
<span className={styles.textLabel}>{label.toLowerCase()}</span>
|
||||||
<span className={styles.colorLabel} style={{backgroundColor: color}} />
|
<span className={styles.colorLabel} style={{backgroundColor: color}} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -52,7 +44,7 @@ function ShowcaseCardTag({tags}: {tags: TagType[]}) {
|
||||||
text={tagObject.description}
|
text={tagObject.description}
|
||||||
anchorEl="#__docusaurus"
|
anchorEl="#__docusaurus"
|
||||||
id={id}>
|
id={id}>
|
||||||
<TagComp id={id} key={index} {...tagObject} />
|
<TagComp key={index} {...tagObject} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -68,10 +60,7 @@ const ShowcaseCard = memo(({user}: {user: User}) => (
|
||||||
<div className="card__body">
|
<div className="card__body">
|
||||||
<div className={clsx(styles.showcaseCardHeader)}>
|
<div className={clsx(styles.showcaseCardHeader)}>
|
||||||
<h4 className={styles.showcaseCardTitle}>
|
<h4 className={styles.showcaseCardTitle}>
|
||||||
<Link
|
<Link href={user.website} className={styles.showcaseCardLink}>
|
||||||
href={user.website}
|
|
||||||
tabIndex={0}
|
|
||||||
className={styles.showcaseCardLink}>
|
|
||||||
{user.title}
|
{user.title}
|
||||||
</Link>
|
</Link>
|
||||||
</h4>
|
</h4>
|
||||||
|
@ -81,7 +70,6 @@ const ShowcaseCard = memo(({user}: {user: User}) => (
|
||||||
{user.source && (
|
{user.source && (
|
||||||
<Link
|
<Link
|
||||||
href={user.source}
|
href={user.source}
|
||||||
tabIndex={0}
|
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'button button--secondary button--sm',
|
'button button--secondary button--sm',
|
||||||
styles.showcaseCardSrcBtn,
|
styles.showcaseCardSrcBtn,
|
||||||
|
|
|
@ -24,24 +24,16 @@
|
||||||
|
|
||||||
.showcaseCardTitle a {
|
.showcaseCardTitle a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
position: relative;
|
background: linear-gradient(
|
||||||
|
var(--ifm-color-primary),
|
||||||
|
var(--ifm-color-primary)
|
||||||
|
)
|
||||||
|
0% 100% / 0% 1px no-repeat;
|
||||||
|
transition: background-size ease-out 200ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.showcaseCardTitle a::after {
|
.showcaseCardTitle a:not(:focus):hover {
|
||||||
display: block;
|
background-size: 100% 1px;
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 0;
|
|
||||||
height: 1px;
|
|
||||||
transition: width ease-out 200ms;
|
|
||||||
background-color: var(--ifm-color-primary);
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.showcaseCardTitle a:hover::after,
|
|
||||||
.showcaseCardTitle a:focus-visible::after {
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.showcaseCardTitle,
|
.showcaseCardTitle,
|
||||||
|
@ -57,10 +49,7 @@
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
z-index: 1;
|
border: none;
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme='dark'] .showcaseCardSrcBtn {
|
html[data-theme='dark'] .showcaseCardSrcBtn {
|
||||||
|
@ -72,11 +61,6 @@ html[data-theme='dark'] .showcaseCardSrcBtn:hover {
|
||||||
background-color: var(--ifm-color-emphasis-300) !important;
|
background-color: var(--ifm-color-emphasis-300) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.showcaseCardSrcBtn:focus,
|
|
||||||
.showcaseCardTitle a:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.showcaseCardSrcBtn:focus-visible {
|
.showcaseCardSrcBtn:focus-visible {
|
||||||
background-color: var(--ifm-color-secondary-dark);
|
background-color: var(--ifm-color-secondary-dark);
|
||||||
}
|
}
|
||||||
|
@ -87,37 +71,23 @@ html[data-theme='dark'] .showcaseCardSrcBtn:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardFooter {
|
.cardFooter {
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
font-size: 0.675rem;
|
font-size: 0.675rem;
|
||||||
border: 1px solid var(--ifm-color-secondary-darkest);
|
border: 1px solid var(--ifm-color-secondary-darkest);
|
||||||
white-space: nowrap;
|
cursor: default;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
margin-bottom: 6px !important;
|
margin-bottom: 6px !important;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
|
||||||
height: 20px;
|
|
||||||
cursor: default;
|
|
||||||
outline: 0px;
|
|
||||||
text-decoration: none;
|
|
||||||
z-index: 5;
|
|
||||||
padding: 0px;
|
|
||||||
vertical-align: middle;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag .textLabel {
|
.tag .textLabel {
|
||||||
overflow: hidden;
|
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag .colorLabel {
|
.tag .colorLabel {
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
import React, {useState, useEffect, useCallback} from 'react';
|
import React, {useState, useEffect, useCallback} from 'react';
|
||||||
import {useHistory, useLocation} from '@docusaurus/router';
|
import {useHistory, useLocation} from '@docusaurus/router';
|
||||||
|
|
||||||
|
import {prepareUserState} from '../../index';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
@ -33,7 +35,11 @@ export default function ShowcaseFilterToggle(): JSX.Element {
|
||||||
const searchParams = new URLSearchParams(location.search);
|
const searchParams = new URLSearchParams(location.search);
|
||||||
searchParams.delete(OperatorQueryKey);
|
searchParams.delete(OperatorQueryKey);
|
||||||
searchParams.append(OperatorQueryKey, operator ? 'OR' : 'AND');
|
searchParams.append(OperatorQueryKey, operator ? 'OR' : 'AND');
|
||||||
history.push({...location, search: searchParams.toString()});
|
history.push({
|
||||||
|
...location,
|
||||||
|
search: searchParams.toString(),
|
||||||
|
state: prepareUserState(),
|
||||||
|
});
|
||||||
}, [operator, location, history]);
|
}, [operator, location, history]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -17,21 +17,22 @@
|
||||||
border: var(--border) solid var(--ifm-color-primary-darkest);
|
border: var(--border) solid var(--ifm-color-primary-darkest);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
align-items: center;
|
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
transition: opacity var(--ifm-transition-fast)
|
transition: opacity var(--ifm-transition-fast)
|
||||||
var(--ifm-transition-timing-default);
|
var(--ifm-transition-timing-default);
|
||||||
box-shadow: var(--ifm-global-shadow-md);
|
box-shadow: var(--ifm-global-shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus ~ .checkboxLabel,
|
|
||||||
input:focus-visible ~ .checkboxLabel,
|
|
||||||
.checkboxLabel:hover {
|
.checkboxLabel:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
box-shadow: var(--ifm-global-shadow-md),
|
box-shadow: var(--ifm-global-shadow-md),
|
||||||
0px 0px 2px 1px var(--ifm-color-primary-dark);
|
0px 0px 2px 1px var(--ifm-color-primary-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:focus-visible ~ .checkboxLabel::after {
|
||||||
|
outline: 2px solid currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
.checkboxLabel > * {
|
.checkboxLabel > * {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
|
@ -15,6 +15,7 @@ import React, {
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import {useHistory, useLocation} from '@docusaurus/router';
|
import {useHistory, useLocation} from '@docusaurus/router';
|
||||||
import {toggleListItem} from '@site/src/utils/jsUtils';
|
import {toggleListItem} from '@site/src/utils/jsUtils';
|
||||||
|
import {prepareUserState} from '../../index';
|
||||||
import type {TagType} from '@site/src/data/users';
|
import type {TagType} from '@site/src/data/users';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
@ -51,7 +52,11 @@ const ShowcaseTagSelect = React.forwardRef<HTMLLabelElement, Props>(
|
||||||
const tags = readSearchTags(location.search);
|
const tags = readSearchTags(location.search);
|
||||||
const newTags = toggleListItem(tags, tag);
|
const newTags = toggleListItem(tags, tag);
|
||||||
const newSearch = replaceSearchTags(location.search, newTags);
|
const newSearch = replaceSearchTags(location.search, newTags);
|
||||||
history.push({...location, search: newSearch});
|
history.push({
|
||||||
|
...location,
|
||||||
|
search: newSearch,
|
||||||
|
state: prepareUserState(),
|
||||||
|
});
|
||||||
}, [tag, location, history]);
|
}, [tag, location, history]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -64,15 +69,23 @@ const ShowcaseTagSelect = React.forwardRef<HTMLLabelElement, Props>(
|
||||||
toggleTag();
|
toggleTag();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onFocus={(e) => {
|
||||||
|
if (e.relatedTarget) {
|
||||||
|
e.target.nextElementSibling.dispatchEvent(
|
||||||
|
new KeyboardEvent('focus'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={(e) => {
|
||||||
|
e.target.nextElementSibling.dispatchEvent(
|
||||||
|
new KeyboardEvent('blur'),
|
||||||
|
);
|
||||||
|
}}
|
||||||
onChange={toggleTag}
|
onChange={toggleTag}
|
||||||
checked={selected}
|
checked={selected}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
<label
|
<label ref={ref} htmlFor={id} className={styles.checkboxLabel}>
|
||||||
ref={ref}
|
|
||||||
htmlFor={id}
|
|
||||||
className={styles.checkboxLabel}
|
|
||||||
aria-describedby={id}>
|
|
||||||
{label}
|
{label}
|
||||||
{icon}
|
{icon}
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -9,38 +9,30 @@ input[type='checkbox'] + .checkboxLabel {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin: 0;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
text-overflow: ellipsis;
|
|
||||||
padding: 0.275rem 0.8rem;
|
padding: 0.275rem 0.8rem;
|
||||||
white-space: nowrap;
|
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
transition: opacity 200ms ease-out;
|
transition: opacity 200ms ease-out;
|
||||||
border: 2px solid var(--ifm-color-secondary-darkest);
|
border: 2px solid var(--ifm-color-secondary-darkest);
|
||||||
background-color: inherit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus + .checkboxLabel,
|
|
||||||
input:focus-visible + .checkboxLabel,
|
|
||||||
.checkboxLabel:hover {
|
.checkboxLabel:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
outline: 0;
|
|
||||||
box-shadow: 0px 0px 2px 1px var(--ifm-color-secondary-darkest);
|
box-shadow: 0px 0px 2px 1px var(--ifm-color-secondary-darkest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:focus-visible + .checkboxLabel {
|
||||||
|
outline: 2px solid currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
input:checked + .checkboxLabel {
|
input:checked + .checkboxLabel {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
transition: opacity 200ms ease-out;
|
|
||||||
background-color: var(--site-color-checkbox-checked-bg);
|
background-color: var(--site-color-checkbox-checked-bg);
|
||||||
border: 2px solid var(--ifm-color-primary-darkest);
|
border: 2px solid var(--ifm-color-primary-darkest);
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked:focus + .checkboxLabel,
|
|
||||||
input:checked:focus-visible + .checkboxLabel,
|
|
||||||
input:checked + .checkboxLabel:hover {
|
input:checked + .checkboxLabel:hover {
|
||||||
outline: 0;
|
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
box-shadow: 0px 0px 2px 1px var(--ifm-color-primary-dark);
|
box-shadow: 0px 0px 2px 1px var(--ifm-color-primary-dark);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ export default function Tooltip({
|
||||||
);
|
);
|
||||||
|
|
||||||
const timeout = useRef<number>(null);
|
const timeout = useRef<number>(null);
|
||||||
|
const tooltipId = `${id}_tooltip`;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (anchorEl) {
|
if (anchorEl) {
|
||||||
|
@ -116,12 +117,13 @@ export default function Tooltip({
|
||||||
<>
|
<>
|
||||||
{React.cloneElement(children, {
|
{React.cloneElement(children, {
|
||||||
ref: setReferenceElement,
|
ref: setReferenceElement,
|
||||||
|
'aria-describedby': open ? tooltipId : undefined,
|
||||||
})}
|
})}
|
||||||
{container
|
{container
|
||||||
? ReactDOM.createPortal(
|
? ReactDOM.createPortal(
|
||||||
open && (
|
open && (
|
||||||
<div
|
<div
|
||||||
id={id}
|
id={tooltipId}
|
||||||
role="tooltip"
|
role="tooltip"
|
||||||
ref={setPopperElement}
|
ref={setPopperElement}
|
||||||
className={styles.tooltip}
|
className={styles.tooltip}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
opacity: 0.92;
|
opacity: 0.92;
|
||||||
white-space: normal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltipArrow {
|
.tooltipArrow {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import ShowcaseCard from './_components/ShowcaseCard';
|
||||||
import {sortedUsers, Tags, TagList, User, TagType} from '@site/src/data/users';
|
import {sortedUsers, Tags, TagList, User, TagType} from '@site/src/data/users';
|
||||||
import ShowcaseTooltip from './_components/ShowcaseTooltip';
|
import ShowcaseTooltip from './_components/ShowcaseTooltip';
|
||||||
|
|
||||||
|
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
||||||
import {useLocation} from '@docusaurus/router';
|
import {useLocation} from '@docusaurus/router';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
@ -31,6 +32,31 @@ const DESCRIPTION = 'List of websites people are building with Docusaurus';
|
||||||
const EDIT_URL =
|
const EDIT_URL =
|
||||||
'https://github.com/facebook/docusaurus/edit/main/website/src/data/users.tsx';
|
'https://github.com/facebook/docusaurus/edit/main/website/src/data/users.tsx';
|
||||||
|
|
||||||
|
type UserState = {
|
||||||
|
scrollTopPosition: number;
|
||||||
|
focusedElementId: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
function restoreUserState(userState: UserState | null) {
|
||||||
|
const {scrollTopPosition, focusedElementId} = userState ?? {
|
||||||
|
scrollTopPosition: 0,
|
||||||
|
focusedElementId: undefined,
|
||||||
|
};
|
||||||
|
document.getElementById(focusedElementId)?.focus();
|
||||||
|
window.scrollTo({top: scrollTopPosition});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepareUserState(): UserState | undefined {
|
||||||
|
if (ExecutionEnvironment.canUseDOM) {
|
||||||
|
return {
|
||||||
|
scrollTopPosition: window.scrollY,
|
||||||
|
focusedElementId: document.activeElement?.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function filterUsers(
|
function filterUsers(
|
||||||
users: User[],
|
users: User[],
|
||||||
selectedTags: TagType[],
|
selectedTags: TagType[],
|
||||||
|
@ -53,10 +79,11 @@ function filterUsers(
|
||||||
|
|
||||||
function useFilteredUsers() {
|
function useFilteredUsers() {
|
||||||
const selectedTags = useSelectedTags();
|
const selectedTags = useSelectedTags();
|
||||||
const location = useLocation();
|
const location = useLocation<UserState>();
|
||||||
const [operator, setOperator] = useState<Operator>('OR');
|
const [operator, setOperator] = useState<Operator>('OR');
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOperator(readOperator(location.search));
|
setOperator(readOperator(location.search));
|
||||||
|
restoreUserState(location.state);
|
||||||
}, [location]);
|
}, [location]);
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => filterUsers(sortedUsers, selectedTags, operator),
|
() => filterUsers(sortedUsers, selectedTags, operator),
|
||||||
|
@ -66,15 +93,15 @@ function useFilteredUsers() {
|
||||||
|
|
||||||
function useSelectedTags() {
|
function useSelectedTags() {
|
||||||
// The search query-string is the source of truth!
|
// The search query-string is the source of truth!
|
||||||
const location = useLocation();
|
const location = useLocation<UserState>();
|
||||||
|
|
||||||
// On SSR / first mount (hydration) no tag is selected
|
// On SSR / first mount (hydration) no tag is selected
|
||||||
const [selectedTags, setSelectedTags] = useState<TagType[]>([]);
|
const [selectedTags, setSelectedTags] = useState<TagType[]>([]);
|
||||||
|
|
||||||
// Sync tags from QS to state (delayed on purpose to avoid SSR/Client hydration mismatch)
|
// Sync tags from QS to state (delayed on purpose to avoid SSR/Client hydration mismatch)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tags = readSearchTags(location.search);
|
setSelectedTags(readSearchTags(location.search));
|
||||||
setSelectedTags(tags);
|
restoreUserState(location.state);
|
||||||
}, [location]);
|
}, [location]);
|
||||||
|
|
||||||
return selectedTags;
|
return selectedTags;
|
||||||
|
@ -101,18 +128,18 @@ function ShowcaseFilters() {
|
||||||
return (
|
return (
|
||||||
<section className="container margin-top--l margin-bottom--lg">
|
<section className="container margin-top--l margin-bottom--lg">
|
||||||
<div className={clsx('margin-bottom--sm', styles.filterCheckbox)}>
|
<div className={clsx('margin-bottom--sm', styles.filterCheckbox)}>
|
||||||
<span>
|
<div>
|
||||||
<h2>Filters</h2>
|
<h2>Filters</h2>
|
||||||
<span>{`(${filteredUsers.length} site${
|
<span>{`(${filteredUsers.length} site${
|
||||||
filteredUsers.length > 1 ? 's' : ''
|
filteredUsers.length > 1 ? 's' : ''
|
||||||
})`}</span>
|
})`}</span>
|
||||||
</span>
|
</div>
|
||||||
<ShowcaseFilterToggle />
|
<ShowcaseFilterToggle />
|
||||||
</div>
|
</div>
|
||||||
<ul className={styles.checkboxList}>
|
<ul className={styles.checkboxList}>
|
||||||
{TagList.map((tag, i) => {
|
{TagList.map((tag, i) => {
|
||||||
const {label, description, color} = Tags[tag];
|
const {label, description, color} = Tags[tag];
|
||||||
const id = `showcase_checkbox_id_${tag};`;
|
const id = `showcase_checkbox_id_${tag}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={i} className={styles.checkboxListItem}>
|
<li key={i} className={styles.checkboxListItem}>
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filterCheckbox > span {
|
.filterCheckbox > div:first-child {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filterCheckbox > span > * {
|
.filterCheckbox > div > * {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
@ -37,20 +37,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkboxListItem {
|
.checkboxListItem {
|
||||||
position: relative;
|
|
||||||
background-color: transparent;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 32px;
|
height: 32px;
|
||||||
outline: 0px;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 0px;
|
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
vertical-align: middle;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkboxListItem {
|
.checkboxListItem {
|
||||||
|
@ -66,7 +56,6 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.showcaseFavorite {
|
.showcaseFavorite {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue