react-icons/io#IoMdCloseCircleOutline TypeScript Examples
The following examples show how to use
react-icons/io#IoMdCloseCircleOutline.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Snackbar.tsx From crosshare with GNU Affero General Public License v3.0 | 5 votes |
function Toast({ id, message }: { id: number; message: string }) {
const { dispatch } = useContext(SnackbarContext);
const [closing, setClosing] = useState(false);
const [closed, setClosed] = useState(false);
const close = useCallback(() => {
// Close the toast which causes it to slide right
setClosing(true);
// After slide right we set closing which causes it to shrink vertically
setTimeout(() => {
setClosed(true);
}, ANIMATION_DELAY);
// After shrink vertically we remove the toast
setTimeout(() => {
dispatch({ type: ActionTypes.RemoveToast, id });
}, 2 * ANIMATION_DELAY);
}, [dispatch, id]);
useEffect(() => {
const timer = setTimeout(() => {
close();
}, DURATION);
return () => clearTimeout(timer);
}, [close]);
return (
<div
css={{
transition: 'all ' + ANIMATION_DELAY + 'ms ease-in-out 0s',
maxHeight: 500,
...(closed && { maxHeight: 0 }),
[SMALL_AND_UP]: {
'& + &': {
marginTop: '1em',
},
},
}}
>
<div
role="button"
tabIndex={0}
css={{
cursor: 'pointer',
backgroundColor: 'var(--overlay-inner)',
color: 'var(--text)',
padding: '1em',
width: '100%',
marginLeft: '110%',
boxShadow: '0px 0px 3px 3px rgba(0, 0, 0, 0.5)',
animation: `${slidein} 0.3s ease-in-out`,
transition: 'all ' + ANIMATION_DELAY + 'ms ease-in-out 0s',
...(message &&
!closing && {
marginLeft: 0,
}),
}}
onClick={close}
onKeyPress={close}
>
<IoMdCloseCircleOutline
css={{
float: 'right',
}}
/>
{message}
</div>
</div>
);
}
Example #2
Source File: TopBar.tsx From crosshare with GNU Affero General Public License v3.0 | 5 votes |
NotificationLink = ({
notification: n,
}: {
notification: NotificationT;
}): JSX.Element => {
const [closing, setClosing] = useState(false);
const close = useCallback(() => {
// Close the toast which causes it to start shrinking
setClosing(true);
// After shrink vertically we remove the toast
setTimeout(() => {
App.firestore().collection('n').doc(n.id).update({ r: true });
}, ANIMATION_DELAY);
}, [n.id]);
let link: JSX.Element;
switch (n.k) {
case 'comment':
link = (
<Link css={NotificationLinkCSS} href={`/crosswords/${n.p}/${slugify(n.pn)}`}>
{n.cn} commented on <u>{n.pn}</u>
</Link>
);
break;
case 'reply':
link = (
<Link css={NotificationLinkCSS} href={`/crosswords/${n.p}/${slugify(n.pn)}`}>
{n.cn} replied to your comment on <u>{n.pn}</u>
</Link>
);
break;
case 'newpuzzle':
link = (
<Link css={NotificationLinkCSS} href={`/crosswords/${n.p}/${slugify(n.pn)}`}>
{n.an} published a new puzzle: <u>{n.pn}</u>
</Link>
);
break;
case 'featured':
link = (
<Link css={NotificationLinkCSS} href={`/crosswords/${n.p}/${slugify(n.pn)}`}>
Crosshare is featuring your puzzle <u>{n.pn}</u>
{n.as ? ` as ${n.as}` : ' on the homepage'}!
</Link>
);
break;
}
return (
<div
css={{
transition: 'all ' + ANIMATION_DELAY + 'ms ease-in-out 0s',
maxHeight: 500,
overflow: 'hidden',
...(closing && { maxHeight: 0 }),
display: 'flex',
alignItems: 'center',
'& + &': {
borderTop: '1px solid var(--text-input-border)',
},
}}
>
{link}
<div
role="button"
tabIndex={0}
onClick={close}
onKeyPress={close}
css={{
paddingLeft: '1em',
cursor: 'pointer',
}}
>
<IoMdCloseCircleOutline />
</div>
</div>
);
}
Example #3
Source File: Filters.tsx From hub with Apache License 2.0 | 4 votes |
Filters = (props: Props) => {
const getFacetsByFilterKey = (filterKey: string): Facets | undefined => {
return find(props.facets, (facets: Facets) => filterKey === facets.filterKey);
};
const getPublishers = (): JSX.Element | null => {
let crElement = null;
const publisherList = getFacetsByFilterKey('publisher');
if (!isUndefined(publisherList) && publisherList.options.length > 0) {
const isChecked = (facetOptionId: string, filterKey: string) => {
return (props.activeFilters[filterKey] || []).includes(facetOptionId.toString());
};
const options = publisherList.options.map((facet: FacetOption) => ({
...facet,
filterKey: facet.filterKey!,
}));
const publisherOptions = options.map((option: Option) => (
<CheckBox
key={`${option.filterKey}_${option.id.toString()}`}
name={option.filterKey}
device={props.device}
value={option.id.toString()}
labelClassName="mw-100"
className={styles.checkbox}
legend={option.total}
label={option.name}
checked={isChecked(option.id.toString(), option.filterKey)}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
props.onChange(e.target.name, e.target.value, e.target.checked)
}
/>
));
crElement = (
<div role="menuitem" className={`mt-2 mt-sm-3 pt-1 ${styles.facet}`}>
<SmallTitle text="Publisher" className="text-dark fw-bold" />
<div className="mt-3" role="group">
<ExpandableList visibleItems={5} items={publisherOptions} forceCollapseList={props.forceCollapseList} />
</div>
</div>
);
}
return crElement;
};
const getKindFacets = (): JSX.Element | null => {
let kindElement = null;
const kind = getFacetsByFilterKey('kind');
if (!isUndefined(kind) && kind.options.length > 0 && kind.filterKey) {
const active = props.activeFilters.hasOwnProperty(kind.filterKey) ? props.activeFilters[kind.filterKey] : [];
const isChecked = (facetOptionId: string) => {
return active.includes(facetOptionId.toString());
};
kindElement = (
<div role="menuitem" className={`mt-1 mt-sm-2 pt-1 ${styles.facet}`}>
<SmallTitle text={kind.title} className="text-dark fw-bold" id={`repo-${kind.filterKey}-${props.device}`} />
<div className="mt-3" role="group" aria-labelledby={`repo-${kind.filterKey}-${props.device}`}>
{kind.options.map((option: FacetOption) => (
<CheckBox
key={`kind_${option.id.toString()}`}
name={kind.filterKey!}
device={props.device}
value={option.id.toString()}
labelClassName="mw-100"
className={styles.checkbox}
legend={option.total}
label={option.name}
checked={isChecked(option.id.toString())}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
props.onChange(e.target.name, e.target.value, e.target.checked)
}
/>
))}
</div>
</div>
);
}
return kindElement;
};
const getCapabilitiesFacets = (): JSX.Element | null => {
let element = null;
const capabilities = getFacetsByFilterKey('capabilities');
if (!isUndefined(capabilities) && capabilities.options.length > 0 && capabilities.filterKey) {
const active = props.activeFilters.hasOwnProperty(capabilities.filterKey)
? props.activeFilters[capabilities.filterKey]
: [];
const isChecked = (facetOptionId: string) => {
return active.includes(facetOptionId.toString());
};
const sortedCapabilities = sortBy(capabilities.options, [
(facet: FacetOption) => {
return OPERATOR_CAPABILITIES.findIndex((level: string) => level === facet.id);
},
]);
element = (
<div role="menuitem" className={`mt-2 mt-sm-3 pt-1 ${styles.facet}`}>
<SmallTitle
text={capabilities.title}
className="text-dark fw-bold"
id={`pkg-${capabilities.filterKey}-${props.device}`}
/>
<div className="mt-3" role="group" aria-labelledby={`pkg-${capabilities.filterKey}-${props.device}`}>
{sortedCapabilities.map((option: FacetOption) => (
<CheckBox
key={`capabilities_${option.id.toString()}`}
name={capabilities.filterKey!}
device={props.device}
value={option.id.toString()}
labelClassName="mw-100"
className={`text-capitalize ${styles.checkbox}`}
legend={option.total}
label={option.name}
checked={isChecked(option.id.toString())}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
props.onChange(e.target.name, e.target.value, e.target.checked)
}
/>
))}
</div>
</div>
);
}
return element;
};
const getRepositoryFacets = (): JSX.Element | null => {
let crElement = null;
const repo = getFacetsByFilterKey('repo');
if (!isUndefined(repo) && repo.options.length > 0 && repo.filterKey) {
const options = repo.options.map((facet: FacetOption) => ({
...facet,
filterKey: repo.filterKey,
}));
const isChecked = (facetOptionId: string) => {
return (props.activeFilters.repo || []).includes(facetOptionId.toString());
};
const repoOptions = options.map((option: FacetOption) => (
<CheckBox
key={`repo_${option.id.toString()}`}
name={repo.filterKey!}
device={props.device}
value={option.id.toString()}
labelClassName="mw-100"
className={styles.checkbox}
legend={option.total}
label={option.name}
checked={isChecked(option.id.toString())}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
props.onChange(e.target.name, e.target.value, e.target.checked)
}
/>
));
crElement = (
<div role="menuitem" className={`mt-2 mt-sm-3 pt-1 ${styles.facet}`}>
<SmallTitle text={repo.title} className="text-dark fw-bold" id={`pkg-${repo.filterKey}-${props.device}`} />
<div className="mt-3" role="group" aria-labelledby={`pkg-${repo.filterKey}-${props.device}`}>
<ExpandableList visibleItems={5} items={repoOptions} forceCollapseList={props.forceCollapseList} />
</div>
</div>
);
}
return crElement;
};
const getLicenseFacets = (): JSX.Element | null => {
let crElement = null;
const license = getFacetsByFilterKey('license');
if (!isUndefined(license) && license.options.length > 0 && license.filterKey) {
const options = license.options.map((facet: FacetOption) => ({
...facet,
filterKey: license.filterKey,
}));
const isChecked = (facetOptionId: string) => {
return (props.activeFilters.license || []).includes(facetOptionId.toString());
};
const licenseOptions = options.map((option: FacetOption) => (
<CheckBox
key={`license_${option.id.toString()}`}
name={license.filterKey!}
device={props.device}
value={option.id.toString()}
labelClassName="mw-100"
className={`text-capitalize ${styles.checkbox}`}
legend={option.total}
label={option.name}
checked={isChecked(option.id.toString())}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
props.onChange(e.target.name, e.target.value, e.target.checked)
}
/>
));
crElement = (
<div role="menuitem" className={`mt-2 mt-sm-3 pt-1 ${styles.facet}`}>
<SmallTitle
text={license.title}
className="text-dark fw-bold"
id={`pkg-${license.filterKey}-${props.device}`}
/>
<div className="mt-3" role="group" aria-labelledby={`pkg-${license.filterKey}-${props.device}`}>
<ExpandableList visibleItems={5} items={licenseOptions} forceCollapseList={props.forceCollapseList} />
</div>
</div>
);
}
return crElement;
};
return (
<div className={classnames(styles.filters, { 'pt-2 mt-3 mb-5': props.visibleTitle })}>
{props.visibleTitle && (
<div className="d-flex flex-row align-items-center justify-content-between pb-2 mb-4 border-bottom">
<div className="h6 text-uppercase mb-0 lh-base">Filters</div>
{(!isEmpty(props.activeFilters) ||
props.deprecated ||
props.operators ||
props.verifiedPublisher ||
props.official ||
!isEmpty(props.activeTsQuery)) && (
<div className={`d-flex align-items-center ${styles.resetBtnWrapper}`}>
<IoMdCloseCircleOutline className={`text-dark ${styles.resetBtnDecorator}`} />
<button
className={`btn btn-link btn-sm p-0 ps-1 text-dark ${styles.resetBtn}`}
onClick={props.onResetFilters}
aria-label="Reset filters"
>
Reset
</button>
</div>
)}
</div>
)}
<div className="d-flex flex-row align-items-baseline">
<CheckBox
name="official"
value="official"
device={props.device}
className={styles.checkbox}
labelClassName="mw-100"
label="Official"
checked={props.official || false}
onChange={props.onOfficialChange}
/>
<div className="d-none d-md-block">
<ElementWithTooltip
tooltipClassName={styles.tooltipMessage}
className={`position-relative ${styles.tooltipIcon}`}
element={<MdInfoOutline />}
tooltipMessage="The publisher owns the software deployed by the packages"
visibleTooltip
active
/>
</div>
</div>
<div className="d-flex flex-row align-items-baseline">
<CheckBox
name="verifiedPublisher"
value="verifiedPublisher"
device={props.device}
className={styles.checkbox}
labelClassName="mw-100"
label="Verified publishers"
checked={props.verifiedPublisher || false}
onChange={props.onVerifiedPublisherChange}
/>
<div className="d-none d-md-block">
<ElementWithTooltip
tooltipClassName={styles.tooltipMessage}
className={styles.tooltipIcon}
element={<MdInfoOutline />}
tooltipMessage="The publisher owns the repository"
visibleTooltip
active
/>
</div>
</div>
{getKindFacets()}
<TsQuery device={props.device} active={props.activeTsQuery || []} onChange={props.onTsQueryChange} />
{getPublishers()}
{getRepositoryFacets()}
{getLicenseFacets()}
{getCapabilitiesFacets()}
<div role="menuitem" className={`mt-2 mt-sm-3 pt-1 ${styles.facet}`}>
<SmallTitle text="Others" className="text-dark fw-bold" />
<div className="mt-3">
<CheckBox
name="operators"
value="operators"
device={props.device}
labelClassName="mw-100"
className={styles.checkbox}
label="Only operators"
checked={props.operators || false}
onChange={props.onOperatorsChange}
/>
<CheckBox
name="deprecated"
value="deprecated"
device={props.device}
className={styles.checkbox}
label="Include deprecated"
labelClassName="mw-100"
checked={props.deprecated || false}
onChange={props.onDeprecatedChange}
/>
</div>
</div>
</div>
);
}
Example #4
Source File: index.tsx From hub with Apache License 2.0 | 4 votes |
SearchView = (props: Props) => {
const { ctx, dispatch } = useContext(AppCtx);
const history = useHistory();
const sampleQueries = getSampleQueries();
const [searchResults, setSearchResults] = useState<SearchResults>({
facets: null,
packages: null,
paginationTotalCount: '0',
});
const { isSearching, setIsSearching, scrollPosition, setScrollPosition } = props;
const [apiError, setApiError] = useState<string | null>(null);
const [currentTsQueryWeb, setCurrentTsQueryWeb] = useState(props.tsQueryWeb);
const calculateOffset = (): number => {
return props.pageNumber && ctx.prefs.search.limit ? (props.pageNumber - 1) * ctx.prefs.search.limit : 0;
};
const [offset, setOffset] = useState<number>(calculateOffset());
const getFilterName = (key: string, label: string): FilterLabel | null => {
const correctKey = ['user', 'org'].includes(key) ? 'publisher' : key;
if (searchResults.facets) {
const selectedKey = searchResults.facets.find((fac: Facets) => fac.filterKey === correctKey);
if (selectedKey) {
const selectedOpt = selectedKey.options.find((opt: FacetOption) => opt.id.toString() === label);
if (selectedOpt) {
return { key: selectedKey.title, name: selectedOpt.name };
} else {
return null;
}
} else {
return null;
}
}
return null;
};
const getTsQueryName = (ts: string): string | null => {
const selectedTsQuery = TS_QUERY.find((query: TsQuery) => query.label === ts);
if (selectedTsQuery) {
return selectedTsQuery.name;
} else {
return null;
}
};
const isEmptyFacets = (): boolean => {
if (searchResults.facets) {
return every(searchResults.facets, (f: Facets) => {
return f.options.length === 0;
});
} else {
return true;
}
};
useScrollRestorationFix();
const saveScrollPosition = () => {
setScrollPosition(window.scrollY);
};
const updateWindowScrollPosition = (newPosition: number) => {
window.scrollTo(0, newPosition);
};
const prepareSelectedFilters = (name: string, newFilters: string[], prevFilters: FiltersProp): FiltersProp => {
let cleanFilters: FiltersProp = {};
switch (name) {
case 'kind':
// Remove selected chart repositories when some kind different to Chart is selected and Chart is not selected
if (newFilters.length > 0 && !newFilters.includes(RepositoryKind.Helm.toString())) {
cleanFilters['repo'] = [];
}
break;
}
return {
...prevFilters,
[name]: newFilters,
...cleanFilters,
};
};
const getCurrentFilters = (): SearchFiltersURL => {
return {
pageNumber: props.pageNumber,
tsQueryWeb: props.tsQueryWeb,
tsQuery: props.tsQuery,
filters: props.filters,
deprecated: props.deprecated,
operators: props.operators,
verifiedPublisher: props.verifiedPublisher,
official: props.official,
sort: props.sort,
};
};
const updateCurrentPage = (searchChanges: any) => {
history.push({
pathname: '/packages/search',
search: prepareQueryString({
...getCurrentFilters(),
pageNumber: 1,
...searchChanges,
}),
});
};
const onFiltersChange = (name: string, value: string, checked: boolean): void => {
const currentFilters = props.filters || {};
let newFilters = isUndefined(currentFilters[name]) ? [] : currentFilters[name].slice();
if (checked) {
newFilters.push(value);
} else {
newFilters = newFilters.filter((el) => el !== value);
}
updateCurrentPage({
filters: prepareSelectedFilters(name, newFilters, currentFilters),
});
};
const onResetSomeFilters = (filterKeys: string[]): void => {
let newFilters: FiltersProp = {};
filterKeys.forEach((fKey: string) => {
newFilters[fKey] = [];
});
updateCurrentPage({
filters: { ...props.filters, ...newFilters },
});
};
const onTsQueryChange = (value: string, checked: boolean): void => {
let query = isUndefined(props.tsQuery) ? [] : props.tsQuery.slice();
if (checked) {
query.push(value);
} else {
query = query.filter((el) => el !== value);
}
updateCurrentPage({
tsQuery: query,
});
};
const onDeprecatedChange = (): void => {
updateCurrentPage({
deprecated: !isUndefined(props.deprecated) && !isNull(props.deprecated) ? !props.deprecated : true,
});
};
const onOperatorsChange = (): void => {
updateCurrentPage({
operators: !isUndefined(props.operators) && !isNull(props.operators) ? !props.operators : true,
});
};
const onVerifiedPublisherChange = (): void => {
updateCurrentPage({
verifiedPublisher:
!isUndefined(props.verifiedPublisher) && !isNull(props.verifiedPublisher) ? !props.verifiedPublisher : true,
});
};
const onOfficialChange = (): void => {
updateCurrentPage({
official: !isUndefined(props.official) && !isNull(props.official) ? !props.official : true,
});
};
const onResetFilters = (): void => {
history.push({
pathname: '/packages/search',
search: prepareQueryString({
pageNumber: 1,
tsQueryWeb: props.tsQueryWeb,
tsQuery: [],
filters: {},
sort: DEFAULT_SORT,
}),
});
};
const onPageNumberChange = (pageNumber: number): void => {
updateCurrentPage({
pageNumber: pageNumber,
});
};
const onSortChange = (sort: string): void => {
history.replace({
pathname: '/packages/search',
search: prepareQueryString({
...getCurrentFilters(),
sort: sort,
pageNumber: 1,
}),
});
setScrollPosition(0);
updateWindowScrollPosition(0);
};
const onPaginationLimitChange = (newLimit: number): void => {
history.replace({
pathname: '/packages/search',
search: prepareQueryString({
...getCurrentFilters(),
pageNumber: 1,
}),
});
setScrollPosition(0);
updateWindowScrollPosition(0);
dispatch(updateLimit(newLimit));
};
useEffect(() => {
async function fetchSearchResults() {
setIsSearching(true);
const query = {
tsQueryWeb: props.tsQueryWeb,
tsQuery: props.tsQuery,
filters: props.filters,
offset: calculateOffset(),
limit: ctx.prefs.search.limit,
deprecated: props.deprecated,
operators: props.operators,
verifiedPublisher: props.verifiedPublisher,
official: props.official,
sort: props.sort || DEFAULT_SORT,
};
try {
let newSearchResults = await API.searchPackages(query);
if (
newSearchResults.paginationTotalCount === '0' &&
searchResults.facets &&
!isEmpty(searchResults.facets) &&
currentTsQueryWeb === props.tsQueryWeb // When some filters have changed, but not ts_query_web
) {
newSearchResults = {
...newSearchResults,
facets: searchResults.facets,
};
}
setSearchResults(newSearchResults);
setOffset(query.offset);
setCurrentTsQueryWeb(props.tsQueryWeb);
setApiError(null);
} catch {
setSearchResults({
facets: [],
packages: [],
paginationTotalCount: '0',
});
setApiError('An error occurred searching packages, please try again later.');
} finally {
setIsSearching(false);
// Update scroll position
if (history.action === 'PUSH') {
// When search page is open from detail page
if (props.fromDetail && !isUndefined(scrollPosition)) {
updateWindowScrollPosition(scrollPosition);
// When search has changed
} else {
updateWindowScrollPosition(0);
}
// On pop action and when scroll position has been previously saved
} else if (!isUndefined(scrollPosition)) {
updateWindowScrollPosition(scrollPosition);
}
}
}
fetchSearchResults();
// prettier-ignore
/* eslint-disable react-hooks/exhaustive-deps */
}, [
props.tsQueryWeb,
JSON.stringify(props.tsQuery),
props.pageNumber,
JSON.stringify(props.filters), // https://twitter.com/dan_abramov/status/1104414272753487872
props.deprecated,
props.operators,
props.verifiedPublisher,
props.official,
ctx.prefs.search.limit,
props.sort,
]);
/* eslint-enable react-hooks/exhaustive-deps */
const activeFilters =
props.deprecated ||
props.operators ||
props.verifiedPublisher ||
props.official ||
!isUndefined(props.tsQuery) ||
!isEmpty(props.filters);
return (
<>
<SubNavbar className={`h-auto ${styles.subnavbar}`}>
<div className="d-flex flex-column w-100">
<div className="d-flex align-items-center justify-content-between flex-nowrap">
<div className="d-flex align-items-center text-truncate w-100">
{!isNull(searchResults.packages) && (
<>
{/* Mobile filters */}
{!isEmptyFacets() && (
<Sidebar
label="Filters"
className="d-inline-block d-md-none me-2"
wrapperClassName="px-4"
buttonType={classnames('btn-sm rounded-circle position-relative', styles.btnMobileFilters, {
[styles.filtersBadge]: activeFilters,
})}
buttonIcon={<FaFilter />}
closeButton={
<>
{isSearching ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Loading...</span>
</>
) : (
<>See {searchResults.paginationTotalCount} results</>
)}
</>
}
leftButton={
<>
<div className="d-flex align-items-center">
<IoMdCloseCircleOutline className={`text-dark ${styles.resetBtnDecorator}`} />
<button
className="btn btn-link btn-sm p-0 ps-1 text-dark"
onClick={onResetFilters}
aria-label="Reset filters"
>
Reset
</button>
</div>
</>
}
header={<div className="h6 text-uppercase mb-0 flex-grow-1">Filters</div>}
>
<div role="menu">
<Filters
forceCollapseList={props.tsQueryWeb !== currentTsQueryWeb}
facets={searchResults.facets}
activeFilters={props.filters || {}}
activeTsQuery={props.tsQuery}
onChange={onFiltersChange}
onResetSomeFilters={onResetSomeFilters}
onTsQueryChange={onTsQueryChange}
deprecated={props.deprecated}
operators={props.operators}
verifiedPublisher={props.verifiedPublisher}
official={props.official}
onDeprecatedChange={onDeprecatedChange}
onOperatorsChange={onOperatorsChange}
onVerifiedPublisherChange={onVerifiedPublisherChange}
onOfficialChange={onOfficialChange}
onResetFilters={onResetFilters}
visibleTitle={false}
device="mobile"
/>
</div>
</Sidebar>
)}
{!isSearching && (
<div className="d-flex flex-column w-100 text-truncate">
<div className={`text-truncate ${styles.searchText}`} role="status">
{parseInt(searchResults.paginationTotalCount) > 0 && (
<span className="pe-1">
{offset + 1} -{' '}
{parseInt(searchResults.paginationTotalCount) < ctx.prefs.search.limit * props.pageNumber
? searchResults.paginationTotalCount
: ctx.prefs.search.limit * props.pageNumber}{' '}
<span className="ms-1">of</span>{' '}
</span>
)}
{searchResults.paginationTotalCount}
<span className="ps-1"> results </span>
{props.tsQueryWeb && props.tsQueryWeb !== '' && (
<span className="d-none d-sm-inline ps-1">
for "<span className="fw-bold">{props.tsQueryWeb}</span>"
</span>
)}
{activeFilters && (
<small className="d-inline d-lg-none fst-italic ms-1"> (some filters applied)</small>
)}
</div>
</div>
)}
</>
)}
</div>
<div className="ms-3">
<div className="d-flex flex-row">
{/* Only display sort options when ts_query_web is defined */}
{props.tsQueryWeb && props.tsQueryWeb !== '' && (
<SortOptions
activeSort={props.sort || DEFAULT_SORT}
updateSort={onSortChange}
disabled={isNull(searchResults.packages) || searchResults.packages.length === 0}
/>
)}
<div className="d-none d-sm-flex">
<PaginationLimit
limit={ctx.prefs.search.limit}
updateLimit={onPaginationLimitChange}
disabled={isNull(searchResults.packages) || searchResults.packages.length === 0}
/>
</div>
<MoreActionsButton />
</div>
</div>
</div>
{activeFilters && (
<div className="d-none d-lg-inline">
<div className="d-flex flex-row flex-wrap align-items-center pt-2">
<span className="me-2 pe-1 mb-2">Filters:</span>
{props.official && <FilterBadge name="Only official" onClick={onOfficialChange} />}
{props.verifiedPublisher && (
<FilterBadge name="Only verified publishers" onClick={onVerifiedPublisherChange} />
)}
{!isUndefined(props.filters) && (
<>
{Object.keys(props.filters).map((type: string) => {
const opts = props.filters![type];
return (
<Fragment key={`opts_${type}`}>
{opts.map((opt: string) => {
const filter = getFilterName(type, opt);
if (isNull(filter)) return null;
return (
<FilterBadge
key={`btn_${type}_${opt}`}
type={filter.key}
name={filter.name}
onClick={() => onFiltersChange(type, opt, false)}
/>
);
})}
</Fragment>
);
})}
</>
)}
{props.tsQuery && (
<>
{props.tsQuery.map((ts: string) => {
const value = getTsQueryName(ts);
if (isNull(value)) return null;
return (
<FilterBadge
key={`btn_${ts}`}
type="Category"
name={value}
onClick={() => onTsQueryChange(ts, false)}
/>
);
})}
</>
)}
{props.operators && <FilterBadge name="Only operators" onClick={onOperatorsChange} />}
{props.deprecated && <FilterBadge name="Include deprecated" onClick={onDeprecatedChange} />}
</div>
</div>
)}
</div>
</SubNavbar>
<div className="d-flex position-relative pt-3 pb-3 flex-grow-1">
{(isSearching || isNull(searchResults.packages)) && <Loading spinnerClassName="position-fixed top-50" />}
<main role="main" className="container-lg px-sm-4 px-lg-0 d-flex flex-row justify-content-between">
{!isEmptyFacets() && (
<aside
className={`px-xs-0 px-sm-3 px-lg-0 d-none d-md-block position-relative ${styles.sidebar}`}
aria-label="Filters"
>
<div className="me-5" role="menu">
<Filters
forceCollapseList={props.tsQueryWeb !== currentTsQueryWeb}
facets={searchResults.facets}
activeFilters={props.filters || {}}
activeTsQuery={props.tsQuery}
onChange={onFiltersChange}
onResetSomeFilters={onResetSomeFilters}
onTsQueryChange={onTsQueryChange}
deprecated={props.deprecated}
operators={props.operators}
verifiedPublisher={props.verifiedPublisher}
official={props.official}
onDeprecatedChange={onDeprecatedChange}
onOperatorsChange={onOperatorsChange}
onVerifiedPublisherChange={onVerifiedPublisherChange}
onOfficialChange={onOfficialChange}
onResetFilters={onResetFilters}
visibleTitle
device="desktop"
/>
</div>
</aside>
)}
<div
className={classnames('flex-grow-1 mt-3 px-xs-0 px-sm-3 px-lg-0', styles.list, {
[styles.emptyList]: isNull(searchResults.packages) || searchResults.packages.length === 0,
})}
>
{!isNull(searchResults.packages) && (
<>
{searchResults.packages.length === 0 ? (
<NoData issuesLinkVisible={!isNull(apiError)}>
{isNull(apiError) ? (
<>
We're sorry!
<p className="h6 mb-0 mt-3 lh-base">
<span> We can't seem to find any packages that match your search </span>
{props.tsQueryWeb && (
<span className="ps-1">
for "<span className="fw-bold">{props.tsQueryWeb}</span>"
</span>
)}
{!isEmpty(props.filters) && <span className="ps-1">with the selected filters</span>}
</p>
<p className="h6 mb-0 mt-5 lh-base">
You can{' '}
{!isEmpty(props.filters) ? (
<button
className="btn btn-link text-dark fw-bold py-0 pb-1 px-0"
onClick={onResetFilters}
aria-label="Reset filters"
>
<u>reset the filters</u>
</button>
) : (
<button
className="btn btn-link text-dark fw-bold py-0 pb-1 px-0"
onClick={() => {
history.push({
pathname: '/packages/search',
search: prepareQueryString({
pageNumber: 1,
tsQueryWeb: '',
tsQuery: [],
filters: {},
}),
});
}}
aria-label="Browse all packages"
>
<u>browse all packages</u>
</button>
)}
{sampleQueries.length > 0 ? (
<>, try a new search or start with one of the sample queries:</>
) : (
<> or try a new search.</>
)}
</p>
<div className="h5 d-flex flex-row align-items-end justify-content-center flex-wrap">
<SampleQueries className="bg-light text-dark border-secondary text-dark" />
</div>
</>
) : (
<>{apiError}</>
)}
</NoData>
) : (
<>
<div className="mb-2 noFocus" id="content" tabIndex={-1} aria-label="Packages list">
<div className="row" role="list">
{searchResults.packages.map((item: Package) => (
<SearchCard
key={item.packageId}
package={item}
searchUrlReferer={{
tsQueryWeb: props.tsQueryWeb,
tsQuery: props.tsQuery,
pageNumber: props.pageNumber,
filters: props.filters,
deprecated: props.deprecated,
operators: props.operators,
verifiedPublisher: props.verifiedPublisher,
official: props.official,
sort: props.sort,
}}
saveScrollPosition={saveScrollPosition}
/>
))}
</div>
</div>
<Pagination
limit={ctx.prefs.search.limit}
offset={offset}
total={parseInt(searchResults.paginationTotalCount)}
active={props.pageNumber}
className="my-5"
onChange={onPageNumberChange}
/>
</>
)}
</>
)}
</div>
</main>
</div>
<Footer isHidden={isSearching || isNull(searchResults.packages)} />
</>
);
}
Example #5
Source File: Overlay.tsx From crosshare with GNU Affero General Public License v3.0 | 4 votes |
Overlay = (props: {
coverImage?: string | null;
onClick?: () => void;
hidden?: boolean;
closeCallback?: () => void;
children: React.ReactNode;
}) => {
const ref = useRef<HTMLElement | null>(null);
const [mounted, setMounted] = useState(false);
useEffect(() => {
ref.current = document.getElementById('modal');
if (!ref.current) {
ref.current = document.createElement('div');
ref.current.setAttribute('id', 'modal');
document.body.appendChild(ref.current);
}
setMounted(true);
}, []);
const overlay = (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
<div
onClick={props.onClick || (() => undefined)}
css={{
display: props.hidden ? 'none' : 'block',
position: 'fixed',
backgroundColor: 'var(--overlay-bg)',
top: 0,
left: 0,
width: '100%',
overflowY: 'scroll',
overscrollBehavior: 'contain',
height: '100%',
zIndex: 10000,
}}
>
<div
css={{
position: 'relative',
width: '95%',
margin: '1em auto',
[SMALL_AND_UP]: {
width: '90%',
margin: '2em auto',
},
maxWidth: '1200px',
backgroundColor: 'var(--overlay-inner)',
border: '1px solid black',
}}
>
{props.coverImage ? <CoverPic coverPicture={props.coverImage} /> : ''}
<div
css={{
padding: '3em 1.5em',
}}
>
{props.closeCallback ? (
<button
css={{
background: 'transparent',
color: 'var(--text)',
...(props.coverImage && { color: 'var(--social-text)' }),
border: 'none',
position: 'absolute',
padding: 0,
fontSize: '2.5em',
verticalAlign: 'text-top',
width: '1em',
height: '1em',
top: '0.5em',
right: '0.5em',
}}
onClick={props.closeCallback}
>
<IoMdCloseCircleOutline
aria-label="close"
css={{ position: 'absolute', top: 0, right: 0 }}
/>
</button>
) : (
''
)}
{props.children}
</div>
</div>
</div>
);
return mounted && ref.current ? createPortal(overlay, ref.current) : overlay;
}