diff --git a/index.css b/index.css
index 5665affb..fce334da 100644
--- a/index.css
+++ b/index.css
@@ -4,8 +4,7 @@
@layer base {
svg {
- vertical-align: middle;
- fill: currentColor;
+ @apply align-middle fill-current;
}
}
diff --git a/package.json b/package.json
index 45e40875..60e5fa31 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,11 @@
{
"name": "webdevhome.github.io",
- "version": "2.1.0",
+ "version": "2.2.0",
"scripts": {
"dev": "vite",
- "build": "tsc && vite build",
+ "build": "npm test && vite build",
"preview": "vite preview",
- "typecheck": "tsc"
+ "test": "tsc && eslint src"
},
"dependencies": {
"@mdi/react": "^1.4.0",
diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx
index efa6ef38..82f9a9f1 100644
--- a/src/components/App/App.tsx
+++ b/src/components/App/App.tsx
@@ -1,4 +1,5 @@
import {
+ mdiArrowCollapseUp,
mdiArrowLeft,
mdiCheck,
mdiFormatListChecks,
@@ -17,6 +18,7 @@ import { FooterDivider } from '../Footer/FooterDivider'
import { FooterGroup } from '../Footer/FooterGroup'
import { AppAction } from '../Header/AppAction'
import { AppHeader } from '../Header/AppHeader'
+import { JumpLinks } from '../JumpLinks/JumpLinks'
import { LinkGroup } from '../Links/LinkGroup'
import { Search } from '../Search/Search'
import { AppContent } from './AppContent'
@@ -34,6 +36,13 @@ export const WebdevHome: FC = () => {
const allLinks = useAllLinks()
const hiddenLinksCount = useHiddenLinksCount()
+ function handleScrollTopClick() {
+ const htmlEl = document.children.item(0)
+ if (htmlEl === null) return
+
+ htmlEl.scrollTo({ top: 0, behavior: 'smooth' })
+ }
+
return (
{
<>
{isCurrentAppMode(AppMode.default) ? (
<>
+
{
{
{isCurrentAppMode(AppMode.default, AppMode.customize) ? (
-
- {links.items.map((group) => (
-
- ))}
-
+ <>
+
+
+ {links.items.map((group, index) => (
+
+ ))}
+
+ >
) : (
)}
diff --git a/src/components/App/AppContent.tsx b/src/components/App/AppContent.tsx
index c4b981c2..b12ff13a 100644
--- a/src/components/App/AppContent.tsx
+++ b/src/components/App/AppContent.tsx
@@ -6,7 +6,7 @@ export const AppContent: FC
= ({ children }) => {
diff --git a/src/components/Header/AppAction.tsx b/src/components/Header/AppAction.tsx
index 6ec49a50..fbb5a617 100644
--- a/src/components/Header/AppAction.tsx
+++ b/src/components/Header/AppAction.tsx
@@ -20,7 +20,7 @@ export const AppAction: FC
= ({
return (
= ({
>
-
{label}
+
{label}
)
}
diff --git a/src/components/Header/AppHeader.tsx b/src/components/Header/AppHeader.tsx
index 71f047f2..58d11488 100644
--- a/src/components/Header/AppHeader.tsx
+++ b/src/components/Header/AppHeader.tsx
@@ -11,7 +11,7 @@ export const AppHeader: FC = ({ actions }) => {
@@ -20,7 +20,7 @@ export const AppHeader: FC
= ({ actions }) => {
{actions !== null ? (
diff --git a/src/components/Header/Logo.tsx b/src/components/Header/Logo.tsx
index 44b9eaff..c0c7e95b 100644
--- a/src/components/Header/Logo.tsx
+++ b/src/components/Header/Logo.tsx
@@ -7,8 +7,9 @@ export const Logo: FC = () => {
className={classNames(
'font-mono text-2xl',
'tracking-wide',
- 'text-center md:text-left text-gray-400 dark:text-gray-200',
- 'pt-2 md:pt-0',
+ 'text-center lg:text-left text-gray-400 dark:text-gray-200',
+ 'pt-2 lg:pt-0',
+ 'text-nowrap',
)}
>
<
diff --git a/src/components/JumpLinks/JumpLink.tsx b/src/components/JumpLinks/JumpLink.tsx
new file mode 100644
index 00000000..12a7d3d5
--- /dev/null
+++ b/src/components/JumpLinks/JumpLink.tsx
@@ -0,0 +1,34 @@
+import classNames from 'classnames'
+import { FC } from 'react'
+import { slugify } from '../../utils/slugify'
+
+interface Props {
+ label: string
+ color?: string
+}
+
+export const JumpLink: FC
= ({ label, color = 'gray' }) => {
+ function handleClick() {
+ const target = document.getElementById(slugify(label))
+ if (target === null) return
+
+ target.scrollIntoView({ behavior: 'smooth' })
+ }
+
+ return (
+
+ {label}
+
+ )
+}
diff --git a/src/components/JumpLinks/JumpLinks.tsx b/src/components/JumpLinks/JumpLinks.tsx
new file mode 100644
index 00000000..ea2a375b
--- /dev/null
+++ b/src/components/JumpLinks/JumpLinks.tsx
@@ -0,0 +1,45 @@
+import { mdiChevronDown, mdiChevronUp } from '@mdi/js'
+import classNames from 'classnames'
+import { FC, useState } from 'react'
+import { MdiIcon } from '../Icon/MdiIcon'
+import { JumpLink } from './JumpLink'
+import { links } from '../../links'
+import { useAllLinksInGroupAreHidden } from '../../stores/hiddenLinks/hiddenLinksHooks'
+
+export const JumpLinks: FC = () => {
+ const allLinksInGroupAreHidden = useAllLinksInGroupAreHidden()
+
+ const [isOpen, setIsOpen] = useState(false)
+
+ function handleToggleClick() {
+ setIsOpen(!isOpen)
+ }
+
+ return (
+
+
+
+ {links.items
+ .filter((group) => !allLinksInGroupAreHidden(group))
+ .map((linkGroup, index) => (
+
+ ))}
+
+
+ )
+}
diff --git a/src/components/Links/LinkGroup.tsx b/src/components/Links/LinkGroup.tsx
index c8a423b3..2430399b 100644
--- a/src/components/Links/LinkGroup.tsx
+++ b/src/components/Links/LinkGroup.tsx
@@ -13,6 +13,7 @@ import {
useAllLinksInGroupAreHidden,
useGetIsLinkHidden,
} from '../../stores/hiddenLinks/hiddenLinksHooks'
+import { slugify } from '../../utils/slugify'
import { MdiIcon } from '../Icon/MdiIcon'
import { Link } from './Link'
import { LinkGroupButton } from './LinkGroupButton'
@@ -34,20 +35,20 @@ export const LinkGroup: FC = ({ group }) => {
}, [group.items, getIsLinkHidden])
const allGroupLinksAreHidden = useMemo(
- () => allLinksInGroupAreHidden(group.items.map((link) => link.url)),
- [allLinksInGroupAreHidden, group.items],
+ () => allLinksInGroupAreHidden(group),
+ [allLinksInGroupAreHidden, group],
)
const handleToggleGroupClick = useCallback(
(...items: LinkItem[]): void => {
- dispatch(toggleHiddenLinksGroup(items.map((link) => link.url)))
+ dispatch(toggleHiddenLinksGroup(items))
},
[dispatch],
)
const noVisibleLinksInGroup = useMemo(() => {
- return allLinksInGroupAreHidden(group.items.map((link) => link.url))
- }, [allLinksInGroupAreHidden, group.items])
+ return allLinksInGroupAreHidden(group)
+ }, [allLinksInGroupAreHidden, group])
const showHiddenLinksButtonLabel = useMemo(() => {
const hiddenLinksCount = hiddenLinks.length
@@ -67,14 +68,17 @@ export const LinkGroup: FC = ({ group }) => {
}
return (
-
+
+ links: Array,
): SetHiddenLinksAction {
return { type: SET_HIDDEN_LINKS, payload: links }
}
export function toggleHiddenLink(
- link: LinkItem['url']
+ link: LinkItem['url'],
): ToggleHiddenLinkAction {
return { type: TOGGLE_HIDDEN_LINK, payload: link }
}
export function toggleHiddenLinksGroup(
- links: Array
+ items: LinkItem[],
): ToggleHiddenLinksGroup {
- return { type: TOGGLE_HIDDEN_LINKS_GROUP, payload: links }
+ return { type: TOGGLE_HIDDEN_LINKS_GROUP, payload: items.map((i) => i.url) }
}
diff --git a/src/stores/hiddenLinks/hiddenLinksHooks.ts b/src/stores/hiddenLinks/hiddenLinksHooks.ts
index 3b59f5e5..55e19d41 100644
--- a/src/stores/hiddenLinks/hiddenLinksHooks.ts
+++ b/src/stores/hiddenLinks/hiddenLinksHooks.ts
@@ -1,14 +1,12 @@
import { useMemo } from 'react'
import { useAppSelector } from '..'
-import { LinkItem } from '../../links'
+import { LinkGroup, LinkItem } from '../../links'
-export function useAllLinksInGroupAreHidden(): (
- links: Array,
-) => boolean {
+export function useAllLinksInGroupAreHidden(): (group: LinkGroup) => boolean {
const hiddenLinks = useAppSelector((state) => state.hiddenLinks.links)
- return function allLinksInGroupAreHidden(links) {
- return links.every((link) => hiddenLinks.includes(link))
+ return function allLinksInGroupAreHidden(group) {
+ return group.items.every((link) => hiddenLinks.includes(link.url))
}
}
diff --git a/src/utils/slugify.ts b/src/utils/slugify.ts
new file mode 100644
index 00000000..5b4f46e3
--- /dev/null
+++ b/src/utils/slugify.ts
@@ -0,0 +1,3 @@
+export function slugify(input: string): string {
+ return input.toLowerCase().replaceAll(' ', '-')
+}
\ No newline at end of file