Added easy access to search input on mobile pages
This commit is contained in:
		
							parent
							
								
									d560008cff
								
							
						
					
					
						commit
						22e1bf4842
					
				| @ -5,7 +5,7 @@ import { atoms } from "contexts/atoms"; | ||||
| import { isUndefined } from "helpers/asserts"; | ||||
| import { useAtomGetter } from "helpers/atoms"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { useScrollTopOnChange } from "hooks/useScrollTopOnChange"; | ||||
| import { useScrollTopOnChange } from "hooks/useScrollOnChange"; | ||||
| import { Ids } from "types/ids"; | ||||
| 
 | ||||
| /* | ||||
|  | ||||
							
								
								
									
										60
									
								
								src/components/Contents/FolderPath.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/components/Contents/FolderPath.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| import { useRef } from "react"; | ||||
| import { Button, TranslatedButton } from "components/Inputs/Button"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| import { ParentFolderPreviewFragment } from "graphql/generated"; | ||||
| import { useAtomSetter } from "helpers/atoms"; | ||||
| import { useScrollRightOnChange } from "hooks/useScrollOnChange"; | ||||
| import { Ids } from "types/ids"; | ||||
| import { filterHasAttributes } from "helpers/asserts"; | ||||
| import { prettySlug } from "helpers/formatters"; | ||||
| import { Ico } from "components/Ico"; | ||||
| 
 | ||||
| interface Props { | ||||
|   path: ParentFolderPreviewFragment[]; | ||||
| } | ||||
| 
 | ||||
| export const FolderPath = ({ path }: Props): JSX.Element => { | ||||
|   useScrollRightOnChange(Ids.ContentsFolderPath, [path]); | ||||
|   const setMenuGesturesEnabled = useAtomSetter(atoms.layout.menuGesturesEnabled); | ||||
|   const gestureReenableTimeout = useRef<NodeJS.Timeout>(); | ||||
| 
 | ||||
|   return ( | ||||
|     <div className="grid"> | ||||
|       <div | ||||
|         id={Ids.ContentsFolderPath} | ||||
|         onPointerEnter={() => { | ||||
|           if (gestureReenableTimeout.current) clearTimeout(gestureReenableTimeout.current); | ||||
|           setMenuGesturesEnabled(false); | ||||
|         }} | ||||
|         onPointerLeave={() => { | ||||
|           gestureReenableTimeout.current = setTimeout(() => setMenuGesturesEnabled(true), 500); | ||||
|         }} | ||||
|         className={`-mx-4 flex place-items-center justify-start gap-x-1 gap-y-4
 | ||||
|           overflow-x-auto px-4 pb-10 scrollbar-none`}>
 | ||||
|         {path.map((pathFolder, index) => ( | ||||
|           <> | ||||
|             {pathFolder.slug === "root" ? ( | ||||
|               <Button href="/contents" icon="home" active={index === path.length - 1} /> | ||||
|             ) : ( | ||||
|               <TranslatedButton | ||||
|                 className="w-max" | ||||
|                 href={`/contents/folder/${pathFolder.slug}`} | ||||
|                 translations={filterHasAttributes(pathFolder.titles, [ | ||||
|                   "language.data.attributes.code", | ||||
|                 ]).map((title) => ({ | ||||
|                   language: title.language.data.attributes.code, | ||||
|                   text: title.title, | ||||
|                 }))} | ||||
|                 fallback={{ | ||||
|                   text: prettySlug(pathFolder.slug), | ||||
|                 }} | ||||
|                 active={index === path.length - 1} | ||||
|               /> | ||||
|             )} | ||||
|             {index < path.length - 1 && <Ico icon="chevron_right" />} | ||||
|           </> | ||||
|         ))} | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| @ -2,10 +2,8 @@ import { useCallback } from "react"; | ||||
| import { Button } from "components/Inputs/Button"; | ||||
| import { TranslatedProps } from "types/TranslatedProps"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| import { isUndefined } from "helpers/asserts"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| import { useAtomGetter } from "helpers/atoms"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { cJoin } from "helpers/className"; | ||||
| 
 | ||||
| /* | ||||
|  *                                        ╭─────────────╮ | ||||
| @ -15,27 +13,18 @@ import { useFormat } from "hooks/useFormat"; | ||||
| interface Props { | ||||
|   href: string; | ||||
|   title: string | null | undefined; | ||||
| 
 | ||||
|   displayOnlyOn?: "1ColumnLayout" | "3ColumnsLayout"; | ||||
|   className?: string; | ||||
| } | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props): JSX.Element => { | ||||
| export const ReturnButton = ({ href, title, className }: Props): JSX.Element => { | ||||
|   const { format } = useFormat(); | ||||
|   const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout); | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       {((is3ColumnsLayout && displayOnlyOn === "3ColumnsLayout") || | ||||
|         (!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") || | ||||
|         isUndefined(displayOnlyOn)) && ( | ||||
|         <div className={className}> | ||||
|           <Button href={href} text={format("return_to_x", { x: title })} icon="navigate_before" /> | ||||
|         </div> | ||||
|       )} | ||||
|     </> | ||||
|     <div className={cJoin("mx-auto w-full max-w-lg place-self-center", className)}> | ||||
|       <Button href={href} text={format("return_to_x", { x: title })} icon="navigate_before" /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { Fragment, useCallback } from "react"; | ||||
| import { useCallback } from "react"; | ||||
| import { AppLayout, AppLayoutRequired } from "./AppLayout"; | ||||
| import { getTocFromMarkdawn, Markdawn, TableOfContents } from "./Markdown/Markdawn"; | ||||
| import { ReturnButton } from "./PanelComponents/ReturnButton"; | ||||
| @ -91,13 +91,8 @@ export const PostPage = ({ | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       {returnHref && returnTitle && ( | ||||
|         <ReturnButton | ||||
|           href={returnHref} | ||||
|           title={returnTitle} | ||||
|           displayOnlyOn={"1ColumnLayout"} | ||||
|           className="mb-10" | ||||
|         /> | ||||
|       {is1ColumnLayout && returnHref && returnTitle && ( | ||||
|         <ReturnButton href={returnHref} title={returnTitle} className="mb-10" /> | ||||
|       )} | ||||
| 
 | ||||
|       {displayThumbnailHeader ? ( | ||||
| @ -109,6 +104,7 @@ export const PostPage = ({ | ||||
|             categories={filterHasAttributes(post.categories?.data, ["attributes"]).map((category) => | ||||
|               formatCategory(category.attributes.slug) | ||||
|             )} | ||||
|             releaseDate={post.date} | ||||
|             languageSwitcher={ | ||||
|               languageSwitcherProps.locales.size > 1 ? ( | ||||
|                 <LanguageSwitcher {...languageSwitcherProps} /> | ||||
|  | ||||
| @ -2,7 +2,7 @@ import { Chip } from "components/Chip"; | ||||
| import { Img } from "components/Img"; | ||||
| import { InsetBox } from "components/Containers/InsetBox"; | ||||
| import { Markdawn } from "components/Markdown/Markdawn"; | ||||
| import { UploadImageFragment } from "graphql/generated"; | ||||
| import { DatePickerFragment, UploadImageFragment } from "graphql/generated"; | ||||
| import { prettyInlineTitle, slugify } from "helpers/formatters"; | ||||
| import { ImageQuality } from "helpers/img"; | ||||
| import { useAtomGetter } from "helpers/atoms"; | ||||
| @ -21,6 +21,7 @@ interface Props { | ||||
|   description?: string | null | undefined; | ||||
|   type?: string; | ||||
|   categories?: string[]; | ||||
|   releaseDate?: DatePickerFragment; | ||||
|   thumbnail?: UploadImageFragment | null | undefined; | ||||
|   className?: string; | ||||
|   languageSwitcher?: JSX.Element; | ||||
| @ -37,9 +38,10 @@ export const ThumbnailHeader = ({ | ||||
|   categories, | ||||
|   description, | ||||
|   languageSwitcher, | ||||
|   releaseDate, | ||||
|   className, | ||||
| }: Props): JSX.Element => { | ||||
|   const { format } = useFormat(); | ||||
|   const { format, formatDate } = useFormat(); | ||||
|   const { showLightBox } = useAtomGetter(atoms.lightBox); | ||||
| 
 | ||||
|   return ( | ||||
| @ -76,6 +78,15 @@ export const ThumbnailHeader = ({ | ||||
|           </div> | ||||
|         )} | ||||
| 
 | ||||
|         {releaseDate && ( | ||||
|           <div className="flex flex-col place-items-center gap-2"> | ||||
|             <h3 className="text-xl">{format("release_date")}</h3> | ||||
|             <div className="flex flex-row flex-wrap"> | ||||
|               <Chip text={formatDate(releaseDate)} /> | ||||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
| 
 | ||||
|         {categories && categories.length > 0 && ( | ||||
|           <div className="flex flex-col place-items-center gap-2"> | ||||
|             <h3 className="text-xl">{format("category", { count: categories.length })}</h3> | ||||
|  | ||||
| @ -14,3 +14,15 @@ export const useScrollTopOnChange = (id: Ids, deps: DependencyList, enabled = tr | ||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|   }, [id, ...deps, enabled]); | ||||
| }; | ||||
| 
 | ||||
| // Scroll to top of element "id" when "deps" update.
 | ||||
| export const useScrollRightOnChange = (id: Ids, deps: DependencyList, enabled = true): void => { | ||||
|   useEffect(() => { | ||||
|     if (enabled) { | ||||
|       logger.log("Change detected. Scrolling to right"); | ||||
|       const elem = document.querySelector(`#${CSS.escape(id)}`); | ||||
|       elem?.scrollTo({ left: elem.scrollWidth, behavior: "smooth" }); | ||||
|     } | ||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|   }, [id, ...deps, enabled]); | ||||
| }; | ||||
| @ -27,6 +27,8 @@ import { getReadySdk } from "graphql/sdk"; | ||||
| import { Paginator } from "components/Containers/Paginator"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { getFormat } from "helpers/i18n"; | ||||
| import { useAtomGetter } from "helpers/atoms"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| 
 | ||||
| /* | ||||
|  *                                         ╭─────────────╮ | ||||
| @ -63,6 +65,7 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => { | ||||
|   const { format } = useFormat(); | ||||
|   const hoverable = useDeviceSupportsHover(); | ||||
|   const router = useTypedRouter(queryParamSchema); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
| 
 | ||||
|   const sortingMethods = useMemo( | ||||
|     () => [ | ||||
| @ -147,14 +150,27 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => { | ||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|   }, [router.isReady]); | ||||
| 
 | ||||
|   const searchInput = ( | ||||
|     <TextInput | ||||
|       placeholder={format("search_placeholder")} | ||||
|       value={query} | ||||
|       onChange={(newQuery) => { | ||||
|         setPage(1); | ||||
|         setQuery(newQuery); | ||||
|         if (isDefinedAndNotEmpty(newQuery)) { | ||||
|           sendAnalytics("Videos/Channel", "Change search term"); | ||||
|         } else { | ||||
|           sendAnalytics("Videos/Channel", "Clear search term"); | ||||
|         } | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/archives/videos" | ||||
|         title={format("videos")} | ||||
|         displayOnlyOn={"3ColumnsLayout"} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {!is1ColumnLayout && ( | ||||
|         <ReturnButton href="/archives/videos" title={format("videos")} className="mb-10" /> | ||||
|       )} | ||||
| 
 | ||||
|       <PanelHeader | ||||
|         icon="movie" | ||||
| @ -166,20 +182,7 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       <TextInput | ||||
|         className="mb-6 w-full" | ||||
|         placeholder={format("search_placeholder")} | ||||
|         value={query} | ||||
|         onChange={(newQuery) => { | ||||
|           setPage(1); | ||||
|           setQuery(newQuery); | ||||
|           if (isDefinedAndNotEmpty(newQuery)) { | ||||
|             sendAnalytics("Videos", "Change search term"); | ||||
|           } else { | ||||
|             sendAnalytics("Videos", "Clear search term"); | ||||
|           } | ||||
|         }} | ||||
|       /> | ||||
|       {!is1ColumnLayout && <div className="mb-6">{searchInput}</div>} | ||||
| 
 | ||||
|       <WithLabel label={format("order_by")}> | ||||
|         <Select | ||||
| @ -190,7 +193,7 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => { | ||||
|             setPage(1); | ||||
|             setSortingMethod(newSort); | ||||
|             sendAnalytics( | ||||
|               "Videos", | ||||
|               "Videos/Channel", | ||||
|               `Change sorting method (${ | ||||
|                 sortingMethods.map((item) => item.meiliAttribute)[newSort] | ||||
|               })` | ||||
| @ -224,7 +227,7 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => { | ||||
|           setQuery(DEFAULT_FILTERS_STATE.searchName); | ||||
|           setSortingMethod(DEFAULT_FILTERS_STATE.sortingMethod); | ||||
|           setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible); | ||||
|           sendAnalytics("Videos", "Reset all filters"); | ||||
|           sendAnalytics("Videos/Channel", "Reset all filters"); | ||||
|         }} | ||||
|       /> | ||||
|     </SubPanel> | ||||
| @ -232,6 +235,7 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {is1ColumnLayout && <div className="mx-auto mb-12 max-w-lg">{searchInput}</div>} | ||||
|       <Paginator page={page} onPageChange={setPage} totalNumberOfPages={videos?.totalPages}> | ||||
|         <div | ||||
|           className="grid grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] items-start | ||||
|  | ||||
| @ -25,6 +25,8 @@ import { Button } from "components/Inputs/Button"; | ||||
| import { Paginator } from "components/Containers/Paginator"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { getFormat } from "helpers/i18n"; | ||||
| import { useAtomGetter } from "helpers/atoms"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| 
 | ||||
| /* | ||||
|  *                                         ╭─────────────╮ | ||||
| @ -57,6 +59,7 @@ const Videos = ({ ...otherProps }: Props): JSX.Element => { | ||||
|   const { format } = useFormat(); | ||||
|   const hoverable = useDeviceSupportsHover(); | ||||
|   const router = useTypedRouter(queryParamSchema); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
| 
 | ||||
|   const sortingMethods = useMemo( | ||||
|     () => [ | ||||
| @ -141,14 +144,25 @@ const Videos = ({ ...otherProps }: Props): JSX.Element => { | ||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|   }, [router.isReady]); | ||||
| 
 | ||||
|   const searchInput = ( | ||||
|     <TextInput | ||||
|       placeholder={format("search_placeholder")} | ||||
|       value={query} | ||||
|       onChange={(newQuery) => { | ||||
|         setPage(1); | ||||
|         setQuery(newQuery); | ||||
|         if (isDefinedAndNotEmpty(newQuery)) { | ||||
|           sendAnalytics("Videos", "Change search term"); | ||||
|         } else { | ||||
|           sendAnalytics("Videos", "Clear search term"); | ||||
|         } | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/archives/" | ||||
|         title={"Archives"} | ||||
|         displayOnlyOn={"3ColumnsLayout"} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {!is1ColumnLayout && <ReturnButton href="/archives/" title={"Archives"} className="mb-10" />} | ||||
| 
 | ||||
|       <PanelHeader | ||||
|         icon="movie" | ||||
| @ -158,20 +172,7 @@ const Videos = ({ ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       <TextInput | ||||
|         className="mb-6 w-full" | ||||
|         placeholder={format("search_placeholder")} | ||||
|         value={query} | ||||
|         onChange={(newQuery) => { | ||||
|           setPage(1); | ||||
|           setQuery(newQuery); | ||||
|           if (isDefinedAndNotEmpty(newQuery)) { | ||||
|             sendAnalytics("Videos", "Change search term"); | ||||
|           } else { | ||||
|             sendAnalytics("Videos", "Clear search term"); | ||||
|           } | ||||
|         }} | ||||
|       /> | ||||
|       {!is1ColumnLayout && <div className="mb-6">{searchInput}</div>} | ||||
| 
 | ||||
|       <WithLabel label={format("order_by")}> | ||||
|         <Select | ||||
| @ -226,6 +227,7 @@ const Videos = ({ ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {is1ColumnLayout && <div className="mx-auto mb-12 max-w-lg">{searchInput}</div>} | ||||
|       <Paginator page={page} onPageChange={setPage} totalNumberOfPages={videos?.totalPages}> | ||||
|         <div | ||||
|           className="grid grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] items-start | ||||
|  | ||||
| @ -34,19 +34,19 @@ interface Props extends AppLayoutRequired { | ||||
| 
 | ||||
| const Video = ({ video, ...otherProps }: Props): JSX.Element => { | ||||
|   const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
|   const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); | ||||
|   const closeSubPanel = useCallback(() => setSubPanelOpened(false), [setSubPanelOpened]); | ||||
|   const { format, formatDate } = useFormat(); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/archives/videos/" | ||||
|         title={format("videos")} | ||||
|         displayOnlyOn={"3ColumnsLayout"} | ||||
|       /> | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
|       {!is1ColumnLayout && ( | ||||
|         <> | ||||
|           <ReturnButton href="/archives/videos/" title={format("videos")} /> | ||||
|           <HorizontalLine /> | ||||
|         </> | ||||
|       )} | ||||
| 
 | ||||
|       <NavOption title={format("video")} url="#video" border onClick={closeSubPanel} /> | ||||
|       <NavOption title={format("channel")} url="#channel" border onClick={closeSubPanel} /> | ||||
| @ -56,12 +56,9 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <ReturnButton | ||||
|         href="/library/" | ||||
|         title={format("library")} | ||||
|         displayOnlyOn={"1ColumnLayout"} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {is1ColumnLayout && ( | ||||
|         <ReturnButton href="/library/" title={format("library")} className="mb-10" /> | ||||
|       )} | ||||
| 
 | ||||
|       <div className="grid place-items-center gap-12"> | ||||
|         <div id="video" className="w-full overflow-hidden rounded-xl shadow-xl shadow-shade/80"> | ||||
|  | ||||
| @ -16,12 +16,14 @@ import { ReturnButton } from "components/PanelComponents/ReturnButton"; | ||||
| import { getOpenGraph } from "helpers/openGraph"; | ||||
| import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales"; | ||||
| import { getDescription } from "helpers/description"; | ||||
| import { useScrollTopOnChange } from "hooks/useScrollTopOnChange"; | ||||
| import { useScrollTopOnChange } from "hooks/useScrollOnChange"; | ||||
| import { Ids } from "types/ids"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { getFormat } from "helpers/i18n"; | ||||
| import { ElementsSeparator } from "helpers/component"; | ||||
| import { ChroniclesLists } from "components/Chronicles/ChroniclesLists"; | ||||
| import { useAtomGetter } from "helpers/atoms"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| 
 | ||||
| /* | ||||
|  *                                           ╭────────╮ | ||||
| @ -35,6 +37,7 @@ interface Props extends AppLayoutRequired { | ||||
| 
 | ||||
| const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element => { | ||||
|   const { format, formatContentType, formatCategory } = useFormat(); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
|   useScrollTopOnChange(Ids.ContentPanel, [chronicle.slug]); | ||||
| 
 | ||||
|   const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({ | ||||
| @ -67,24 +70,22 @@ const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element = | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         displayOnlyOn={"3ColumnsLayout"} | ||||
|         href="/chronicles" | ||||
|         title={format("chronicles")} | ||||
|       /> | ||||
|       <HorizontalLine /> | ||||
|       {!is1ColumnLayout && ( | ||||
|         <> | ||||
|           <ReturnButton href="/chronicles" title={format("chronicles")} /> | ||||
|           <HorizontalLine /> | ||||
|         </> | ||||
|       )} | ||||
| 
 | ||||
|       <ChroniclesLists chapters={chapters} currentChronicleSlug={chronicle.slug} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         displayOnlyOn={"1ColumnLayout"} | ||||
|         href="/chronicles" | ||||
|         title={format("chronicles")} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {is1ColumnLayout && ( | ||||
|         <ReturnButton href="/chronicles" title={format("chronicles")} className="mb-10" /> | ||||
|       )} | ||||
| 
 | ||||
|       {isDefined(selectedTranslation) ? ( | ||||
|         <> | ||||
|  | ||||
| @ -14,7 +14,7 @@ import { prettyInlineTitle, prettySlug } from "helpers/formatters"; | ||||
| import { isUntangibleGroupItem } from "helpers/libraryItem"; | ||||
| import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts"; | ||||
| import { ContentWithTranslations } from "types/types"; | ||||
| import { useScrollTopOnChange } from "hooks/useScrollTopOnChange"; | ||||
| import { useScrollTopOnChange } from "hooks/useScrollOnChange"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| import { getOpenGraph } from "helpers/openGraph"; | ||||
| import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales"; | ||||
| @ -226,11 +226,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <TranslatedReturnButton | ||||
|         {...returnButtonProps} | ||||
|         displayOnlyOn="1ColumnLayout" | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {is1ColumnLayout && <TranslatedReturnButton {...returnButtonProps} className="mb-10" />} | ||||
| 
 | ||||
|       <div className="grid place-items-center"> | ||||
|         <ElementsSeparator | ||||
|  | ||||
| @ -64,6 +64,7 @@ const Contents = (props: Props): JSX.Element => { | ||||
|   const { format, formatCategory, formatContentType, formatLanguage } = useFormat(); | ||||
|   const router = useTypedRouter(queryParamSchema); | ||||
|   const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
| 
 | ||||
|   const sortingMethods = useMemo( | ||||
|     () => [ | ||||
| @ -152,6 +153,22 @@ const Contents = (props: Props): JSX.Element => { | ||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|   }, [router.isReady]); | ||||
| 
 | ||||
|   const searchInput = ( | ||||
|     <TextInput | ||||
|       placeholder={format("search_placeholder")} | ||||
|       value={query} | ||||
|       onChange={(name) => { | ||||
|         setPage(1); | ||||
|         setQuery(name); | ||||
|         if (isDefinedAndNotEmpty(name)) { | ||||
|           sendAnalytics("Contents/All", "Change search term"); | ||||
|         } else { | ||||
|           sendAnalytics("Contents/All", "Clear search term"); | ||||
|         } | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <PanelHeader | ||||
| @ -171,20 +188,8 @@ const Contents = (props: Props): JSX.Element => { | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       <TextInput | ||||
|         className="mb-6 w-full" | ||||
|         placeholder={format("search_placeholder")} | ||||
|         value={query} | ||||
|         onChange={(name) => { | ||||
|           setPage(1); | ||||
|           setQuery(name); | ||||
|           if (isDefinedAndNotEmpty(name)) { | ||||
|             sendAnalytics("Contents/All", "Change search term"); | ||||
|           } else { | ||||
|             sendAnalytics("Contents/All", "Clear search term"); | ||||
|           } | ||||
|         }} | ||||
|       /> | ||||
|       {!is1ColumnLayout && <div className="mb-6">{searchInput}</div>} | ||||
|        | ||||
| 
 | ||||
|       <WithLabel label={format("order_by")}> | ||||
|         <Select | ||||
| @ -252,6 +257,7 @@ const Contents = (props: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {is1ColumnLayout && <div className="mx-auto mb-12 max-w-lg">{searchInput}</div>} | ||||
|       <Paginator page={page} onPageChange={setPage} totalNumberOfPages={contents?.totalPages}> | ||||
|         <div | ||||
|           className="grid grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] items-start | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next"; | ||||
| import { useMemo } from "react"; | ||||
| import { AppLayout, AppLayoutRequired } from "components/AppLayout"; | ||||
| import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel"; | ||||
| import { getOpenGraph } from "helpers/openGraph"; | ||||
| @ -8,8 +7,7 @@ import { filterHasAttributes } from "helpers/asserts"; | ||||
| import { GetContentsFolderQuery, ParentFolderPreviewFragment } from "graphql/generated"; | ||||
| import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales"; | ||||
| import { prettySlug } from "helpers/formatters"; | ||||
| import { Ico } from "components/Ico"; | ||||
| import { Button, TranslatedButton } from "components/Inputs/Button"; | ||||
| import { Button } from "components/Inputs/Button"; | ||||
| import { PanelHeader } from "components/PanelComponents/PanelHeader"; | ||||
| import { SubPanel } from "components/Containers/SubPanel"; | ||||
| import { TranslatedPreviewCard } from "components/PreviewCard"; | ||||
| @ -21,6 +19,7 @@ import { TranslatedPreviewFolder } from "components/Contents/PreviewFolder"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { getFormat } from "helpers/i18n"; | ||||
| import { Chip } from "components/Chip"; | ||||
| import { FolderPath } from "components/Contents/FolderPath"; | ||||
| 
 | ||||
| /* | ||||
|  *                                           ╭────────╮ | ||||
| @ -39,14 +38,6 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX. | ||||
|   const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); | ||||
|   const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl); | ||||
| 
 | ||||
|   const filteredPath = useMemo( | ||||
|     () => | ||||
|       path.filter( | ||||
|         (_, index) => isContentPanelAtLeast4xl || index === 0 || index === path.length - 1 | ||||
|       ), | ||||
|     [path, isContentPanelAtLeast4xl] | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <PanelHeader | ||||
| @ -68,30 +59,7 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX. | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <div className="mb-10 flex flex-wrap place-items-center justify-start gap-x-1 gap-y-4"> | ||||
|         {filteredPath.map((pathFolder, index) => ( | ||||
|           <> | ||||
|             {pathFolder.slug === "root" ? ( | ||||
|               <Button href="/contents" icon="home" active={index === filteredPath.length - 1} /> | ||||
|             ) : ( | ||||
|               <TranslatedButton | ||||
|                 href={`/contents/folder/${pathFolder.slug}`} | ||||
|                 translations={filterHasAttributes(pathFolder.titles, [ | ||||
|                   "language.data.attributes.code", | ||||
|                 ]).map((title) => ({ | ||||
|                   language: title.language.data.attributes.code, | ||||
|                   text: title.title, | ||||
|                 }))} | ||||
|                 fallback={{ | ||||
|                   text: prettySlug(pathFolder.slug), | ||||
|                 }} | ||||
|                 active={index === filteredPath.length - 1} | ||||
|               /> | ||||
|             )} | ||||
|             {index < filteredPath.length - 1 && <Ico icon="chevron_right" />} | ||||
|           </> | ||||
|         ))} | ||||
|       </div> | ||||
|       <FolderPath path={path} /> | ||||
| 
 | ||||
|       {folder.subfolders?.data && folder.subfolders.data.length > 0 && ( | ||||
|         <div className="mb-8"> | ||||
| @ -101,7 +69,7 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX. | ||||
|           </div> | ||||
|           <div | ||||
|             className={cJoin( | ||||
|               "grid items-start gap-8 pb-12", | ||||
|               "grid items-start pb-12", | ||||
|               cIf( | ||||
|                 isContentPanelAtLeast4xl, | ||||
|                 "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8", | ||||
| @ -133,7 +101,7 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX. | ||||
|           </div> | ||||
|           <div | ||||
|             className={cJoin( | ||||
|               "grid items-start gap-8 pb-12", | ||||
|               "grid items-start pb-12", | ||||
|               cIf( | ||||
|                 isContentPanelAtLeast4xl, | ||||
|                 "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8", | ||||
|  | ||||
| @ -29,7 +29,7 @@ import { | ||||
|   isDefined, | ||||
|   isDefinedAndNotEmpty, | ||||
| } from "helpers/asserts"; | ||||
| import { useScrollTopOnChange } from "hooks/useScrollTopOnChange"; | ||||
| import { useScrollTopOnChange } from "hooks/useScrollOnChange"; | ||||
| import { getScanArchiveURL, getTrackURL, isUntangibleGroupItem } from "helpers/libraryItem"; | ||||
| import { useDeviceSupportsHover } from "hooks/useMediaQuery"; | ||||
| import { WithLabel } from "components/Inputs/WithLabel"; | ||||
| @ -95,7 +95,7 @@ const LibrarySlug = ({ | ||||
| 
 | ||||
|   const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl); | ||||
|   const isContentPanelAtLeastSm = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastSm); | ||||
|   const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
| 
 | ||||
|   const hoverable = useDeviceSupportsHover(); | ||||
|   const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(false); | ||||
| @ -111,13 +111,8 @@ const LibrarySlug = ({ | ||||
|     <SubPanel> | ||||
|       <ElementsSeparator> | ||||
|         {[ | ||||
|           is3ColumnsLayout && ( | ||||
|             <ReturnButton | ||||
|               key="ReturnButton" | ||||
|               href="/library/" | ||||
|               title={format("library")} | ||||
|               displayOnlyOn="3ColumnsLayout" | ||||
|             /> | ||||
|           !is1ColumnLayout && ( | ||||
|             <ReturnButton key="ReturnButton" href="/library/" title={format("library")} /> | ||||
|           ), | ||||
|           <div className="grid gap-4" key="NavOption"> | ||||
|             <NavOption | ||||
| @ -177,12 +172,10 @@ const LibrarySlug = ({ | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <ReturnButton | ||||
|         href="/library/" | ||||
|         title={format("library")} | ||||
|         displayOnlyOn="1ColumnLayout" | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {is1ColumnLayout && ( | ||||
|         <ReturnButton href="/library/" title={format("library")} className="mb-10" /> | ||||
|       )} | ||||
| 
 | ||||
|       <div className="grid place-items-center gap-12"> | ||||
|         <div | ||||
|           className={cJoin( | ||||
|  | ||||
| @ -33,6 +33,8 @@ import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus"; | ||||
| import { Paginator } from "components/Containers/Paginator"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { getFormat } from "helpers/i18n"; | ||||
| import { useAtomGetter } from "helpers/atoms"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| 
 | ||||
| /* | ||||
|  *                                         ╭─────────────╮ | ||||
| @ -71,6 +73,7 @@ const Library = (props: Props): JSX.Element => { | ||||
|   const hoverable = useDeviceSupportsHover(); | ||||
|   const { format, formatCategory, formatLibraryItemSubType } = useFormat(); | ||||
|   const { libraryItemUserStatus } = useLibraryItemUserStatus(); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
| 
 | ||||
|   const sortingMethods = useMemo( | ||||
|     () => [ | ||||
| @ -234,6 +237,22 @@ const Library = (props: Props): JSX.Element => { | ||||
|     if (isDefined(totalPages) && totalPages < page && totalPages >= 1) setPage(totalPages); | ||||
|   }, [libraryItems?.totalPages, page]); | ||||
| 
 | ||||
|   const searchInput = ( | ||||
|     <TextInput | ||||
|       placeholder={format("search_placeholder")} | ||||
|       value={query} | ||||
|       onChange={(name) => { | ||||
|         setPage(1); | ||||
|         setQuery(name); | ||||
|         if (isDefinedAndNotEmpty(name)) { | ||||
|           sendAnalytics("Library", "Change search term"); | ||||
|         } else { | ||||
|           sendAnalytics("Library", "Clear search term"); | ||||
|         } | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <PanelHeader | ||||
| @ -244,20 +263,7 @@ const Library = (props: Props): JSX.Element => { | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       <TextInput | ||||
|         className="mb-6 w-full" | ||||
|         placeholder={format("search_placeholder")} | ||||
|         value={query} | ||||
|         onChange={(name) => { | ||||
|           setPage(1); | ||||
|           setQuery(name); | ||||
|           if (isDefinedAndNotEmpty(name)) { | ||||
|             sendAnalytics("Library", "Change search term"); | ||||
|           } else { | ||||
|             sendAnalytics("Library", "Clear search term"); | ||||
|           } | ||||
|         }} | ||||
|       /> | ||||
|       {!is1ColumnLayout && <div className="mb-6">{searchInput}</div>} | ||||
| 
 | ||||
|       <WithLabel label={format("order_by")}> | ||||
|         <Select | ||||
| @ -388,6 +394,7 @@ const Library = (props: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {is1ColumnLayout && <div className="mx-auto mb-12 max-w-lg">{searchInput}</div>} | ||||
|       <Paginator page={page} onPageChange={setPage} totalNumberOfPages={libraryItems?.totalPages}> | ||||
|         <div | ||||
|           className="grid grid-cols-[repeat(auto-fill,_minmax(12rem,1fr))] items-end | ||||
|  | ||||
| @ -63,6 +63,7 @@ const News = ({ ...otherProps }: Props): JSX.Element => { | ||||
|   const hoverable = useDeviceSupportsHover(); | ||||
|   const router = useTypedRouter(queryParamSchema); | ||||
|   const isTerminalMode = useAtomGetter(atoms.layout.terminalMode); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
| 
 | ||||
|   const [query, setQuery] = useState(router.query.query ?? DEFAULT_FILTERS_STATE.query); | ||||
| 
 | ||||
| @ -136,6 +137,22 @@ const News = ({ ...otherProps }: Props): JSX.Element => { | ||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|   }, [router.isReady]); | ||||
| 
 | ||||
|   const searchInput = ( | ||||
|     <TextInput | ||||
|       placeholder={format("search_placeholder")} | ||||
|       value={query} | ||||
|       onChange={(name) => { | ||||
|         setPage(1); | ||||
|         setQuery(name); | ||||
|         if (isDefinedAndNotEmpty(name)) { | ||||
|           sendAnalytics("News", "Change search term"); | ||||
|         } else { | ||||
|           sendAnalytics("News", "Clear search term"); | ||||
|         } | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <PanelHeader | ||||
| @ -146,20 +163,7 @@ const News = ({ ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       <TextInput | ||||
|         className="mb-6 w-full" | ||||
|         placeholder={format("search_placeholder")} | ||||
|         value={query} | ||||
|         onChange={(name) => { | ||||
|           setPage(1); | ||||
|           setQuery(name); | ||||
|           if (isDefinedAndNotEmpty(name)) { | ||||
|             sendAnalytics("News", "Change search term"); | ||||
|           } else { | ||||
|             sendAnalytics("News", "Clear search term"); | ||||
|           } | ||||
|         }} | ||||
|       /> | ||||
|       {!is1ColumnLayout && <div className="mb-6">{searchInput}</div>} | ||||
| 
 | ||||
|       <WithLabel label={format("language", { count: Infinity })}> | ||||
|         <Select | ||||
| @ -208,6 +212,7 @@ const News = ({ ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {is1ColumnLayout && <div className="mx-auto mb-12 max-w-lg">{searchInput}</div>} | ||||
|       <Paginator page={page} onPageChange={setPage} totalNumberOfPages={posts?.totalPages}> | ||||
|         <div | ||||
|           className="grid grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] items-start | ||||
|  | ||||
| @ -39,6 +39,7 @@ interface Props extends AppLayoutRequired { | ||||
| 
 | ||||
| const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => { | ||||
|   const { format, formatCategory, formatWikiTag } = useFormat(); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
|   const router = useRouter(); | ||||
|   const isTerminalMode = useAtomGetter(atoms.layout.terminalMode); | ||||
|   const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); | ||||
| @ -51,41 +52,30 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => { | ||||
|       [] | ||||
|     ), | ||||
|   }); | ||||
|   const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout); | ||||
| 
 | ||||
|   const toc = getTocFromMarkdawn(selectedTranslation?.body?.body, selectedTranslation?.title); | ||||
| 
 | ||||
|   const subPanel = is3ColumnsLayout ? ( | ||||
|     <SubPanel> | ||||
|       <ElementsSeparator> | ||||
|         {[ | ||||
|           <ReturnButton | ||||
|             key="return" | ||||
|             href={`/wiki`} | ||||
|             title={format("wiki")} | ||||
|             displayOnlyOn={"3ColumnsLayout"} | ||||
|           />, | ||||
|           toc && ( | ||||
|             <TableOfContents | ||||
|               key="toc" | ||||
|               toc={toc} | ||||
|               onContentClicked={() => setSubPanelOpened(false)} | ||||
|             /> | ||||
|           ), | ||||
|         ]} | ||||
|       </ElementsSeparator> | ||||
|     </SubPanel> | ||||
|   ) : undefined; | ||||
|   const subPanel = | ||||
|     toc || !is1ColumnLayout ? ( | ||||
|       <SubPanel> | ||||
|         <ElementsSeparator> | ||||
|           {[ | ||||
|             !is1ColumnLayout && <ReturnButton key="return" href={`/wiki`} title={format("wiki")} />, | ||||
|             toc && ( | ||||
|               <TableOfContents | ||||
|                 key="toc" | ||||
|                 toc={toc} | ||||
|                 onContentClicked={() => setSubPanelOpened(false)} | ||||
|               /> | ||||
|             ), | ||||
|           ]} | ||||
|         </ElementsSeparator> | ||||
|       </SubPanel> | ||||
|     ) : undefined; | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Large}> | ||||
|       <ReturnButton | ||||
|         href={`/wiki`} | ||||
|         title={format("wiki")} | ||||
|         displayOnlyOn={"1ColumnLayout"} | ||||
|         className="mb-10" | ||||
|       /> | ||||
| 
 | ||||
|       {is1ColumnLayout && <ReturnButton href={`/wiki`} title={format("wiki")} className="mb-10" />} | ||||
|       <div className="flex flex-wrap place-content-center gap-3"> | ||||
|         <h1 className="text-center text-3xl">{selectedTranslation?.title}</h1> | ||||
|         {selectedTranslation?.aliases && selectedTranslation.aliases.length > 0 && ( | ||||
| @ -103,7 +93,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => { | ||||
|             <div | ||||
|               className={cJoin( | ||||
|                 "mb-8 overflow-hidden rounded-lg bg-mid text-center", | ||||
|                 cIf(is3ColumnsLayout, "float-right ml-8 w-96") | ||||
|                 cIf(!is1ColumnLayout, "float-right ml-8 w-96") | ||||
|               )}> | ||||
|               {page.thumbnail?.data?.attributes && ( | ||||
|                 <Img | ||||
|  | ||||
| @ -27,7 +27,7 @@ import { useIntersectionList } from "hooks/useIntersectionList"; | ||||
| import { HorizontalLine } from "components/HorizontalLine"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { getFormat } from "helpers/i18n"; | ||||
| import { useAtomSetter } from "helpers/atoms"; | ||||
| import { useAtomGetter, useAtomSetter } from "helpers/atoms"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| 
 | ||||
| /* | ||||
| @ -43,6 +43,7 @@ interface Props extends AppLayoutRequired { | ||||
| const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props): JSX.Element => { | ||||
|   const { format } = useFormat(); | ||||
|   const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
|   const closeSubPanel = useCallback(() => setSubPanelOpened(false), [setSubPanelOpened]); | ||||
|   const ids = useMemo( | ||||
|     () => filterHasAttributes(chronologyEras, ["attributes"]).map((era) => era.attributes.slug), | ||||
| @ -53,7 +54,7 @@ const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props): | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton href="/wiki" title={format("wiki")} displayOnlyOn="3ColumnsLayout" /> | ||||
|       {!is1ColumnLayout && <ReturnButton href="/wiki" title={format("wiki")} />} | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
| @ -83,12 +84,7 @@ const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props): | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href="/wiki" | ||||
|         title={format("wiki")} | ||||
|         displayOnlyOn="1ColumnLayout" | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {is1ColumnLayout && <ReturnButton href="/wiki" title={format("wiki")} className="mb-10" />} | ||||
| 
 | ||||
|       {filterHasAttributes(chronologyEras, ["attributes"]).map((era) => ( | ||||
|         <TranslatedChronologyEra | ||||
|  | ||||
| @ -29,7 +29,7 @@ import { | ||||
| import { Paginator } from "components/Containers/Paginator"; | ||||
| import { useFormat } from "hooks/useFormat"; | ||||
| import { getFormat } from "helpers/i18n"; | ||||
| import { useAtomSetter } from "helpers/atoms"; | ||||
| import { useAtomGetter, useAtomSetter } from "helpers/atoms"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| import { Select } from "components/Inputs/Select"; | ||||
| 
 | ||||
| @ -64,6 +64,7 @@ const Wiki = (props: Props): JSX.Element => { | ||||
|   const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); | ||||
|   const closeSubPanel = useCallback(() => setSubPanelOpened(false), [setSubPanelOpened]); | ||||
|   const router = useTypedRouter(queryParamSchema); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
| 
 | ||||
|   const languageOptions = useMemo(() => { | ||||
|     const memo = | ||||
| @ -137,6 +138,22 @@ const Wiki = (props: Props): JSX.Element => { | ||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|   }, [router.isReady]); | ||||
| 
 | ||||
|   const searchInput = ( | ||||
|     <TextInput | ||||
|       placeholder={format("search_placeholder")} | ||||
|       value={query} | ||||
|       onChange={(name) => { | ||||
|         setPage(1); | ||||
|         setQuery(name); | ||||
|         if (isDefinedAndNotEmpty(name)) { | ||||
|           sendAnalytics("Wiki", "Change search term"); | ||||
|         } else { | ||||
|           sendAnalytics("Wiki", "Clear search term"); | ||||
|         } | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <PanelHeader | ||||
| @ -147,20 +164,7 @@ const Wiki = (props: Props): JSX.Element => { | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       <TextInput | ||||
|         className="mb-6 w-full" | ||||
|         placeholder={format("search_placeholder")} | ||||
|         value={query} | ||||
|         onChange={(name) => { | ||||
|           setPage(1); | ||||
|           setQuery(name); | ||||
|           if (isDefinedAndNotEmpty(name)) { | ||||
|             sendAnalytics("Wiki", "Change search term"); | ||||
|           } else { | ||||
|             sendAnalytics("Wiki", "Clear search term"); | ||||
|           } | ||||
|         }} | ||||
|       /> | ||||
|       {!is1ColumnLayout && <div className="mb-6">{searchInput}</div>} | ||||
| 
 | ||||
|       <WithLabel label={format("language", { count: Infinity })}> | ||||
|         <Select | ||||
| @ -226,6 +230,7 @@ const Wiki = (props: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       {is1ColumnLayout && <div className="mx-auto mb-12 max-w-lg">{searchInput}</div>} | ||||
|       <Paginator page={page} onPageChange={setPage} totalNumberOfPages={wikiPages?.totalPages}> | ||||
|         <div | ||||
|           className="grid grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] items-start | ||||
|  | ||||
| @ -73,17 +73,19 @@ const WeaponPage = ({ weapon, primaryName, aliases, ...otherProps }: Props): JSX | ||||
|   const currentIntersection = useIntersectionList(intersectionIds); | ||||
|   const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout); | ||||
| 
 | ||||
|   const searchInput = ( | ||||
|     <ReturnButton | ||||
|       key="return-button" | ||||
|       href="/wiki/weapons" | ||||
|       title={format("weapon", { count: Infinity })} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ElementsSeparator> | ||||
|         {[ | ||||
|           is3ColumnsLayout && ( | ||||
|             <ReturnButton | ||||
|               key="return-button" | ||||
|               href="/wiki/weapons" | ||||
|               title={format("weapon", { count: Infinity })} | ||||
|             /> | ||||
|           ), | ||||
|           is3ColumnsLayout && searchInput, | ||||
| 
 | ||||
|           <Fragment key="nav-options"> | ||||
|             {intersectionIds.map((id, index) => ( | ||||
| @ -126,12 +128,8 @@ const WeaponPage = ({ weapon, primaryName, aliases, ...otherProps }: Props): JSX | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href="/wiki/weapons" | ||||
|         title={format("weapon", { count: Infinity })} | ||||
|         displayOnlyOn="1ColumnLayout" | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {!is3ColumnsLayout && <div className="mb-10">{searchInput}</div>} | ||||
| 
 | ||||
|       <ThumbnailHeader | ||||
|         title={primaryName} | ||||
|         subtitle={aliases.join("・")} | ||||
|  | ||||
| @ -30,6 +30,8 @@ import { WithLabel } from "components/Inputs/WithLabel"; | ||||
| import { Switch } from "components/Inputs/Switch"; | ||||
| import { ReturnButton } from "components/PanelComponents/ReturnButton"; | ||||
| import { Select } from "components/Inputs/Select"; | ||||
| import { useAtomGetter } from "helpers/atoms"; | ||||
| import { atoms } from "contexts/atoms"; | ||||
| 
 | ||||
| /* | ||||
|  *                                         ╭─────────────╮ | ||||
| @ -60,6 +62,7 @@ const Weapons = (props: Props): JSX.Element => { | ||||
|   const { format, formatCategory, formatWeaponType, formatLanguage } = useFormat(); | ||||
|   const hoverable = useDeviceSupportsHover(); | ||||
|   const router = useTypedRouter(queryParamSchema); | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
| 
 | ||||
|   const languageOptions = useMemo(() => { | ||||
|     const memo = | ||||
| @ -132,36 +135,35 @@ const Weapons = (props: Props): JSX.Element => { | ||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|   }, [router.isReady]); | ||||
| 
 | ||||
|   const searchInput = ( | ||||
|     <TextInput | ||||
|       placeholder={format("search_placeholder")} | ||||
|       value={query} | ||||
|       onChange={(name) => { | ||||
|         setPage(1); | ||||
|         setQuery(name); | ||||
|         if (isDefinedAndNotEmpty(name)) { | ||||
|           sendAnalytics("Weapons", "Change search term"); | ||||
|         } else { | ||||
|           sendAnalytics("Weapons", "Clear search term"); | ||||
|         } | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/wiki" | ||||
|         title={format("wiki")} | ||||
|         displayOnlyOn="3ColumnsLayout" | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {!is1ColumnLayout && <ReturnButton href="/wiki" title={format("wiki")} className="mb-10" />} | ||||
| 
 | ||||
|       <PanelHeader | ||||
|         icon="shield" | ||||
|         title={format("weapon", { count: Infinity })} | ||||
|         description={format("weapons_description")} | ||||
|       /> | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       <TextInput | ||||
|         className="mb-6 w-full" | ||||
|         placeholder={format("search_placeholder")} | ||||
|         value={query} | ||||
|         onChange={(name) => { | ||||
|           setPage(1); | ||||
|           setQuery(name); | ||||
|           if (isDefinedAndNotEmpty(name)) { | ||||
|             sendAnalytics("Weapons", "Change search term"); | ||||
|           } else { | ||||
|             sendAnalytics("Weapons", "Clear search term"); | ||||
|           } | ||||
|         }} | ||||
|       /> | ||||
|       {!is1ColumnLayout && <div className="mb-6">{searchInput}</div>} | ||||
| 
 | ||||
|       <WithLabel label={format("language", { count: Infinity })}> | ||||
|         <Select | ||||
| @ -210,12 +212,12 @@ const Weapons = (props: Props): JSX.Element => { | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel width={ContentPanelWidthSizes.Full}> | ||||
|       <ReturnButton | ||||
|         href="/wiki" | ||||
|         title={format("wiki")} | ||||
|         displayOnlyOn="1ColumnLayout" | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {is1ColumnLayout && ( | ||||
|         <> | ||||
|           <ReturnButton href="/wiki" title={format("wiki")} className="mb-6" /> | ||||
|           <div className="mx-auto mb-12 max-w-lg">{searchInput}</div> | ||||
|         </> | ||||
|       )} | ||||
| 
 | ||||
|       <Paginator page={page} onPageChange={setPage} totalNumberOfPages={weapons?.totalPages}> | ||||
|         <div | ||||
|  | ||||
| @ -3,4 +3,5 @@ export enum Ids { | ||||
|   ContentPanel = "contentPanel495922447721572", | ||||
|   SubPanel = "subPanelz9e8rs2d3f18zer98ze", | ||||
|   LightBox = "lightBoxqsd564az89e732s1", | ||||
|   ContentsFolderPath = "contentsfolderpath8zer6az4", | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint