mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-06-30 22:46:39 -04:00
make sticky header optional via prop
This commit is contained in:
@ -1,8 +1,7 @@
|
||||
import classNames from "classnames/bind";
|
||||
import classNames from "classnames";
|
||||
import CopyButton from "../CopyButton/CopyButton";
|
||||
|
||||
import styles from "./CodeBlock.module.css";
|
||||
const cx = classNames.bind(styles);
|
||||
|
||||
type CodeBlockProps = JSX.IntrinsicElements["code"] & {
|
||||
forceBlock?: boolean;
|
||||
@ -19,7 +18,11 @@ const CodeBlock = ({ forceBlock, className, children, ...rest }: CodeBlockProps)
|
||||
<div className={styles.block}>
|
||||
<CopyButton source={children} className={styles.copy_btn} />
|
||||
<code
|
||||
className={cx(styles.code, { highlight: prismEnabled }, className?.replace("code-highlight", "").trim())}
|
||||
className={classNames(
|
||||
styles.code,
|
||||
prismEnabled && styles.highlight,
|
||||
className?.replace("code-highlight", "").trim()
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import classNames from "classnames/bind";
|
||||
import classNames from "classnames";
|
||||
import { Formik, Form, Field } from "formik";
|
||||
import TextareaAutosize from "react-textarea-autosize";
|
||||
import Link from "../Link/Link";
|
||||
@ -8,7 +8,6 @@ import { SendIcon, CheckOcticon, XOcticon } from "../Icons";
|
||||
import type { FormikHelpers } from "formik";
|
||||
|
||||
import styles from "./ContactForm.module.css";
|
||||
const cx = classNames.bind(styles);
|
||||
|
||||
type Values = {
|
||||
name: string;
|
||||
@ -103,7 +102,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
|
||||
{({ field, meta }) => (
|
||||
<input
|
||||
type="text"
|
||||
className={cx(styles.input, { missing: meta.error && meta.touched })}
|
||||
className={classNames(styles.input, meta.error && meta.touched && styles.missing)}
|
||||
placeholder="Name"
|
||||
disabled={success}
|
||||
{...field}
|
||||
@ -116,7 +115,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
|
||||
<input
|
||||
type="email"
|
||||
inputMode="email"
|
||||
className={cx(styles.input, { missing: meta.error && meta.touched })}
|
||||
className={classNames(styles.input, meta.error && meta.touched && styles.missing)}
|
||||
placeholder="Email"
|
||||
disabled={success}
|
||||
{...field}
|
||||
@ -127,7 +126,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
|
||||
<Field name="message">
|
||||
{({ field, meta }) => (
|
||||
<TextareaAutosize
|
||||
className={cx(styles.input, styles.textarea, { missing: meta.error && meta.touched })}
|
||||
className={classNames(styles.input, styles.textarea, meta.error && meta.touched && styles.missing)}
|
||||
placeholder="Write something..."
|
||||
minRows={5}
|
||||
disabled={success}
|
||||
@ -154,7 +153,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
|
||||
|
||||
<div className={styles.action_row}>
|
||||
<button
|
||||
className={cx(styles.btn_submit, { hidden: success })}
|
||||
className={classNames(styles.btn_submit, success && styles.hidden)}
|
||||
type="submit"
|
||||
title="Send Message"
|
||||
aria-label="Send Message"
|
||||
@ -171,11 +170,11 @@ const ContactForm = ({ className }: ContactFormProps) => {
|
||||
</button>
|
||||
|
||||
<span
|
||||
className={cx({
|
||||
result_success: success,
|
||||
result_error: !success,
|
||||
hidden: !submitted || !feedback || isSubmitting,
|
||||
})}
|
||||
className={classNames(
|
||||
success && styles.result_success,
|
||||
!success && styles.result_error,
|
||||
(!submitted || !feedback || isSubmitting) && styles.hidden
|
||||
)}
|
||||
>
|
||||
{success ? (
|
||||
<CheckOcticon className={styles.result_icon} fill="CurrentColor" />
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { forwardRef, useState, useEffect } from "react";
|
||||
import classNames from "classnames/bind";
|
||||
import classNames from "classnames";
|
||||
import copy from "copy-to-clipboard";
|
||||
import innerText from "react-innertext";
|
||||
import { ClipboardOcticon, CheckOcticon } from "../Icons";
|
||||
import type { ReactNode, Ref } from "react";
|
||||
|
||||
import styles from "./CopyButton.module.css";
|
||||
const cx = classNames.bind(styles);
|
||||
|
||||
type CopyButtonProps = {
|
||||
source: ReactNode;
|
||||
@ -47,7 +46,7 @@ const CopyButton = forwardRef(function CopyButton(
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cx(styles.button, { success: !!copied }, className)}
|
||||
className={classNames(styles.button, copied && styles.success, className)}
|
||||
title="Copy to clipboard"
|
||||
aria-label="Copy to clipboard"
|
||||
onClick={handleCopy}
|
||||
|
@ -6,7 +6,7 @@ import * as config from "../../lib/config";
|
||||
|
||||
import styles from "./Footer.module.css";
|
||||
|
||||
type FooterProps = JSX.IntrinsicElements["div"];
|
||||
type FooterProps = JSX.IntrinsicElements["footer"];
|
||||
|
||||
const Footer = ({ className, ...rest }: FooterProps) => (
|
||||
<footer className={classNames(styles.footer, className)} {...rest}>
|
||||
|
@ -1,18 +1,21 @@
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 4.5em;
|
||||
padding: 0.7em 1.5em;
|
||||
border-bottom: 1px solid var(--kinda-light);
|
||||
background-color: var(--background-header);
|
||||
backdrop-filter: saturate(180%) blur(5px);
|
||||
z-index: 1000;
|
||||
|
||||
/* light-dark theme switch fading */
|
||||
transition: color 0.25s ease, background 0.25s ease, border 0.25s ease;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
backdrop-filter: saturate(180%) blur(5px);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -5,10 +5,12 @@ import Menu from "../Menu/Menu";
|
||||
|
||||
import styles from "./Header.module.css";
|
||||
|
||||
type HeaderProps = JSX.IntrinsicElements["div"];
|
||||
type HeaderProps = JSX.IntrinsicElements["header"] & {
|
||||
sticky?: boolean;
|
||||
};
|
||||
|
||||
const Header = ({ className }: HeaderProps) => (
|
||||
<header className={classNames(styles.header, className)}>
|
||||
const Header = ({ sticky, className, ...rest }: HeaderProps) => (
|
||||
<header className={classNames(styles.header, sticky && styles.sticky, className)} {...rest}>
|
||||
<nav className={styles.nav}>
|
||||
<Selfie className={styles.selfie} />
|
||||
<Menu className={styles.menu} />
|
||||
|
@ -9,10 +9,11 @@ import themes, { toCSS } from "../../lib/config/themes";
|
||||
import styles from "./Layout.module.css";
|
||||
|
||||
type LayoutProps = JSX.IntrinsicElements["div"] & {
|
||||
noContainer?: boolean; // pass true to disable default `<main>` container styles with padding, etc.
|
||||
container?: boolean; // pass false to disable default `<main>` container styles with padding, etc.
|
||||
stickyHeader?: boolean; // pass false to override default stickiness of header when scrolling
|
||||
};
|
||||
|
||||
const Layout = ({ noContainer, className, children, ...rest }: LayoutProps) => {
|
||||
const Layout = ({ container = true, stickyHeader = true, className, children, ...rest }: LayoutProps) => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
return (
|
||||
@ -38,15 +39,15 @@ const Layout = ({ noContainer, className, children, ...rest }: LayoutProps) => {
|
||||
</Script>
|
||||
|
||||
<div className={classNames(styles.flex, className)} {...rest}>
|
||||
<Header />
|
||||
<Header sticky={stickyHeader} />
|
||||
|
||||
{/* passing `noContainer={true}` to Layout allows 100% control of the content area on a per-page basis */}
|
||||
{noContainer ? (
|
||||
<>{children}</>
|
||||
) : (
|
||||
{/* passing `container={false}` to Layout allows 100% control of the content area on a per-page basis */}
|
||||
{container ? (
|
||||
<main className={styles.default}>
|
||||
<div className={styles.container}>{children}</div>
|
||||
</main>
|
||||
) : (
|
||||
<>{children}</>
|
||||
)}
|
||||
|
||||
<Footer className={styles.footer} />
|
||||
|
@ -1,8 +1,7 @@
|
||||
import Link from "next/link";
|
||||
import classNames from "classnames/bind";
|
||||
import classNames from "classnames";
|
||||
|
||||
import styles from "./MenuLink.module.css";
|
||||
const cx = classNames.bind(styles);
|
||||
|
||||
export type MenuLinkProps = {
|
||||
href?: string;
|
||||
@ -20,7 +19,7 @@ const MenuLink = ({ icon: Icon, href, text, current, className }: MenuLinkProps)
|
||||
if (href) {
|
||||
return (
|
||||
<Link href={href} prefetch={false}>
|
||||
<a className={cx(styles.link, { current: !!current }, className)}>
|
||||
<a className={classNames(styles.link, current && styles.current, className)}>
|
||||
<Icon className={styles.icon} /> <span className={styles.label}>{text}</span>
|
||||
</a>
|
||||
</Link>
|
||||
|
@ -4,17 +4,17 @@ import type { NoteMetaType } from "../../types";
|
||||
|
||||
import styles from "./NoteTitle.module.css";
|
||||
|
||||
type NoteTitleProps = Pick<NoteMetaType, "slug" | "htmlTitle"> & JSX.IntrinsicElements["a"];
|
||||
type NoteTitleProps = Pick<NoteMetaType, "slug" | "htmlTitle"> & JSX.IntrinsicElements["h1"];
|
||||
|
||||
const NoteTitle = ({ slug, htmlTitle, className, ...rest }: NoteTitleProps) => (
|
||||
<h1 className={classNames(styles.title, className)}>
|
||||
<h1 className={classNames(styles.title, className)} {...rest}>
|
||||
<Link
|
||||
href={{
|
||||
pathname: "/notes/[slug]/",
|
||||
query: { slug: slug },
|
||||
}}
|
||||
>
|
||||
<a className={styles.link} dangerouslySetInnerHTML={{ __html: htmlTitle }} {...rest} />
|
||||
<a className={styles.link} dangerouslySetInnerHTML={{ __html: htmlTitle }} />
|
||||
</Link>
|
||||
</h1>
|
||||
);
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import classNames from "classnames/bind";
|
||||
import classNames from "classnames";
|
||||
|
||||
import styles from "./Wallpaper.module.css";
|
||||
const cx = classNames.bind(styles);
|
||||
|
||||
type WallpaperProps = JSX.IntrinsicElements["div"] & {
|
||||
type WallpaperProps = JSX.IntrinsicElements["main"] & {
|
||||
image: string;
|
||||
tile?: boolean;
|
||||
};
|
||||
@ -16,7 +15,7 @@ const Wallpaper = ({ image, tile, className, ...rest }: WallpaperProps) => {
|
||||
bgRef.current.style.backgroundImage = `url(${image})`;
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return <main ref={bgRef} className={cx(styles.wallpaper, { tile: !!tile }, className)} {...rest} />;
|
||||
return <main ref={bgRef} className={classNames(styles.wallpaper, tile && styles.tile, className)} {...rest} />;
|
||||
};
|
||||
|
||||
export default Wallpaper;
|
||||
|
Reference in New Issue
Block a user