switch from react-spectrum to react-aria custom components
This commit is contained in:
parent
d28ee00866
commit
a9dd2e25d5
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/react-aria-npm-3.21.0-ec257f2765-d24c4f6cf0.zip
vendored
Normal file
BIN
.yarn/cache/react-aria-npm-3.21.0-ec257f2765-d24c4f6cf0.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/react-stately-npm-3.19.0-925032631f-e6a1d84c35.zip
vendored
Normal file
BIN
.yarn/cache/react-stately-npm-3.19.0-925032631f-e6a1d84c35.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
@ -1,7 +1,12 @@
|
||||
import * as React from "react";
|
||||
import { lightTheme, Provider, SSRProvider } from "@adobe/react-spectrum";
|
||||
// import * as React from "react";
|
||||
import "./src/styles/global.css";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
plausible: any;
|
||||
}
|
||||
}
|
||||
|
||||
const env =
|
||||
process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || "development";
|
||||
export const onRouteUpdate = function () {
|
||||
@ -22,21 +27,21 @@ export const onRouteUpdate = function () {
|
||||
// p: MyParagraph,
|
||||
// };
|
||||
|
||||
export const wrapRootElement = ({ element }) => (
|
||||
<SSRProvider>
|
||||
<Provider
|
||||
UNSAFE_className="overflow-x-hidden"
|
||||
UNSAFE_style={{
|
||||
background: "unset",
|
||||
color: "unset",
|
||||
}}
|
||||
colorScheme="light"
|
||||
// scale="medium"
|
||||
theme={lightTheme}
|
||||
>
|
||||
{element}
|
||||
</Provider>
|
||||
</SSRProvider>
|
||||
);
|
||||
// export const wrapRootElement = ({ element }) => (
|
||||
// <SSRProvider>
|
||||
// <Provider
|
||||
// UNSAFE_className="overflow-x-hidden"
|
||||
// UNSAFE_style={{
|
||||
// background: "unset",
|
||||
// color: "unset",
|
||||
// }}
|
||||
// colorScheme="light"
|
||||
// // scale="medium"
|
||||
// theme={{light: {}, ...lightTheme}}
|
||||
// >
|
||||
// {element}
|
||||
// </Provider>
|
||||
// </SSRProvider>
|
||||
// );
|
||||
|
||||
// {/* // <MDXProvider components={components}>{element}</MDXProvider> */}
|
@ -1,11 +1,11 @@
|
||||
import * as React from "react";
|
||||
import { lightTheme, Provider } from "@adobe/react-spectrum";
|
||||
// import { lightTheme, Provider } from "@adobe/react-spectrum";
|
||||
import "./src/styles/global.css";
|
||||
import { SSRProvider } from "@react-aria/ssr";
|
||||
|
||||
export const wrapRootElement = ({ element }) => (
|
||||
<SSRProvider>
|
||||
<Provider
|
||||
{/* <Provider
|
||||
UNSAFE_style={{
|
||||
background: "unset",
|
||||
color: "unset",
|
||||
@ -14,8 +14,8 @@ export const wrapRootElement = ({ element }) => (
|
||||
colorScheme="light"
|
||||
// scale="medium"
|
||||
theme={lightTheme}
|
||||
>
|
||||
> */}
|
||||
{element}
|
||||
</Provider>
|
||||
{/* </Provider> */}
|
||||
</SSRProvider>
|
||||
);
|
@ -22,8 +22,8 @@
|
||||
"pretty": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@adobe/react-spectrum": "^3.19.0",
|
||||
"@spectrum-icons/workflow": "^4.0.0",
|
||||
"@react-spectrum/provider": "^3.6.0",
|
||||
"@spectrum-icons/workflow": "^4.0.4",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"chalk": "^4.1.1",
|
||||
"chroma-js": "^2.1.2",
|
||||
@ -47,10 +47,12 @@
|
||||
"postcss-nested": "^6.0.0",
|
||||
"ramda": "^0.27.1",
|
||||
"react": "^18.2.0",
|
||||
"react-aria": "^3.21.0",
|
||||
"react-cool-dimensions": "^2.0.7",
|
||||
"react-div-100vh": "^0.7.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-stately": "^3.19.0",
|
||||
"react-tiny-popover": "^7.2.0",
|
||||
"sass": "^1.34.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
|
@ -39,6 +39,11 @@ const logKeyShortcut = (keyCode) => {
|
||||
}
|
||||
};
|
||||
|
||||
const IconStyle = {
|
||||
width: '24px',
|
||||
margin: '0 4px'
|
||||
}
|
||||
|
||||
const ArrowLinkClasses = `hover:underline text-vibrant-light hover:text-muted-light
|
||||
lg:px-4 self-stretch flex items-center hover:bg-black/50 max-h-screen sticky top-0
|
||||
`;
|
||||
@ -260,37 +265,37 @@ const GalleryImage = ({ data, location: { state } }) => {
|
||||
<div className="flex flex-col items-end">
|
||||
<MetadataItem
|
||||
data={dateTaken.toLocaleDateString()}
|
||||
icon={<Calendar />}
|
||||
icon={<Calendar UNSAFE_style={IconStyle} />}
|
||||
title="date taken"
|
||||
/>
|
||||
<div className="sm:flex justify-end gap-2 border border-vibrant-light pl-2 rounded">
|
||||
<MetadataItem
|
||||
data={shutterSpeed}
|
||||
icon={<Stopwatch />}
|
||||
icon={<Stopwatch UNSAFE_style={IconStyle} />}
|
||||
title="shutter speed"
|
||||
/>
|
||||
{meta.FNumber && (
|
||||
<MetadataItem
|
||||
data={`f/${meta.FNumber}`}
|
||||
icon={<Exposure />}
|
||||
icon={<Exposure UNSAFE_style={IconStyle} />}
|
||||
title="aperture"
|
||||
/>
|
||||
)}
|
||||
<MetadataItem
|
||||
data={meta.ISO}
|
||||
icon={<Filmroll />}
|
||||
icon={<Filmroll UNSAFE_style={IconStyle} />}
|
||||
title="ISO"
|
||||
/>
|
||||
</div>
|
||||
<MetadataItem
|
||||
data={locationString}
|
||||
icon={<Location />}
|
||||
icon={<Location UNSAFE_style={IconStyle} />}
|
||||
title="location"
|
||||
/>
|
||||
{(meta.Make || meta.Model) && (
|
||||
<MetadataItem
|
||||
data={[meta.Make, meta.Model].join(" ")}
|
||||
icon={<Camera />}
|
||||
icon={<Camera UNSAFE_style={IconStyle} />}
|
||||
title="camera"
|
||||
/>
|
||||
)}
|
||||
@ -302,7 +307,7 @@ const GalleryImage = ({ data, location: { state } }) => {
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" @")}
|
||||
icon={<Circle />}
|
||||
icon={<Circle UNSAFE_style={IconStyle} />}
|
||||
title="lens"
|
||||
/>
|
||||
)}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as React from "react";
|
||||
import classNames from "classnames";
|
||||
import Checkmark from "@spectrum-icons/workflow/Checkmark";
|
||||
|
||||
interface KeywordsPickerProps {
|
||||
keywords: string[];
|
||||
@ -9,7 +10,7 @@ interface KeywordsPickerProps {
|
||||
const KeywordsPicker = ({ keywords, value, onChange }: KeywordsPickerProps) => {
|
||||
return (
|
||||
<div className="mx-2 mt-2">
|
||||
<span className="text-xs text-[var(--spectrum-fieldlabel-text-color,var(--spectrum-alias-label-text-color))]">
|
||||
<span className="text-xs text-black">
|
||||
Collections
|
||||
</span>
|
||||
<ul className="flex gap-1 flex-wrap mt-1 mb-2">
|
||||
@ -19,20 +20,24 @@ const KeywordsPicker = ({ keywords, value, onChange }: KeywordsPickerProps) => {
|
||||
<li key={keyword}>
|
||||
<button
|
||||
className={classNames(
|
||||
"transition",
|
||||
`py-[5px] px-3 rounded-full`,
|
||||
`text-[var(--spectrum-fieldbutton-text-color,var(--spectrum-alias-text-color))]
|
||||
|
||||
border border-[var(--spectrum-fieldbutton-border-color,var(--spectrum-alias-border-color))]`,
|
||||
`text-black border border-black`,
|
||||
selected
|
||||
? "bg-green-500 hover:bg-green-300"
|
||||
: `bg-[var(--spectrum-fieldbutton-background-color,var(--spectrum-global-color-gray-75))]
|
||||
hover:bg-[var(--spectrum-fieldbutton-background-color-down,var(--spectrum-global-color-gray-200))]`
|
||||
? "bg-transparentblack font-bold"
|
||||
: `bg-white
|
||||
hover:bg-transparentblack`
|
||||
)}
|
||||
onClick={() => (selected ? onChange(null) : onChange(keyword))}
|
||||
type="button"
|
||||
>
|
||||
{keyword}
|
||||
{keyword}{" "}
|
||||
{selected && (
|
||||
<Checkmark
|
||||
UNSAFE_className="mx-1"
|
||||
UNSAFE_style={{ width: "15px" }}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
|
105
src/components/ListBox.tsx
Normal file
105
src/components/ListBox.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
/* eslint-disable @typescript-eslint/no-use-before-define */
|
||||
import * as React from "react";
|
||||
import type { AriaListBoxOptions } from "@react-aria/listbox";
|
||||
import type { ListState } from "react-stately";
|
||||
import type { Node } from "@react-types/shared";
|
||||
import { useListBox, useListBoxSection, useOption } from "react-aria";
|
||||
// import { CheckIcon } from "@heroicons/react/solid";
|
||||
import Checkmark from '@spectrum-icons/workflow/Checkmark'
|
||||
|
||||
interface ListBoxProps extends AriaListBoxOptions<unknown> {
|
||||
listBoxRef?: React.RefObject<HTMLUListElement>;
|
||||
state: ListState<unknown>;
|
||||
}
|
||||
|
||||
interface SectionProps {
|
||||
section: Node<unknown>;
|
||||
state: ListState<unknown>;
|
||||
}
|
||||
|
||||
interface OptionProps {
|
||||
item: Node<unknown>;
|
||||
state: ListState<unknown>;
|
||||
}
|
||||
|
||||
export function ListBox(props: ListBoxProps) {
|
||||
let ref = React.useRef<HTMLUListElement>(null);
|
||||
let { listBoxRef = ref, state } = props;
|
||||
let { listBoxProps } = useListBox(props, state, listBoxRef);
|
||||
|
||||
return (
|
||||
<ul
|
||||
{...listBoxProps}
|
||||
className="w-full max-h-72 overflow-auto outline-none"
|
||||
ref={listBoxRef}
|
||||
>
|
||||
{[...state.collection].map((item) =>
|
||||
item.type === "section" ? (
|
||||
<ListBoxSection key={item.key} section={item} state={state} />
|
||||
) : (
|
||||
<Option item={item} key={item.key} state={state} />
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
function ListBoxSection({ section, state }: SectionProps) {
|
||||
let { itemProps, headingProps, groupProps } = useListBoxSection({
|
||||
heading: section.rendered,
|
||||
"aria-label": section["aria-label"]
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<li {...itemProps} className="pt-2">
|
||||
{section.rendered && (
|
||||
<span
|
||||
{...headingProps}
|
||||
className="text-xs font-bold uppercase text-gray-500 mx-3"
|
||||
>
|
||||
{section.rendered}
|
||||
</span>
|
||||
)}
|
||||
<ul {...groupProps}>
|
||||
{[...section.childNodes].map((node) => (
|
||||
<Option item={node} key={node.key} state={state} />
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Option({ item, state }: OptionProps) {
|
||||
let ref = React.useRef<HTMLLIElement>(null);
|
||||
let { optionProps, isDisabled, isSelected, isFocused } = useOption(
|
||||
{
|
||||
key: item.key
|
||||
},
|
||||
state,
|
||||
ref
|
||||
);
|
||||
|
||||
let text = "text-black";
|
||||
if (isFocused || isSelected) {
|
||||
text = "text-black";
|
||||
} else if (isDisabled) {
|
||||
text = "text-gray-200";
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
{...optionProps}
|
||||
className={`p-3 outline-none cursor-default flex items-center justify-between ${text} ${
|
||||
isFocused ? "bg-transparentblack" : ""
|
||||
} ${isSelected ? "font-bold" : ""}`}
|
||||
ref={ref}
|
||||
>
|
||||
{item.rendered}
|
||||
{isSelected && (
|
||||
<Checkmark UNSAFE_className="mx-1" UNSAFE_style={{ width: '15px' }} aria-hidden="true" />
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
}
|
@ -138,7 +138,7 @@ const MasonryGallery = ({
|
||||
return (
|
||||
<Link
|
||||
className={classNames(
|
||||
"border-4 overflow-hidden",
|
||||
"border-4 border-white overflow-hidden",
|
||||
debugHue && "border-8"
|
||||
)}
|
||||
id={image.base}
|
||||
@ -159,7 +159,7 @@ const MasonryGallery = ({
|
||||
${image.fields?.imageMeta?.dominantHue?.[1] ?? 0 * 100}%,
|
||||
${image.fields?.imageMeta?.dominantHue?.[2] ?? 0 * 100}%
|
||||
)`
|
||||
: "white",
|
||||
: "",
|
||||
}}
|
||||
to={`/photogallery/${image.base}`}
|
||||
>
|
||||
|
39
src/components/Popover.tsx
Normal file
39
src/components/Popover.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import type { OverlayTriggerState } from "react-stately";
|
||||
import type { AriaPopoverProps } from "@react-aria/overlays";
|
||||
import * as React from "react";
|
||||
import { usePopover, DismissButton, Overlay } from "@react-aria/overlays";
|
||||
|
||||
interface PopoverProps extends Omit<AriaPopoverProps, "popoverRef"> {
|
||||
children: React.ReactNode;
|
||||
state: OverlayTriggerState;
|
||||
className?: string;
|
||||
popoverRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export function Popover(props: PopoverProps) {
|
||||
let ref = React.useRef<HTMLDivElement>(null);
|
||||
let { popoverRef = ref, state, children, className, isNonModal } = props;
|
||||
|
||||
let { popoverProps, underlayProps } = usePopover(
|
||||
{
|
||||
...props,
|
||||
popoverRef
|
||||
},
|
||||
state
|
||||
);
|
||||
|
||||
return (
|
||||
<Overlay>
|
||||
{!isNonModal && <div {...underlayProps} className="fixed inset-0" />}
|
||||
<div
|
||||
{...popoverProps}
|
||||
className={`z-10 shadow border border-black bg-white rounded mt-1 ${className}`}
|
||||
ref={popoverRef}
|
||||
>
|
||||
{!isNonModal && <DismissButton onDismiss={state.close} />}
|
||||
{children}
|
||||
<DismissButton onDismiss={state.close} />
|
||||
</div>
|
||||
</Overlay>
|
||||
);
|
||||
}
|
89
src/components/Select.tsx
Normal file
89
src/components/Select.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import * as React from "react";
|
||||
import type { AriaSelectProps } from "@react-types/select";
|
||||
import { useSelectState } from "react-stately";
|
||||
import {
|
||||
useSelect,
|
||||
HiddenSelect,
|
||||
useButton,
|
||||
mergeProps,
|
||||
useFocusRing
|
||||
} from "react-aria";
|
||||
// import { SelectorIcon } from "@heroicons/react/solid";
|
||||
import ChevronDown from "@spectrum-icons/workflow/ChevronDown";
|
||||
|
||||
import { ListBox } from "./ListBox";
|
||||
import { Popover } from "./Popover";
|
||||
|
||||
export { Item } from "react-stately";
|
||||
|
||||
export function Select<T extends object>(props: AriaSelectProps<T>) {
|
||||
// Create state based on the incoming props
|
||||
let state = useSelectState(props);
|
||||
|
||||
// Get props for child elements from useSelect
|
||||
let ref = React.useRef(null);
|
||||
let { labelProps, triggerProps, valueProps, menuProps } = useSelect(
|
||||
props,
|
||||
state,
|
||||
ref
|
||||
);
|
||||
|
||||
// Get props for the button based on the trigger props from useSelect
|
||||
let { buttonProps } = useButton(triggerProps, ref);
|
||||
|
||||
let { focusProps, isFocusVisible } = useFocusRing();
|
||||
|
||||
return (
|
||||
<div className="inline-flex flex-col relative">
|
||||
<div
|
||||
{...labelProps}
|
||||
className="block text-sm text-left cursor-default mb-1"
|
||||
>
|
||||
{props.label}
|
||||
</div>
|
||||
<HiddenSelect
|
||||
label={props.label}
|
||||
name={props.name}
|
||||
state={state}
|
||||
triggerRef={ref}
|
||||
/>
|
||||
<button
|
||||
{...mergeProps(buttonProps, focusProps)}
|
||||
className={`py-[5px] px-3 w-[150px] flex flex-row items-center justify-between overflow-hidden cursor-default rounded border ${
|
||||
isFocusVisible ? "border-green-500" : "border-black"
|
||||
} ${state.isOpen ? "bg-gray-100" : "bg-white"}`}
|
||||
ref={ref}
|
||||
>
|
||||
<span
|
||||
{...valueProps}
|
||||
// className={`text-md flex-auto ${
|
||||
// state.selectedItem ? "text-gray-800" : "text-gray-500"
|
||||
// }`}
|
||||
>
|
||||
{state.selectedItem
|
||||
? state.selectedItem.rendered
|
||||
: "Select an option"}
|
||||
</span>
|
||||
<ChevronDown
|
||||
UNSAFE_className="mx-1"
|
||||
UNSAFE_style={{
|
||||
width: '15px'
|
||||
}}
|
||||
// UNSAFE_className={`w-[20px] h-5 ${
|
||||
// isFocusVisible ? "text-pink-500" : "text-gray-500"
|
||||
// }`}
|
||||
/>
|
||||
</button>
|
||||
{state.isOpen && (
|
||||
<Popover
|
||||
className="w-[150px]"
|
||||
placement="bottom start"
|
||||
state={state}
|
||||
triggerRef={ref}
|
||||
>
|
||||
<ListBox {...menuProps} state={state} />
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -78,8 +78,8 @@ const IndexPage = ({
|
||||
LightMuted: [0, 0, 0],
|
||||
Vibrant: [0, 0, 0],
|
||||
LightVibrant: [0, 0, 0],
|
||||
DarkMuted: [255, 255, 255],
|
||||
DarkVibrant: [255, 255, 255],
|
||||
DarkMuted: [238, 238, 238],
|
||||
DarkVibrant: [238, 238, 238],
|
||||
})}
|
||||
/>
|
||||
</Helmet>
|
||||
|
@ -2,12 +2,13 @@ import * as React from "react";
|
||||
import * as R from "ramda";
|
||||
import { graphql, PageProps } from "gatsby";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { Picker, Item } from "@adobe/react-spectrum";
|
||||
// import { Picker, Item } from "@adobe/react-spectrum";
|
||||
|
||||
import MasonryGallery from "../components/MasonryGallery";
|
||||
import KeywordsPicker from "../components/KeywordsPicker";
|
||||
import { getGalleryPageUrl, getHelmetSafeBodyStyle } from "../utils";
|
||||
import Nav from "../components/Nav";
|
||||
import { Item, Select } from "../components/Select";
|
||||
|
||||
const SORT_KEYS = {
|
||||
hue: ["fields", "imageMeta", "vibrantHue"],
|
||||
@ -170,8 +171,8 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
LightMuted: [0, 0, 0],
|
||||
Vibrant: [0, 0, 0],
|
||||
LightVibrant: [0, 0, 0],
|
||||
DarkMuted: [255, 255, 255],
|
||||
DarkVibrant: [255, 255, 255],
|
||||
DarkMuted: [238, 238, 238],
|
||||
DarkVibrant: [238, 238, 238],
|
||||
})}
|
||||
/>
|
||||
</Helmet>
|
||||
@ -205,7 +206,7 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
value={filterKeyword}
|
||||
/>
|
||||
<div className="m-2">
|
||||
<Picker
|
||||
<Select
|
||||
label="Sort by..."
|
||||
// @ts-ignore
|
||||
onSelectionChange={setSortKey}
|
||||
@ -214,7 +215,7 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
<Item key="rating">Curated</Item>
|
||||
<Item key="date">Date</Item>
|
||||
<Item key="hue">Hue</Item>
|
||||
</Picker>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -45,8 +45,9 @@ module.exports = {
|
||||
"huge-2": "max(7.8vw, 120px)",
|
||||
},
|
||||
colors: {
|
||||
"white": "#EEEEEE",
|
||||
buzzwordsPrimary: "#F6C54B",
|
||||
transparentblack: "rgba(0,0,0,0.3)",
|
||||
transparentblack: "rgba(0,0,0,0.24)",
|
||||
vibrant: {
|
||||
DEFAULT: ({ opacityVariable, opacityValue }) => {
|
||||
if (opacityValue !== undefined) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user